diff --git a/compiler/frontend/src/org/jetbrains/jet/lexer/JetKeywordToken.java b/compiler/frontend/src/org/jetbrains/jet/lexer/JetKeywordToken.java index cc3c35dd1f7..920fd65766b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lexer/JetKeywordToken.java +++ b/compiler/frontend/src/org/jetbrains/jet/lexer/JetKeywordToken.java @@ -8,10 +8,16 @@ import org.jetbrains.annotations.NotNull; public class JetKeywordToken extends JetToken { + /** + * Generate soft keyword (identifier that has a keyword meaning only in some contexts) + */ public static JetKeywordToken keyword(String value) { return new JetKeywordToken(value, false); } + /** + * Generate keyword (identifier that has a keyword meaning in all possible contexts) + */ public static JetKeywordToken softKeyword(String value) { return new JetKeywordToken(value, true); } @@ -29,6 +35,7 @@ public class JetKeywordToken extends JetToken { return myValue; } + public boolean isSoft() { return myIsSoft; } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 3c218cee23f..472e7e9f54e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -42,6 +42,7 @@ + @@ -57,6 +58,7 @@ + { + + 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 + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + + for (String keyword : COMPLETE_KEYWORD) { + result.addElement(LookupElementBuilder.create(keyword).setBold()); + } + + for (String softKeyword : COMPLETE_SOFT_KEYWORDS) { + result.addElement(LookupElementBuilder.create(softKeyword)); + } + } + } + + public JetKeywordCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new JetKeywordCompletionProvider()); + } +} diff --git a/idea/testData/completion/keywords/InFunctionScope.kt b/idea/testData/completion/keywords/InFunctionScope.kt new file mode 100644 index 00000000000..c59f322f1a8 --- /dev/null +++ b/idea/testData/completion/keywords/InFunctionScope.kt @@ -0,0 +1,5 @@ +fun foo() { + +} + +// EXIST: val, var diff --git a/idea/testData/completion/keywords/InTypeScope.kt b/idea/testData/completion/keywords/InTypeScope.kt new file mode 100644 index 00000000000..31f2249d499 --- /dev/null +++ b/idea/testData/completion/keywords/InTypeScope.kt @@ -0,0 +1,6 @@ +fun foo() { + val test : +} + +// TODO: Move all keywords to absent +// EXPECT: fun, val, var, namespace diff --git a/idea/testData/completion/keywords/TopScope.kt b/idea/testData/completion/keywords/TopScope.kt new file mode 100644 index 00000000000..a151d11e196 --- /dev/null +++ b/idea/testData/completion/keywords/TopScope.kt @@ -0,0 +1,8 @@ + + +// EXPECT: namespace, as, type, class, this, super, val, var, fun, for, null, true +// EXPECT: false, is, in, throw, return, break, continue, object, if, try, else, while +// EXPECT: do, when, trait, This +// EXPECT: import, where, by, get, set, abstract, enum, open, annotation, override, private +// EXPECT: public, internal, protected, catch, out, vararg, inline, finally, final, ref +// ABSENT: ?in, new, extends, implements \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java new file mode 100644 index 00000000000..c0ff4263210 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/completion/JetCompletionTestBase.java @@ -0,0 +1,115 @@ +package org.jetbrains.jet.completion; + +import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupEx; +import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.testFramework.LightCodeInsightTestCase; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +/** + * @author Nikolay.Krasko + */ +public abstract class JetCompletionTestBase extends LightCodeInsightTestCase { + + protected void doTest() { + final String testName = getTestName(false); + configureByFile(testName + ".kt"); + + CompletionType completionType = (testName.startsWith("Smart")) ? CompletionType.SMART : CompletionType.BASIC; + new CodeCompletionHandlerBase(completionType, false, false, true).invokeCompletion(getProject(), getEditor()); + + LookupEx lookup = LookupManager.getActiveLookup(getEditor()); + assert lookup != null; + + HashSet items = new HashSet(resolveLookups(lookup.getItems())); + + List shouldExist = itemsShouldExist(getFile().getText()); + for (String shouldExistItem : shouldExist) { + assertTrue(String.format("Should contain proposal '%s'.", shouldExistItem), + items.contains(shouldExistItem)); + } + + List shouldAbsent = itemsShouldAbsent(getFile().getText()); + for (String shouldAbsentItem : shouldAbsent) { + assertTrue(String.format("Shouldn't contain proposal '%s'.", shouldAbsentItem), + !items.contains(shouldAbsentItem)); + } + } + + private static List resolveLookups(List items) { + ArrayList result = new ArrayList(items.size()); + for (LookupElement item : items) { + result.add(item.getLookupString()); + } + + return result; + } + + @NotNull + private static List itemsShouldExist(String fileText) { + return findListWithPrefix("// EXIST:", fileText); + } + + @NotNull + private static List itemsShouldAbsent(String fileText) { + return findListWithPrefix("// ABSENT:", fileText); + } + + @NotNull + private static List findListWithPrefix(String prefix, String fileText) { + ArrayList result = new ArrayList(); + + for (String line : findLinesWithPrefixRemoved(prefix, fileText)) { + String[] completions = line.split(","); + + for (String completion : completions) { + result.add(completion.trim()); + } + } + + return result; + } + + @NotNull + private static List findLinesWithPrefixRemoved(String prefix, String fileText) { + ArrayList result = new ArrayList(); + + for (String line : fileNonEmptyLines(fileText)) { + if (line.startsWith(prefix)) { + result.add(line.substring(prefix.length()).trim()); + } + } + + return result; + } + + @NotNull + private static List fileNonEmptyLines(String fileText) { + ArrayList result = new ArrayList(); + + BufferedReader reader = new BufferedReader(new StringReader(fileText)); + + try { + String line; + + while ((line = reader.readLine()) != null) { + if (!line.isEmpty()) { + result.add(line.trim()); + } + } + } catch(IOException e) { + assert false; + } + + return result; + } +} diff --git a/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java new file mode 100644 index 00000000000..7cb30ecfe96 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/completion/KeywordsCompletionTest.java @@ -0,0 +1,61 @@ +package org.jetbrains.jet.completion; + +import junit.framework.TestSuite; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.io.File; + +/** + * Test auto completion messages + * + * @author Nikolay.Krasko + */ +public class KeywordsCompletionTest extends JetCompletionTestBase { + + private final String myPath; + private final String myName; + + public KeywordsCompletionTest(@NotNull String path, @NotNull String name) { + myPath = path; + myName = name; + + // Set name explicitly because otherwise there will be "TestCase.fName cannot be null" + setName("testComletionExecute"); + } + + public void testComletionExecute() { + doTest(); + } + + @Override + protected String getTestDataPath() { + return new File(PluginTestCaseBase.getTestDataPathBase(), myPath).getPath() + + File.separator; + } + + @NotNull + @Override + public String getName() { + return "test" + myName; + } + + @NotNull + public static TestSuite suite() { + TestSuite suite = new TestSuite(); + + JetTestCaseBuilder.appendTestsInDirectory( + PluginTestCaseBase.getTestDataPathBase(), "/completion/keywords/", false, + JetTestCaseBuilder.emptyFilter, new JetTestCaseBuilder.NamedTestFactory() { + + @NotNull + @Override + public junit.framework.Test createTest(@NotNull String dataPath, @NotNull String name) { + return new KeywordsCompletionTest(dataPath, name); + } + }, suite); + + return suite; + } +} \ No newline at end of file