Code completion: space after comma on selecting item with comma key
This commit is contained in:
@@ -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<Boolean> = Key("KotlinCompletionCharFilter.ACCEPT_OPENNING_BRACE")
|
||||
public val ACCEPT_EQ: Key<Boolean> = Key("KotlinCompletionCharFilter.ACCEPT_EQ")
|
||||
|
||||
public val SELECTED_ITEM_PREFIX: Key<String> = 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LookupElement>()
|
||||
@@ -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<LookupElement>(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())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,14 @@ class WithTailInsertHandler(val tailText: String,
|
||||
val overwriteText: Boolean = true) : InsertHandler<LookupElement> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,19 +71,19 @@ fun LookupElement.addTail(tail: Tail?): LookupElement {
|
||||
|
||||
Tail.COMMA -> object: LookupElementDecorator<LookupElement>(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<LookupElement>(this) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
handlers.WithTailInsertHandler(")", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate())
|
||||
WithTailInsertHandler.rparenthTail().handleInsert(context, getDelegate())
|
||||
}
|
||||
}
|
||||
|
||||
Tail.ELSE -> object: LookupElementDecorator<LookupElement>(this) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
handlers.WithTailInsertHandler("else", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate())
|
||||
WithTailInsertHandler.elseTail().handleInsert(context, getDelegate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ fun foo(p1: Int) { }
|
||||
fun foo(p1: Int, p2: Int) { }
|
||||
|
||||
fun bar(ppp: Int, a: Int) {
|
||||
foo(ppp,<caret>)
|
||||
foo(ppp, <caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: ppp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fun foo(p1: Int, p2: Int) { }
|
||||
|
||||
fun bar(pp: Int, a: Int) {
|
||||
foo(pp,<caret>)
|
||||
foo(pp, <caret>)
|
||||
}
|
||||
|
||||
// COMPLETION_TYPE: SMART
|
||||
|
||||
@@ -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<caret>)
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 0
|
||||
// ELEMENT: *
|
||||
// CHAR: ','
|
||||
@@ -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,<caret>)
|
||||
}
|
||||
|
||||
// INVOCATION_COUNT: 0
|
||||
// ELEMENT: *
|
||||
// CHAR: ','
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo(p1: Int?, p2: Int) { }
|
||||
|
||||
fun bar(p: Int) {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: null
|
||||
// CHAR: ','
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo(p1: Int?, p2: Int) { }
|
||||
|
||||
fun bar(p: Int) {
|
||||
foo(null, <caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: null
|
||||
// CHAR: ','
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo(p1: Int?, p2: Int) { }
|
||||
|
||||
fun bar(nullable: Int?) {
|
||||
foo(null<caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: *
|
||||
// CHAR: ','
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo(p1: Int?, p2: Int) { }
|
||||
|
||||
fun bar(nullable: Int?) {
|
||||
foo(null,<caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: *
|
||||
// CHAR: ','
|
||||
+18
-1
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user