diff --git a/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionCharFilter.kt b/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionCharFilter.kt index f73ec225b90..68e83a9b8a3 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionCharFilter.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionCharFilter.kt @@ -20,16 +20,18 @@ import com.intellij.codeInsight.lookup.CharFilter import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.CharFilter.Result import org.jetbrains.jet.lang.psi.JetFile -import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler import com.intellij.openapi.util.Key +import org.jetbrains.jet.lang.descriptors.VariableDescriptor public class KotlinCompletionCharFilter() : CharFilter() { class object { public val ACCEPT_OPENING_BRACE: Key = Key("KotlinCompletionCharFilter.ACCEPT_OPENNING_BRACE") public val ACCEPT_EQ: Key = Key("KotlinCompletionCharFilter.ACCEPT_EQ") + + public val SELECTED_ITEM_PREFIX: Key = Key("KotlinCompletionCharFilter.SELECTED_ITEM_PREFIX") } - public override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? { + override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? { if (lookup.getPsiFile() !is JetFile) return null if (!lookup.isCompletion()) return null @@ -38,6 +40,8 @@ public class KotlinCompletionCharFilter() : CharFilter() { } val currentItem = lookup.getCurrentItem() + currentItem?.putUserData(SELECTED_ITEM_PREFIX, lookup.itemPattern(currentItem)) + return when (c) { '.' -> { //TODO: this heuristics better to be only used for auto-popup completion but I see no way to check this @@ -66,7 +70,7 @@ public class KotlinCompletionCharFilter() : CharFilter() { ',', ' ', '(' -> Result.SELECT_ITEM_AND_FINISH_LOOKUP - else -> return CharFilter.Result.HIDE_LOOKUP + else -> CharFilter.Result.HIDE_LOOKUP } } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt index 8d728ecbf18..c15a6261066 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementsCollector.kt @@ -28,6 +28,7 @@ import com.intellij.codeInsight.completion.PrefixMatcher import java.util.ArrayList import com.intellij.codeInsight.completion.CompletionResultSet import org.jetbrains.jet.lang.descriptors.FunctionDescriptor +import com.intellij.openapi.util.TextRange class LookupElementsCollector(private val prefixMatcher: PrefixMatcher, private val resolveSession: ResolveSessionForBodies) { private val elements = ArrayList() @@ -89,7 +90,18 @@ class LookupElementsCollector(private val prefixMatcher: PrefixMatcher, private public fun addElement(element: LookupElement) { if (prefixMatcher.prefixMatches(element)) { - elements.add(element) + elements.add(object: LookupElementDecorator(element) { + override fun handleInsert(context: InsertionContext) { + getDelegate().handleInsert(context) + + if (context.getCompletionChar() == ',' && context.shouldAddCompletionChar()) { + val insertedText = context.getDocument().getText(TextRange(context.getStartOffset(), context.getTailOffset())) + if (insertedText != getUserData(KotlinCompletionCharFilter.SELECTED_ITEM_PREFIX)) { // avoid insertion of space after comma if we have typed the whole text to insert already + WithTailInsertHandler.commaTail().postHandleInsert(context, getDelegate()) + } + } + } + }) } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt index 854d7a5a8a2..de12a280431 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/WithTailInsertHandler.kt @@ -31,10 +31,14 @@ class WithTailInsertHandler(val tailText: String, val overwriteText: Boolean = true) : InsertHandler { override fun handleInsert(context: InsertionContext, item: LookupElement) { item.handleInsert(context) + postHandleInsert(context, item) + } - if (tailText == context.getCompletionChar().toString() && context.shouldAddCompletionChar()) { - return + fun postHandleInsert(context: InsertionContext, item: LookupElement) { + if (tailText == context.getCompletionChar().toString()) { + context.setAddCompletionChar(false) } + //TODO: what if completion char is different? val document = context.getDocument() PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(document) @@ -47,10 +51,10 @@ class WithTailInsertHandler(val tailText: String, val moveCaret = context.getEditor().getCaretModel().getOffset() == tailOffset - fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == c - fun isTextAt(offset: Int, text: String) = offset + text.length <= document.getTextLength() && document.getText(TextRange(offset, offset + text.length)) == text - if (overwriteText) { + fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == c + fun isTextAt(offset: Int, text: String) = offset + text.length <= document.getTextLength() && document.getText(TextRange(offset, offset + text.length)) == text + if (spaceBefore && isCharAt(tailOffset, ' ')) { document.deleteString(tailOffset, tailOffset + 1) } @@ -79,4 +83,10 @@ class WithTailInsertHandler(val tailText: String, } } } + + class object { + fun commaTail() = WithTailInsertHandler(",", spaceBefore = false, spaceAfter = true /*TODO: use code style option*/) + fun rparenthTail() = WithTailInsertHandler(")", spaceBefore = false, spaceAfter = false) + fun elseTail() = WithTailInsertHandler("else", spaceBefore = true, spaceAfter = true) + } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt index b13fa137228..914f3e13328 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/Utils.kt @@ -71,19 +71,19 @@ fun LookupElement.addTail(tail: Tail?): LookupElement { Tail.COMMA -> object: LookupElementDecorator(this) { override fun handleInsert(context: InsertionContext) { - WithTailInsertHandler(",", spaceBefore = false, spaceAfter = true /*TODO: use code style option*/).handleInsert(context, getDelegate()) + WithTailInsertHandler.commaTail().handleInsert(context, getDelegate()) } } Tail.RPARENTH -> object: LookupElementDecorator(this) { override fun handleInsert(context: InsertionContext) { - handlers.WithTailInsertHandler(")", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate()) + WithTailInsertHandler.rparenthTail().handleInsert(context, getDelegate()) } } Tail.ELSE -> object: LookupElementDecorator(this) { override fun handleInsert(context: InsertionContext) { - handlers.WithTailInsertHandler("else", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate()) + WithTailInsertHandler.elseTail().handleInsert(context, getDelegate()) } } } diff --git a/idea/testData/completion/handlers/charFilter/Comma1.kt.after b/idea/testData/completion/handlers/charFilter/Comma1.kt.after index 3936989074c..f911132d50f 100644 --- a/idea/testData/completion/handlers/charFilter/Comma1.kt.after +++ b/idea/testData/completion/handlers/charFilter/Comma1.kt.after @@ -2,7 +2,7 @@ fun foo(p1: Int) { } fun foo(p1: Int, p2: Int) { } fun bar(ppp: Int, a: Int) { - foo(ppp,) + foo(ppp, ) } // ELEMENT: ppp diff --git a/idea/testData/completion/handlers/charFilter/Comma2.kt.after b/idea/testData/completion/handlers/charFilter/Comma2.kt.after index 227be05d111..f362d086971 100644 --- a/idea/testData/completion/handlers/charFilter/Comma2.kt.after +++ b/idea/testData/completion/handlers/charFilter/Comma2.kt.after @@ -1,7 +1,7 @@ fun foo(p1: Int, p2: Int) { } fun bar(pp: Int, a: Int) { - foo(pp,) + foo(pp, ) } // COMPLETION_TYPE: SMART diff --git a/idea/testData/completion/handlers/charFilter/Comma3.kt b/idea/testData/completion/handlers/charFilter/Comma3.kt new file mode 100644 index 00000000000..3e17381dbec --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma3.kt @@ -0,0 +1,11 @@ +// this test tests that no space auto-inserted after comma if we are just typing + +fun foo(p1: Int, p2: Int) { } + +fun bar(ppp: Int, ppp1: Int) { + foo(ppp) +} + +// INVOCATION_COUNT: 0 +// ELEMENT: * +// CHAR: ',' diff --git a/idea/testData/completion/handlers/charFilter/Comma3.kt.after b/idea/testData/completion/handlers/charFilter/Comma3.kt.after new file mode 100644 index 00000000000..5531fef8740 --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma3.kt.after @@ -0,0 +1,11 @@ +// this test tests that no space auto-inserted after comma if we are just typing + +fun foo(p1: Int, p2: Int) { } + +fun bar(ppp: Int, ppp1: Int) { + foo(ppp,) +} + +// INVOCATION_COUNT: 0 +// ELEMENT: * +// CHAR: ',' diff --git a/idea/testData/completion/handlers/charFilter/Comma4.kt b/idea/testData/completion/handlers/charFilter/Comma4.kt new file mode 100644 index 00000000000..379d0241cd7 --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma4.kt @@ -0,0 +1,8 @@ +fun foo(p1: Int?, p2: Int) { } + +fun bar(p: Int) { + foo() +} + +// ELEMENT: null +// CHAR: ',' diff --git a/idea/testData/completion/handlers/charFilter/Comma4.kt.after b/idea/testData/completion/handlers/charFilter/Comma4.kt.after new file mode 100644 index 00000000000..cd782bdf028 --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma4.kt.after @@ -0,0 +1,8 @@ +fun foo(p1: Int?, p2: Int) { } + +fun bar(p: Int) { + foo(null, ) +} + +// ELEMENT: null +// CHAR: ',' diff --git a/idea/testData/completion/handlers/charFilter/Comma5.kt b/idea/testData/completion/handlers/charFilter/Comma5.kt new file mode 100644 index 00000000000..18f6b30bdb8 --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma5.kt @@ -0,0 +1,8 @@ +fun foo(p1: Int?, p2: Int) { } + +fun bar(nullable: Int?) { + foo(null) +} + +// ELEMENT: * +// CHAR: ',' diff --git a/idea/testData/completion/handlers/charFilter/Comma5.kt.after b/idea/testData/completion/handlers/charFilter/Comma5.kt.after new file mode 100644 index 00000000000..cada5433646 --- /dev/null +++ b/idea/testData/completion/handlers/charFilter/Comma5.kt.after @@ -0,0 +1,8 @@ +fun foo(p1: Int?, p2: Int) { } + +fun bar(nullable: Int?) { + foo(null,) +} + +// ELEMENT: * +// CHAR: ',' diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionCharFilterTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionCharFilterTestGenerated.java index da658eaffc4..5dbee37afa7 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/CompletionCharFilterTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/CompletionCharFilterTestGenerated.java @@ -19,7 +19,6 @@ package org.jetbrains.jet.completion.handlers; import com.intellij.testFramework.TestDataPath; import org.jetbrains.jet.JUnit3RunnerWithInners; import org.jetbrains.jet.JetTestUtils; -import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; import org.junit.runner.RunWith; @@ -54,6 +53,24 @@ public class CompletionCharFilterTestGenerated extends AbstractCompletionCharFil doTest(fileName); } + @TestMetadata("Comma3.kt") + public void testComma3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/Comma3.kt"); + doTest(fileName); + } + + @TestMetadata("Comma4.kt") + public void testComma4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/Comma4.kt"); + doTest(fileName); + } + + @TestMetadata("Comma5.kt") + public void testComma5() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/Comma5.kt"); + doTest(fileName); + } + @TestMetadata("ConstructorWithLambdaArg1.kt") public void testConstructorWithLambdaArg1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/charFilter/ConstructorWithLambdaArg1.kt");