diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java index 6f7a57c8492..eaec4204faf 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.plugin.completion; import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.patterns.PlatformPatterns; import com.intellij.patterns.PsiElementPattern; @@ -11,6 +12,7 @@ import com.intellij.psi.filters.position.FilterPattern; import com.intellij.psi.filters.position.LeftNeighbour; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.plugin.completion.handlers.JetKeywordInsertHandler; /** * A keyword contributor for Kotlin @@ -21,7 +23,9 @@ import org.jetbrains.annotations.NotNull; public class JetKeywordCompletionContributor extends CompletionContributor { private static class JetTopKeywordCompletionProvider extends CompletionProvider { - + + 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", @@ -32,18 +36,19 @@ public class JetKeywordCompletionContributor extends CompletionContributor { "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()); + + result.addElement(LookupElementBuilder.create(keyword).setInsertHandler(KEYWORDS_INSERT_HANDLER).setBold()); } for (String softKeyword : COMPLETE_SOFT_KEYWORDS) { - result.addElement(LookupElementBuilder.create(softKeyword)); + result.addElement(LookupElementBuilder.create(softKeyword).setInsertHandler(KEYWORDS_INSERT_HANDLER)); } } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java new file mode 100644 index 00000000000..68aafbc70a1 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java @@ -0,0 +1,38 @@ +package org.jetbrains.jet.plugin.completion.handlers; + +import com.intellij.codeInsight.completion.InsertHandler; +import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.editor.Editor; + +/** + * Inserts '()' after function proposal insert. + * + * @author Nikolay Krasko + */ +public class JetFunctionInsertHandler implements InsertHandler { + + public enum CaretPosition { IN_BRACKETS, AFTER_BRACKETS } + + private final CaretPosition caretPosition; + + public JetFunctionInsertHandler(CaretPosition caretPosition) { + this.caretPosition = caretPosition; + } + + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + int startOffset = context.getStartOffset(); + int lookupStringLength = item.getLookupString().length(); + int endOffset = startOffset + lookupStringLength; + + context.getDocument().insertString(endOffset, "()"); + + Editor editor = context.getEditor(); + if (caretPosition == CaretPosition.IN_BRACKETS) { + editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() + 1); + } else { + editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() + 2); + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetKeywordInsertHandler.java b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetKeywordInsertHandler.java new file mode 100644 index 00000000000..2424d1a2c7a --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetKeywordInsertHandler.java @@ -0,0 +1,34 @@ +package org.jetbrains.jet.plugin.completion.handlers; + +import com.google.common.collect.Sets; +import com.intellij.codeInsight.completion.InsertHandler; +import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; + +import java.util.Set; + +/** + * @author Nikolay Krasko + */ +public class JetKeywordInsertHandler implements InsertHandler { + + private final static Set NO_SPACE_AFTER = Sets.newHashSet("this", "super", "This", "true", "false", "null"); + + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + String keyword = item.getLookupString(); + + // Add space after keyword + if (!NO_SPACE_AFTER.contains(keyword)) { + Editor editor = context.getEditor(); + Document document = editor.getDocument(); + + int offset = context.getStartOffset() + keyword.length(); + context.setAddCompletionChar(false); + document.insertString(offset, " "); + editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() + 1); + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.java index 3239d3c47ef..54e27ce690f 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.java @@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.Variance; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; +import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler; import org.jetbrains.jet.resolve.DescriptorRenderer; import java.util.List; @@ -37,6 +38,13 @@ import java.util.Set; * @author yole */ class JetSimpleNameReference extends JetPsiReference { + + private final static JetFunctionInsertHandler EMPTY_FUNCTION_HANDLER = new JetFunctionInsertHandler( + JetFunctionInsertHandler.CaretPosition.AFTER_BRACKETS); + + private final static JetFunctionInsertHandler PARAMS_FUNCTION_HANDLER = new JetFunctionInsertHandler( + JetFunctionInsertHandler.CaretPosition.IN_BRACKETS); + private final JetSimpleNameExpression myExpression; public JetSimpleNameReference(JetSimpleNameExpression jetSimpleNameExpression) { @@ -135,10 +143,12 @@ class JetSimpleNameReference extends JetPsiReference { String typeText = ""; String tailText = ""; boolean tailTextGrayed = false; + if (descriptor instanceof FunctionDescriptor) { FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; JetType returnType = functionDescriptor.getReturnType(); typeText = DescriptorRenderer.TEXT.renderType(returnType); + tailText = "(" + StringUtil.join(functionDescriptor.getValueParameters(), new Function() { @Override public String fun(ValueParameterDescriptor valueParameterDescriptor) { @@ -146,6 +156,14 @@ class JetSimpleNameReference extends JetPsiReference { DescriptorRenderer.TEXT.renderType(valueParameterDescriptor.getOutType()); } }, ",") + ")"; + + // TODO: A special case when it's impossible to resolve type parameters from arguments. Need '<' caret '>' + // TODO: Support omitting brackets for one argument functions + if (functionDescriptor.getValueParameters().isEmpty()) { + element = element.setInsertHandler(EMPTY_FUNCTION_HANDLER); + } else { + element = element.setInsertHandler(PARAMS_FUNCTION_HANDLER); + } } else if (descriptor instanceof VariableDescriptor) { JetType outType = ((VariableDescriptor) descriptor).getOutType(); @@ -170,8 +188,8 @@ class JetSimpleNameReference extends JetPsiReference { return result.toArray(); } - /** - * Checks that receiver declaration could be resolved to call expected receiver. + /* + * Checks if receiver declaration could be resolved to call expected receiver. */ private static boolean checkReceiverResolution ( @NotNull ReceiverDescriptor expectedReceiver, diff --git a/idea/testData/completion/handlers/NoParamsFunction.kt b/idea/testData/completion/handlers/NoParamsFunction.kt new file mode 100644 index 00000000000..120b5e55d02 --- /dev/null +++ b/idea/testData/completion/handlers/NoParamsFunction.kt @@ -0,0 +1,8 @@ +fun test() { +} + +fun other() { + te +} + +// INSERT: test \ No newline at end of file diff --git a/idea/testData/completion/handlers/NoParamsFunction.kt.after b/idea/testData/completion/handlers/NoParamsFunction.kt.after new file mode 100644 index 00000000000..920873c0b51 --- /dev/null +++ b/idea/testData/completion/handlers/NoParamsFunction.kt.after @@ -0,0 +1,8 @@ +fun test() { +} + +fun other() { + test() +} + +// INSERT: test \ No newline at end of file diff --git a/idea/testData/completion/handlers/ParamsFunction.kt b/idea/testData/completion/handlers/ParamsFunction.kt new file mode 100644 index 00000000000..c7910930150 --- /dev/null +++ b/idea/testData/completion/handlers/ParamsFunction.kt @@ -0,0 +1,6 @@ +fun test(a: Int) { +} + +fun other() { + te +} \ No newline at end of file diff --git a/idea/testData/completion/handlers/ParamsFunction.kt.after b/idea/testData/completion/handlers/ParamsFunction.kt.after new file mode 100644 index 00000000000..175892cc3a5 --- /dev/null +++ b/idea/testData/completion/handlers/ParamsFunction.kt.after @@ -0,0 +1,6 @@ +fun test(a: Int) { +} + +fun other() { + test() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/FunctionsHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/FunctionsHandlerTest.java new file mode 100644 index 00000000000..f265bde4714 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/completion/handlers/FunctionsHandlerTest.java @@ -0,0 +1,28 @@ +package org.jetbrains.jet.completion.handlers; + +import com.intellij.codeInsight.completion.LightCompletionTestCase; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.io.File; + +/** + * @author Nikolay Krasko + */ +public class FunctionsHandlerTest extends LightCompletionTestCase { + + public void testNoParamsFunction() { + configureByFile("NoParamsFunction.kt"); + checkResultByFile("NoParamsFunction.kt.after"); + } + + public void testFunctionWithParams() { + configureByFile("ParamsFunction.kt"); + checkResultByFile("ParamsFunction.kt.after"); + } + + @Override + protected String getTestDataPath() { + return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/handlers/").getPath() + + File.separator; + } +} diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java b/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java new file mode 100644 index 00000000000..4b6193b2549 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/completion/handlers/KeywordsHandlerTest.java @@ -0,0 +1,31 @@ +package org.jetbrains.jet.completion.handlers; + +import com.intellij.codeInsight.completion.LightCompletionTestCase; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.io.File; +import java.io.IOException; + +/** + * @author Nikolay Krasko + */ +public class KeywordsHandlerTest extends LightCompletionTestCase { + + public void testSpaceAfter() throws IOException { + configureFromFileText("Test.kt", "protecte"); + complete(); + checkResultByText("protected "); + } + + public void testNoSpaceAfter() throws IOException { + configureFromFileText("Test.kt", "nul"); + complete(); + checkResultByText("null"); + } + + @Override + protected String getTestDataPath() { + return new File(PluginTestCaseBase.getTestDataPathBase(), "/completion/handlers/").getPath() + + File.separator; + } +}