KT-759 Filter keywords auto completion according to context

This commit is contained in:
Nikolay Krasko
2011-12-28 17:51:18 +04:00
parent 939f9ac279
commit f259b7d397
11 changed files with 408 additions and 46 deletions
@@ -1,65 +1,162 @@
package org.jetbrains.jet.plugin.completion; package org.jetbrains.jet.plugin.completion;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns; import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.AndFilter;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.NotFilter; import com.intellij.psi.filters.NotFilter;
import com.intellij.psi.filters.TextFilter; import com.intellij.psi.filters.TextFilter;
import com.intellij.psi.filters.position.FilterPattern; import com.intellij.psi.filters.position.FilterPattern;
import com.intellij.psi.filters.position.LeftNeighbour; import com.intellij.psi.filters.position.LeftNeighbour;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext; import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler;
import org.jetbrains.jet.plugin.completion.handlers.JetKeywordInsertHandler; import org.jetbrains.jet.plugin.completion.handlers.JetKeywordInsertHandler;
import java.util.Collection;
import java.util.List;
/** /**
* A keyword contributor for Kotlin * A keyword contributor for Kotlin
* TODO: add different context for different keywords
* *
* @author Nikolay Krasko * @author Nikolay Krasko
*/ */
public class JetKeywordCompletionContributor extends CompletionContributor { public class JetKeywordCompletionContributor extends CompletionContributor {
private static class JetTopKeywordCompletionProvider extends CompletionProvider<CompletionParameters> { private final static InsertHandler<LookupElement> KEYWORDS_INSERT_HANDLER = new JetKeywordInsertHandler();
private final static InsertHandler<LookupElement> FUNCTION_INSERT_HANDLER = new JetFunctionInsertHandler(
JetFunctionInsertHandler.CaretPosition.AFTER_BRACKETS);
private final static List<String> FUNCTION_KEYWORDS = Lists.newArrayList("get", "set");
private final static InsertHandler<LookupElement> KEYWORDS_INSERT_HANDLER = new JetKeywordInsertHandler(); private static class InTopFilter implements ElementFilter {
private final static String[] COMPLETE_KEYWORD = new String[] {
"namespace", "as", "type", "class", "this", "super", "val", "var", "fun", "for", "null", "true",
"false", "is", "in", "throw", "return", "break", "continue", "object", "if", "try", "else", "while",
"do", "when", "trait", "This"
};
private final static String[] COMPLETE_SOFT_KEYWORDS = new String[] {
"import", "where", "by", "get", "set", "abstract", "enum", "open", "annotation", "override", "private",
"public", "internal", "protected", "catch", "out", "vararg", "inline", "finally", "final", "ref"
};
@Override @Override
protected void addCompletions(@NotNull CompletionParameters parameters, public boolean isAcceptable(Object element, PsiElement context) {
ProcessingContext context, return PsiTreeUtil.getParentOfType(context, JetFile.class, false, JetClass.class, JetFunction.class) != null;
@NotNull CompletionResultSet result) { }
for (String keyword : COMPLETE_KEYWORD) { @Override
public boolean isClassAcceptable(Class hintClass) {
result.addElement(LookupElementBuilder.create(keyword).setInsertHandler(KEYWORDS_INSERT_HANDLER).setBold()); return true;
}
for (String softKeyword : COMPLETE_SOFT_KEYWORDS) {
result.addElement(LookupElementBuilder.create(softKeyword).setInsertHandler(KEYWORDS_INSERT_HANDLER));
}
} }
} }
private static class InNonClassBlockFilter implements ElementFilter {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return PsiTreeUtil.getParentOfType(context, JetFunction.class, true, JetClass.class) != null &&
PsiTreeUtil.getParentOfType(context, JetBlockExpression.class, true, JetFunction.class) != null;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}
private static class InParametersFilter implements ElementFilter {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return PsiTreeUtil.getParentOfType(context, JetParameterList.class, false) != null;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}
private static class InClassBodyFilter implements ElementFilter {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return PsiTreeUtil.getParentOfType(context, JetClassBody.class, true, JetFunction.class) != null;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}
private static class InPropertyFilter implements ElementFilter {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return PsiTreeUtil.getParentOfType(context, JetProperty.class, false) != null;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}
public static class KeywordsCompletionProvider extends CompletionProvider<CompletionParameters> {
private final Collection<LookupElement> elements;
public KeywordsCompletionProvider(String ...keywords) {
List<String> elementsList = Lists.newArrayList(keywords);
elements = Collections2.transform(elementsList, new Function<String, LookupElement>() {
@Override
public LookupElement apply(String keyword) {
final LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(keyword).setBold();
if (!FUNCTION_KEYWORDS.contains(keyword)) {
return lookupElementBuilder.setInsertHandler(KEYWORDS_INSERT_HANDLER);
}
return lookupElementBuilder.setInsertHandler(FUNCTION_INSERT_HANDLER);
}
});
}
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
@NotNull CompletionResultSet result) {
result.addAllElements(elements);
}
}
public JetKeywordCompletionContributor() { public JetKeywordCompletionContributor() {
registerScopeKeywordsCompletion(new InTopFilter(),
"abstract", "class", "enum", "final", "fun", "get", "import", "inline",
"internal", "open", "package", "private", "protected", "public", "set",
"trait", "type", "val", "var");
PsiElementPattern.Capture<PsiElement> notDotPlace = registerScopeKeywordsCompletion(new InClassBodyFilter(),
PlatformPatterns.psiElement().and(new FilterPattern(new NotFilter(new LeftNeighbour(new TextFilter("."))))); "abstract", "class", "enum", "final", "fun", "inline", "internal", "get",
"open", "override", "private", "protected", "public", "set", "trait",
"type", "val", "var");
extend(CompletionType.BASIC, registerScopeKeywordsCompletion(new InNonClassBlockFilter(),
notDotPlace, "as", "break", "by", "catch", "class", "continue", "default", "do", "else",
new JetTopKeywordCompletionProvider()); "enum", "false", "finally", "for", "fun", "get", "if", "in", "inline",
"internal", "is", "null", "object", "private", "protected", "public", "ref",
"return", "set", "super", "This", "this", "throw", "trait", "true", "try",
"type", "val", "var", "vararg", "when", "where", "while");
registerScopeKeywordsCompletion(new InPropertyFilter(), "else", "false", "if", "null", "this", "true");
registerScopeKeywordsCompletion(new InParametersFilter(), "ref", "out");
}
private void registerScopeKeywordsCompletion(final ElementFilter placeFilter, String... keywords) {
extend(CompletionType.BASIC, getPlacePattern(placeFilter), new KeywordsCompletionProvider(keywords));
}
private static ElementPattern<PsiElement> getPlacePattern(final ElementFilter placeFilter) {
return PlatformPatterns.psiElement().and(
new FilterPattern(new AndFilter(new NotFilter(new LeftNeighbour(new TextFilter("."))), placeFilter)));
} }
} }
@@ -0,0 +1,4 @@
class TestClass {
val a : Int
ge<caret>
}
@@ -0,0 +1,4 @@
class TestClass {
val a : Int
get()<caret>
}
@@ -3,4 +3,4 @@ fun foo() {
str. <caret> str. <caret>
} }
// ABSENT: public, val, in // ABSENT: public, val, in, if, when, this
@@ -0,0 +1,54 @@
class TestClass {
<caret>
}
// EXIST: abstract
// ABSENT: annotation
// ABSENT: as
// ABSENT: break
// ABSENT: by
// ABSENT: catch
// EXIST: class
// ABSENT: continue
// ABSENT: default
// ABSENT: do
// ABSENT: else
// EXIST: enum
// ABSENT: false
// EXIST: final
// ABSENT: finally
// ABSENT: for
// EXIST: fun
// EXIST: get
// ABSENT: if
// ABSENT: import
// ABSENT: in
// EXIST: inline
// EXIST: internal
// ABSENT: is
// ABSENT: null
// ABSENT: object
// EXIST: open
// ABSENT: out
// EXIST: override
// ABSENT: package
// EXIST: private
// EXIST: protected
// EXIST: public
// ABSENT: ref
// ABSENT: return
// EXIST: set
// ABSENT: super
// ABSENT: This
// ABSENT: this
// ABSENT: throw
// EXIST: trait
// ABSENT: true
// ABSENT: try
// EXIST: type
// EXIST: val
// EXIST: var
// ABSENT: vararg
// ABSENT: when
// ABSENT: where
// ABSENT: while
@@ -2,4 +2,53 @@ fun foo() {
<caret> <caret>
} }
// EXIST: val, var // ABSENT: abstract
// ??ABSENT: annotation // java annotation namespace
// EXIST: as
// EXIST: break
// EXIST: by
// EXIST: catch
// EXIST: class
// EXIST: continue
// EXIST: default
// EXIST: do
// EXIST: else
// EXIST: enum
// EXIST: false
// ABSENT: final
// EXIST: finally
// EXIST: for
// EXIST: fun
// EXIST: get
// EXIST: if
// ABSENT: import
// EXIST: in
// EXIST: inline
// EXIST: internal
// EXIST: is
// EXIST: null
// EXIST: object
// ABSENT: open
// ABSENT: out
// ABSENT: override
// ABSENT: package
// EXIST: private
// EXIST: protected
// EXIST: public
// EXIST: ref
// EXIST: return
// EXIST: set
// EXIST: super
// EXIST: This
// EXIST: this
// EXIST: throw
// EXIST: trait
// EXIST: true
// EXIST: try
// EXIST: type
// EXIST: val
// EXIST: var
// EXIST: vararg
// EXIST: when
// EXIST: where
// EXIST: while
@@ -0,0 +1,54 @@
fun test(<caret>) {
}
// ABSENT: abstract
// ABSENT: annotation
// ABSENT: as
// ABSENT: break
// ABSENT: by
// ABSENT: catch
// ABSENT: class
// ABSENT: continue
// ABSENT: default
// ABSENT: do
// ABSENT: else
// ABSENT: enum
// ABSENT: false
// ABSENT: final
// ABSENT: finally
// ABSENT: for
// ABSENT: fun
// ABSENT: get
// ABSENT: if
// ABSENT: import
// ABSENT: in
// ABSENT: inline
// ABSENT: internal
// ABSENT: is
// ABSENT: null
// ABSENT: object
// ABSENT: open
// EXIST: out
// ABSENT: override
// ABSENT: package
// ABSENT: private
// ABSENT: protected
// ABSENT: public
// EXIST: ref
// ABSENT: return
// ABSENT: set
// ABSENT: super
// ABSENT: This
// ABSENT: this
// ABSENT: throw
// ABSENT: trait
// ABSENT: true
// ABSENT: try
// ABSENT: type
// ABSENT: val
// ABSENT: var
// ABSENT: vararg
// ABSENT: when
// ABSENT: where
// ABSENT: while
@@ -0,0 +1,8 @@
var a : Int = <caret>
// EXIST: else
// EXIST: false
// EXIST: if
// EXIST: null
// EXIST: this
// EXIST: true
@@ -2,5 +2,53 @@ fun foo() {
val test : <caret> val test : <caret>
} }
// TODO: Move all keywords to absent // ABSENT: abstract
// EXIST: fun, val, var, namespace // ??ABSENT: annotation // java annotation namespace
// EXIST: as
// EXIST: break
// EXIST: by
// EXIST: catch
// EXIST: class
// EXIST: continue
// EXIST: default
// EXIST: do
// EXIST: else
// EXIST: enum
// EXIST: false
// ABSENT: final
// EXIST: finally
// EXIST: for
// EXIST: fun
// EXIST: get
// EXIST: if
// ABSENT: import
// EXIST: in
// EXIST: inline
// EXIST: internal
// EXIST: is
// EXIST: null
// EXIST: object
// ABSENT: open
// ABSENT: out
// ABSENT: override
// ABSENT: package
// EXIST: private
// EXIST: protected
// EXIST: public
// EXIST: ref
// EXIST: return
// EXIST: set
// EXIST: super
// EXIST: This
// EXIST: this
// EXIST: throw
// EXIST: trait
// EXIST: true
// EXIST: try
// EXIST: type
// EXIST: val
// EXIST: var
// EXIST: vararg
// EXIST: when
// EXIST: where
// EXIST: while
+50 -6
View File
@@ -1,8 +1,52 @@
<caret> <caret>
// EXIST: namespace, as, type, class, this, super, val, var, fun, for, null, true // EXIST: abstract
// EXIST: false, is, in, throw, return, break, continue, object, if, try, else, while // ABSENT: annotation
// EXIST: do, when, trait, This // ABSENT: as
// EXIST: import, where, by, get, set, abstract, enum, open, annotation, override, private // ABSENT: break
// EXIST: public, internal, protected, catch, out, vararg, inline, finally, final, ref // ABSENT: by
// ABSENT: ?in, new, extends, implements // ABSENT: catch
// EXIST: class
// ABSENT: continue
// ABSENT: default
// ABSENT: do
// ABSENT: else
// EXIST: enum
// ABSENT: false
// EXIST: final
// ABSENT: finally
// ABSENT: for
// EXIST: fun
// EXIST: get
// ABSENT: if
// EXIST: import
// ABSENT: in
// EXIST: inline
// EXIST: internal
// ABSENT: is
// ABSENT: null
// ABSENT: object
// EXIST: open
// ABSENT: out
// ABSENT: override
// EXIST: package
// EXIST: private
// EXIST: protected
// EXIST: public
// ABSENT: ref
// ABSENT: return
// EXIST: set
// ABSENT: super
// ABSENT: This
// ABSENT: this
// ABSENT: throw
// EXIST: trait
// ABSENT: true
// ABSENT: try
// EXIST: type
// EXIST: val
// EXIST: var
// ABSENT: vararg
// ABSENT: when
// ABSENT: where
// ABSENT: while
@@ -12,15 +12,15 @@ import java.io.IOException;
public class KeywordsHandlerTest extends LightCompletionTestCase { public class KeywordsHandlerTest extends LightCompletionTestCase {
public void testSpaceAfter() throws IOException { public void testSpaceAfter() throws IOException {
configureFromFileText("Test.kt", "protecte<caret>"); configureFromFileText("Test.kt", "impor<caret>");
complete(); complete();
checkResultByText("protected <caret>"); checkResultByText("import <caret>");
} }
public void testNoSpaceAfter() throws IOException { public void testNoSpaceAfter() throws IOException {
configureFromFileText("Test.kt", "nul<caret>"); configureFromFileText("Test.kt", "fun test() { nul<caret> }");
complete(); complete();
checkResultByText("null<caret>"); checkResultByText("fun test() { null<caret> }");
} }
@Override @Override