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;
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<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 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<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() {
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 =
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<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>
}
// 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>
}
// 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>
}
// 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
+50 -6
View File
@@ -1,8 +1,52 @@
<caret>
// 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
// 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
@@ -12,15 +12,15 @@ import java.io.IOException;
public class KeywordsHandlerTest extends LightCompletionTestCase {
public void testSpaceAfter() throws IOException {
configureFromFileText("Test.kt", "protecte<caret>");
configureFromFileText("Test.kt", "impor<caret>");
complete();
checkResultByText("protected <caret>");
checkResultByText("import <caret>");
}
public void testNoSpaceAfter() throws IOException {
configureFromFileText("Test.kt", "nul<caret>");
configureFromFileText("Test.kt", "fun test() { nul<caret> }");
complete();
checkResultByText("null<caret>");
checkResultByText("fun test() { null<caret> }");
}
@Override