KT-8529 Code completion for parameter name and type together replaces wrong range on Tab

#KT-8529 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-01-20 21:24:15 +03:00
parent bc8095ce33
commit 53fe1568c9
7 changed files with 72 additions and 10 deletions
@@ -108,9 +108,9 @@ class KotlinCompletionContributor : CompletionContributor() {
?: DEFAULT_DUMMY_IDENTIFIER
}
if (context.completionType == CompletionType.SMART && !isAtEndOfLine(offset, context.editor.document) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
val tokenAt = psiFile.findElementAt(Math.max(0, offset))
if (tokenAt != null) {
val tokenAt = psiFile.findElementAt(Math.max(0, offset))
if (tokenAt != null) {
if (context.completionType == CompletionType.SMART && !isAtEndOfLine(offset, context.editor.document) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
var parent = tokenAt.parent
if (parent is KtExpression && parent !is KtBlockExpression) {
// search expression to be replaced - go up while we are the first child of parent expression
@@ -131,11 +131,19 @@ class KotlinCompletionContributor : CompletionContributor() {
val argumentList = (expression.parent as? KtValueArgument)?.parent as? KtValueArgumentList
if (argumentList != null) {
context.offsetMap.addOffset(SmartCompletion.MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET,
argumentList.rightParenthesis?.textRange?.startOffset ?: argumentList.endOffset)
argumentList.rightParenthesis?.textRange?.startOffset ?: argumentList.endOffset)
}
}
}
if (tokenAt.node.elementType == KtTokens.IDENTIFIER) {
val parameter = tokenAt.parent as? KtParameter
if (parameter != null) {
context.offsetMap.addOffset(ParameterNameAndTypeCompletion.REPLACEMENT_OFFSET, parameter.endOffset)
}
}
}
}
private fun replacementOffsetByExpression(expression: KtExpression): Int {
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionInitializationContext
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher
import com.intellij.codeInsight.lookup.*
import com.intellij.openapi.progress.ProgressManager
@@ -30,9 +27,12 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.psi.codeStyle.NameUtil
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.completion.handlers.isCharAt
import org.jetbrains.kotlin.idea.completion.handlers.skipSpaces
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.getResolutionScope
@@ -206,6 +206,22 @@ class ParameterNameAndTypeCompletion(
}
override fun handleInsert(context: InsertionContext) {
if (context.completionChar == Lookup.REPLACE_SELECT_CHAR) {
val replacementOffset = context.offsetMap.getOffset(REPLACEMENT_OFFSET)
if (replacementOffset != -1) {
val tailOffset = context.tailOffset
context.document.deleteString(tailOffset, replacementOffset)
val chars = context.document.charsSequence
var offset = chars.skipSpaces(tailOffset)
if (chars.isCharAt(offset, ',')) {
offset++
offset = chars.skipSpaces(offset)
context.editor.moveCaret(offset)
}
}
}
val settings = CodeStyleSettingsManager.getInstance(context.project).currentSettings.getCustomSettings(KotlinCodeStyleSettings::class.java)
val spaceBefore = if (settings.SPACE_BEFORE_TYPE_COLON) " " else ""
val spaceAfter = if (settings.SPACE_AFTER_TYPE_COLON) " " else ""
@@ -224,8 +240,10 @@ class ParameterNameAndTypeCompletion(
override fun hashCode() = parameterName.hashCode()
}
private companion object {
val PRIORITY_KEY = Key<Int>("ParameterNameAndTypeCompletion.PRIORITY_KEY")
companion object {
private val PRIORITY_KEY = Key<Int>("ParameterNameAndTypeCompletion.PRIORITY_KEY")
val REPLACEMENT_OFFSET = OffsetKey.create("ParameterNameAndTypeCompletion.REPLACEMENT_OFFSET")
}
object Weigher : LookupElementWeigher("kotlin.parameterNameAndTypePriority") {
@@ -0,0 +1,6 @@
class FooBar
fun f(b<caret>pp: (Int, Char) -> String)
// ELEMENT_TEXT: bar: FooBar
// CHAR: '\t'
@@ -0,0 +1,6 @@
class FooBar
fun f(bar: FooBar<caret>)
// ELEMENT_TEXT: bar: FooBar
// CHAR: '\t'
@@ -0,0 +1,6 @@
class FooBar
fun f(b<caret>pp: Int, c: Char)
// ELEMENT_TEXT: bar: FooBar
// CHAR: '\t'
@@ -0,0 +1,6 @@
class FooBar
fun f(bar: FooBar, <caret>c: Char)
// ELEMENT_TEXT: bar: FooBar
// CHAR: '\t'
@@ -562,6 +562,18 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
doTest(fileName);
}
@TestMetadata("TabReplace1.kt")
public void testTabReplace1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/TabReplace1.kt");
doTest(fileName);
}
@TestMetadata("TabReplace2.kt")
public void testTabReplace2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/TabReplace2.kt");
doTest(fileName);
}
@TestMetadata("TypeParameter.kt")
public void testTypeParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/parameterNameAndType/TypeParameter.kt");