/*
* Copyright 2010 Jon S Akhtar (Sylvanaar)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sylvanaar.idea.Lua.editor.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.sylvanaar.idea.Lua.lang.psi.LuaPsiFile;
import com.sylvanaar.idea.Lua.lang.psi.LuaPsiManager;
import com.sylvanaar.idea.Lua.lang.psi.expressions.LuaFieldIdentifier;
import com.sylvanaar.idea.Lua.lang.psi.impl.statements.LuaFunctionDefinitionStatementImpl;
import com.sylvanaar.idea.Lua.lang.psi.stubs.index.LuaGlobalDeclarationIndex;
import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaCompoundIdentifier;
import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaGlobalIdentifier;
import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaIdentifier;
import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaSymbol;
import com.sylvanaar.idea.Lua.lang.psi.visitor.LuaRecursiveElementVisitor;
import com.sylvanaar.idea.Lua.options.LuaApplicationSettings;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import static com.intellij.patterns.PlatformPatterns.psiElement;
public class LuaCompletionContributor extends DefaultCompletionContributor {
private static final Logger log = Logger.getInstance("Lua.CompletionContributor");
private static final ElementPattern<PsiElement> AFTER_SELF_DOT = psiElement().withParent(LuaCompoundIdentifier.class).afterSibling(psiElement().withName("self"));
private static final ElementPattern<PsiElement> AFTER_DOT = psiElement().afterLeaf(".", ":");
private static final ElementPattern<PsiElement> AFTER_FUNCTION = psiElement().afterLeafSkipping(psiElement().whitespace(), PlatformPatterns.string().matches("function"));
private static final ElementPattern<PsiElement> NOT_AFTER_DOT = psiElement().withParent(LuaIdentifier.class).andNot(psiElement().afterLeaf(".", ":"));
private static final Key<Collection<String>> PREFIX_FILTERED_GLOBALS_COLLECTION = new Key<Collection<String>>("lua.prefix.globals");
private static final ElementPattern<PsiElement> AFTER_SELF =
psiElement().withParent(LuaSymbol.class).afterLeaf(":",".");
private Collection<String> getAllGlobals(@NotNull CompletionParameters parameters, ProcessingContext context) {
return LuaPsiManager.getInstance(parameters.getOriginalFile().getProject()).getFilteredGlobalsCache();
}
private Collection<String> getPrefixFilteredGlobals(String prefix, @NotNull CompletionParameters parameters, ProcessingContext context) {
Collection<String> names = context.get(PREFIX_FILTERED_GLOBALS_COLLECTION);
if (names != null) return names;
names = new ArrayList<String>();
int prefixLen = prefix.length();
for (String key : getAllGlobals(parameters, context)) {
if (key.length() > prefixLen && key.startsWith(prefix))
names.add(key);
}
context.put(PREFIX_FILTERED_GLOBALS_COLLECTION, names);
return names;
}
public LuaCompletionContributor() {
extend(CompletionType.BASIC, NOT_AFTER_DOT, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
for (String s : LuaKeywordsManager.getKeywords())
result.addElement(new LuaLookupElement(s));
}
});
// extend(CompletionType.BASIC, AFTER_FUNCTION, new CompletionProvider<CompletionParameters>() {
// @Override
// protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
// @NotNull CompletionResultSet result) {
// String prefix = result.getPrefixMatcher().getPrefix();
//
// for (String key : getPrefixFilteredGlobals(prefix, parameters, context)) {
// result.addElement(new LuaLookupElement(key));
// }
// }
// });
extend(CompletionType.BASIC, NOT_AFTER_DOT, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
@NotNull CompletionResultSet result) {
if (!LuaApplicationSettings.getInstance().INCLUDE_ALL_FIELDS_IN_COMPLETIONS) return;
LuaPsiFile file = (LuaPsiFile) parameters.getOriginalFile();
globalUsageVisitor.reset();
file.acceptChildren(globalUsageVisitor);
String prefix = result.getPrefixMatcher().getPrefix();
int prefixLen = prefix.length();
for (String key : globalUsageVisitor.getResult()) {
if (key.length() > prefixLen && key.startsWith(prefix))
result.addElement(new LuaLookupElement(key));
}
}
});
extend(CompletionType.BASIC, AFTER_SELF, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context, @NotNull CompletionResultSet result) {
PsiElement element = parameters.getPosition();
try {
LuaCompoundIdentifier cid = (LuaCompoundIdentifier) element.getContext().getContext();
if (!cid.getLeftSymbol().equals("self"))
return;
} catch (Exception e) {
return;
}
while (!(element instanceof LuaFunctionDefinitionStatementImpl) && element != null)
element = element.getContext();
// Must be inside a function
if (element == null) return;
LuaFunctionDefinitionStatementImpl func = (LuaFunctionDefinitionStatementImpl) element;
LuaSymbol symbol = func.getIdentifier();
int colonIdx = symbol.getText().lastIndexOf(':');
int dotIdx = symbol.getText().lastIndexOf('.');
if (colonIdx < 0 && dotIdx < 0) return;
int idx = Math.max(colonIdx, dotIdx);
String prefix = symbol.getText().substring(0, idx+1);
for(String key : LuaGlobalDeclarationIndex.getInstance().getAllKeys(element.getProject())) {
// System.out.println(key);
if (key.startsWith(prefix)) {
result.addElement(new LuaLookupElement("self:"+key.substring(prefix.length())));
result.addElement(new LuaLookupElement("self."+key.substring(prefix.length())));
}
}
fieldVisitor.reset();
((LuaPsiFile)parameters.getOriginalFile()).accept(fieldVisitor);
for (String s : fieldVisitor.getResult()) {
if (s.startsWith(prefix)) {
result.addElement(new LuaLookupElement("self:"+s));
result.addElement(new LuaLookupElement("self."+s));
}
}
}
});
extend(CompletionType.BASIC, AFTER_DOT, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
if (!LuaApplicationSettings.getInstance().INCLUDE_ALL_FIELDS_IN_COMPLETIONS)
return;
String prefix = result.getPrefixMatcher().getPrefix();
String matchPrefix = null;
for(int i=prefix.length()-1; i>=0; i--)
if (prefix.charAt(i) == '.' || prefix.charAt(i) == ':') {
matchPrefix = prefix.substring(i+1, prefix.length());
prefix = prefix.substring(0, i+1);
break;
}
for(String key : getAllGlobals(parameters, context)) {
if (matchPrefix != null && key.startsWith(matchPrefix))
result.addElement(new LuaLookupElement(prefix + key));
}
}
});
}
@Override
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
int end = context.getIdentifierEndOffset();
int start = context.getStartOffset();
String identifierToReplace = context.getEditor().getDocument().getText(new TextRange(start-1, end));
if (identifierToReplace.charAt(0) == '.' || identifierToReplace.charAt(0) == ':')
context.setReplacementOffset(start);
super.beforeCompletion(context);
}
@Override
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
super.fillCompletionVariants(parameters, result); //To change body of overridden methods use File | Settings | File Templates.
}
LuaFieldElementVisitor fieldVisitor = new LuaFieldElementVisitor();
LuaGlobalUsageVisitor globalUsageVisitor = new LuaGlobalUsageVisitor();
private class LuaFieldElementVisitor extends LuaRecursiveElementVisitor {
Set<String> result = new HashSet<String>();
@Override
public void visitIdentifier(LuaIdentifier e) {
super.visitIdentifier(e);
if (e instanceof LuaFieldIdentifier && e.getTextLength() > 0 && e.getText().charAt(0) != '[' && e.getName() != null)
result.add(e.getName());
}
public Set<String> getResult() {
return result;
}
public void reset() { result.clear(); }
}
private class LuaGlobalUsageVisitor extends LuaRecursiveElementVisitor {
Set<String> result = new HashSet<String>();
@Override
public void visitIdentifier(LuaIdentifier e) {
super.visitIdentifier(e);
if (e instanceof LuaGlobalIdentifier && e.getTextLength() > 0 && e.getName() != null)
result.add(e.getName());
}
public Set<String> getResult() {
return result;
}
public void reset() { result.clear(); }
}
}