From f259b7d397a5fb7564e3e2dd09edc6b56807e8a6 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 28 Dec 2011 17:51:18 +0400 Subject: [PATCH] KT-759 Filter keywords auto completion according to context --- .../JetKeywordCompletionContributor.java | 161 ++++++++++++++---- .../completion/handlers/PropertiesSetter.kt | 4 + .../handlers/PropertiesSetter.kt.after | 4 + .../completion/keywords/AfterSpaceAndDot.kt | 2 +- .../completion/keywords/InClassScope.kt | 54 ++++++ .../completion/keywords/InFunctionScope.kt | 51 +++++- .../completion/keywords/InParametersList.kt | 54 ++++++ .../completion/keywords/InTopProperty.kt | 8 + .../completion/keywords/InTypeScope.kt | 52 +++++- idea/testData/completion/keywords/TopScope.kt | 56 +++++- .../handlers/KeywordsHandlerTest.java | 8 +- 11 files changed, 408 insertions(+), 46 deletions(-) create mode 100644 idea/testData/completion/handlers/PropertiesSetter.kt create mode 100644 idea/testData/completion/handlers/PropertiesSetter.kt.after create mode 100644 idea/testData/completion/keywords/InClassScope.kt create mode 100644 idea/testData/completion/keywords/InParametersList.kt create mode 100644 idea/testData/completion/keywords/InTopProperty.kt diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java index eaec4204faf..7ab756f2607 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java @@ -1,65 +1,162 @@ 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.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.patterns.ElementPattern; import com.intellij.patterns.PlatformPatterns; -import com.intellij.patterns.PsiElementPattern; 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.TextFilter; import com.intellij.psi.filters.position.FilterPattern; import com.intellij.psi.filters.position.LeftNeighbour; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; 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 java.util.Collection; +import java.util.List; + /** * A keyword contributor for Kotlin - * TODO: add different context for different keywords * * @author Nikolay Krasko */ public class JetKeywordCompletionContributor extends CompletionContributor { - private static class JetTopKeywordCompletionProvider extends CompletionProvider { + private final static InsertHandler KEYWORDS_INSERT_HANDLER = new JetKeywordInsertHandler(); + private final static InsertHandler FUNCTION_INSERT_HANDLER = new JetFunctionInsertHandler( + JetFunctionInsertHandler.CaretPosition.AFTER_BRACKETS); + + private final static List FUNCTION_KEYWORDS = Lists.newArrayList("get", "set"); - private final static InsertHandler KEYWORDS_INSERT_HANDLER = new JetKeywordInsertHandler(); - - 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" - }; - + private static class InTopFilter implements ElementFilter { @Override - protected void addCompletions(@NotNull CompletionParameters parameters, - ProcessingContext context, - @NotNull CompletionResultSet result) { + public boolean isAcceptable(Object element, PsiElement context) { + return PsiTreeUtil.getParentOfType(context, JetFile.class, false, JetClass.class, JetFunction.class) != null; + } - for (String keyword : COMPLETE_KEYWORD) { - - result.addElement(LookupElementBuilder.create(keyword).setInsertHandler(KEYWORDS_INSERT_HANDLER).setBold()); - } - - for (String softKeyword : COMPLETE_SOFT_KEYWORDS) { - result.addElement(LookupElementBuilder.create(softKeyword).setInsertHandler(KEYWORDS_INSERT_HANDLER)); - } + @Override + public boolean isClassAcceptable(Class hintClass) { + return true; } } + 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 { + + private final Collection elements; + + public KeywordsCompletionProvider(String ...keywords) { + List elementsList = Lists.newArrayList(keywords); + elements = Collections2.transform(elementsList, new Function() { + @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() { + registerScopeKeywordsCompletion(new InTopFilter(), + "abstract", "class", "enum", "final", "fun", "get", "import", "inline", + "internal", "open", "package", "private", "protected", "public", "set", + "trait", "type", "val", "var"); - PsiElementPattern.Capture notDotPlace = - PlatformPatterns.psiElement().and(new FilterPattern(new NotFilter(new LeftNeighbour(new TextFilter("."))))); + registerScopeKeywordsCompletion(new InClassBodyFilter(), + "abstract", "class", "enum", "final", "fun", "inline", "internal", "get", + "open", "override", "private", "protected", "public", "set", "trait", + "type", "val", "var"); - extend(CompletionType.BASIC, - notDotPlace, - new JetTopKeywordCompletionProvider()); + registerScopeKeywordsCompletion(new InNonClassBlockFilter(), + "as", "break", "by", "catch", "class", "continue", "default", "do", "else", + "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 getPlacePattern(final ElementFilter placeFilter) { + return PlatformPatterns.psiElement().and( + new FilterPattern(new AndFilter(new NotFilter(new LeftNeighbour(new TextFilter("."))), placeFilter))); } } diff --git a/idea/testData/completion/handlers/PropertiesSetter.kt b/idea/testData/completion/handlers/PropertiesSetter.kt new file mode 100644 index 00000000000..07e43cdaf16 --- /dev/null +++ b/idea/testData/completion/handlers/PropertiesSetter.kt @@ -0,0 +1,4 @@ +class TestClass { + val a : Int + ge +} \ No newline at end of file diff --git a/idea/testData/completion/handlers/PropertiesSetter.kt.after b/idea/testData/completion/handlers/PropertiesSetter.kt.after new file mode 100644 index 00000000000..acf5481fb09 --- /dev/null +++ b/idea/testData/completion/handlers/PropertiesSetter.kt.after @@ -0,0 +1,4 @@ +class TestClass { + val a : Int + get() +} \ No newline at end of file diff --git a/idea/testData/completion/keywords/AfterSpaceAndDot.kt b/idea/testData/completion/keywords/AfterSpaceAndDot.kt index bd0a7028c99..39fa179f90b 100644 --- a/idea/testData/completion/keywords/AfterSpaceAndDot.kt +++ b/idea/testData/completion/keywords/AfterSpaceAndDot.kt @@ -3,4 +3,4 @@ fun foo() { str. } -// ABSENT: public, val, in \ No newline at end of file +// ABSENT: public, val, in, if, when, this \ No newline at end of file diff --git a/idea/testData/completion/keywords/InClassScope.kt b/idea/testData/completion/keywords/InClassScope.kt new file mode 100644 index 00000000000..bbf7f7ef8ae --- /dev/null +++ b/idea/testData/completion/keywords/InClassScope.kt @@ -0,0 +1,54 @@ +class TestClass { + +} + +// 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 \ No newline at end of file diff --git a/idea/testData/completion/keywords/InFunctionScope.kt b/idea/testData/completion/keywords/InFunctionScope.kt index c59f322f1a8..1f51914c8e1 100644 --- a/idea/testData/completion/keywords/InFunctionScope.kt +++ b/idea/testData/completion/keywords/InFunctionScope.kt @@ -2,4 +2,53 @@ fun foo() { } -// 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 diff --git a/idea/testData/completion/keywords/InParametersList.kt b/idea/testData/completion/keywords/InParametersList.kt new file mode 100644 index 00000000000..38b0beec93c --- /dev/null +++ b/idea/testData/completion/keywords/InParametersList.kt @@ -0,0 +1,54 @@ +fun test() { + +} + +// 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 \ No newline at end of file diff --git a/idea/testData/completion/keywords/InTopProperty.kt b/idea/testData/completion/keywords/InTopProperty.kt new file mode 100644 index 00000000000..ea8bb7a4cbc --- /dev/null +++ b/idea/testData/completion/keywords/InTopProperty.kt @@ -0,0 +1,8 @@ +var a : Int = + +// EXIST: else +// EXIST: false +// EXIST: if +// EXIST: null +// EXIST: this +// EXIST: true \ No newline at end of file diff --git a/idea/testData/completion/keywords/InTypeScope.kt b/idea/testData/completion/keywords/InTypeScope.kt index c8a490df14c..e83a1f01f90 100644 --- a/idea/testData/completion/keywords/InTypeScope.kt +++ b/idea/testData/completion/keywords/InTypeScope.kt @@ -2,5 +2,53 @@ fun foo() { val test : } -// TODO: Move all keywords to absent -// EXIST: fun, val, var, namespace +// 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 diff --git a/idea/testData/completion/keywords/TopScope.kt b/idea/testData/completion/keywords/TopScope.kt index 308a7710384..52efbe1d427 100644 --- a/idea/testData/completion/keywords/TopScope.kt +++ b/idea/testData/completion/keywords/TopScope.kt @@ -1,8 +1,52 @@ -// EXIST: namespace, as, type, class, this, super, val, var, fun, for, null, true -// EXIST: false, is, in, throw, return, break, continue, object, if, try, else, while -// EXIST: do, when, trait, This -// EXIST: import, where, by, get, set, abstract, enum, open, annotation, override, private -// EXIST: public, internal, protected, catch, out, vararg, inline, finally, final, ref -// ABSENT: ?in, new, extends, implements \ No newline at end of file +// 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 +// 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 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java index 4b6193b2549..ce2ee714511 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java @@ -12,15 +12,15 @@ import java.io.IOException; public class KeywordsHandlerTest extends LightCompletionTestCase { public void testSpaceAfter() throws IOException { - configureFromFileText("Test.kt", "protecte"); + configureFromFileText("Test.kt", "impor"); complete(); - checkResultByText("protected "); + checkResultByText("import "); } public void testNoSpaceAfter() throws IOException { - configureFromFileText("Test.kt", "nul"); + configureFromFileText("Test.kt", "fun test() { nul }"); complete(); - checkResultByText("null"); + checkResultByText("fun test() { null }"); } @Override