KT-13047 Autocomplete inserts a fully-qualified variable name inside a String

KT-12083 String interpolation -- auto {} if there is a letter after cursor

 #KT-13047 Fixed
 #KT-12083 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-09-12 19:06:58 +03:00
parent 6e02051be5
commit 5bf701ab03
15 changed files with 159 additions and 1 deletions
@@ -24,11 +24,17 @@ import org.jetbrains.kotlin.idea.util.CallType
class KotlinPropertyInsertHandler(callType: CallType<*>) : KotlinCallableInsertHandler(callType) {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val surroundedWithBraces = surroundWithBracesIfInStringTemplate(context)
super.handleInsert(context, item)
if (context.completionChar == Lookup.REPLACE_SELECT_CHAR) {
deleteEmptyParenthesis(context)
}
if (surroundedWithBraces) {
removeRedundantBracesInStringTemplate(context)
}
}
private fun deleteEmptyParenthesis(context: InsertionContext) {
@@ -29,9 +29,12 @@ import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.completion.KeywordLookupObject
import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.canPlaceAfterSimpleNameEntry
fun surroundWithBracesIfInStringTemplate(context: InsertionContext) {
fun surroundWithBracesIfInStringTemplate(context: InsertionContext): Boolean {
val startOffset = context.startOffset
val document = context.document
if (startOffset > 0 && document.charsSequence[startOffset - 1] == '$') {
@@ -47,6 +50,30 @@ fun surroundWithBracesIfInStringTemplate(context: InsertionContext) {
val tailOffset = context.tailOffset
document.insertString(tailOffset, "}")
context.tailOffset = tailOffset
return true
}
}
return false
}
fun removeRedundantBracesInStringTemplate(context: InsertionContext) {
val document = context.document
val tailOffset = context.tailOffset
if (document.charsSequence[tailOffset] == '}') {
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
psiDocumentManager.commitAllDocuments()
val token = context.file.findElementAt(tailOffset)
if (token != null && token.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_END) {
val entry = token.parent as KtBlockStringTemplateEntry
val nameExpression = entry.expression as? KtNameReferenceExpression ?: return
if (canPlaceAfterSimpleNameEntry(entry.nextSibling)) {
context.tailOffset++ // place after '}' otherwise it gets invalidated
val name = nameExpression.getReferencedName()
val newEntry = KtPsiFactory(entry).createSimpleNameStringTemplateEntry(name)
entry.replace(newEntry)
}
}
}
}
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "$<caret>"
}
// ELEMENT: vvv
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "$vvv<caret>"
}
// ELEMENT: vvv
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "${<caret>}"
}
// ELEMENT: vvv
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "${vvv}"
}
// ELEMENT: vvv
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "$<caret>word"
}
// ELEMENT: vvv
@@ -0,0 +1,9 @@
package p
val vvv = 1
fun foo() {
val s = "${vvv<caret>}word"
}
// ELEMENT: vvv
@@ -0,0 +1,14 @@
package p
object OOO {
val vvv = 1
}
class C {
fun foo() {
"$v<caret>"
}
}
// ELEMENT: vvv
// INVOCATION_COUNT: 2
@@ -0,0 +1,14 @@
package p
object OOO {
val vvv = 1
}
class C {
fun foo() {
"${OOO.vvv}"
}
}
// ELEMENT: vvv
// INVOCATION_COUNT: 2
@@ -0,0 +1,5 @@
package some
fun other() {
val v = "$somePr<caret>"
}
@@ -0,0 +1,3 @@
package other
val someProp: Int = 1
@@ -0,0 +1,7 @@
package some
import other.someProp
fun other() {
val v = "$someProp<caret>"
}
@@ -709,6 +709,24 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/stringTemplate"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("GlobalVal.kt")
public void testGlobalVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt");
doTest(fileName);
}
@TestMetadata("GlobalValInCurlyBraces.kt")
public void testGlobalValInCurlyBraces() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt");
doTest(fileName);
}
@TestMetadata("InsertCurlyBracesBeforeLetter.kt")
public void testInsertCurlyBracesBeforeLetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt");
doTest(fileName);
}
@TestMetadata("NotEmptyPrefix.kt")
public void testNotEmptyPrefix() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/NotEmptyPrefix.kt");
@@ -720,6 +738,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/Replace.kt");
doTest(fileName);
}
@TestMetadata("ValInObject.kt")
public void testValInObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/handlers/basic/typeArgsForCall")
@@ -49,6 +49,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
doTest()
}
fun testTopLevelValImportInStringTemplate() {
doTest()
}
fun testNoParenthesisInImports() {
doTest()
}