From 5bf701ab033396d30847ad98ac2e5fe81f1d2319 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 12 Sep 2016 19:06:58 +0300 Subject: [PATCH] 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 --- .../handlers/KotlinPropertyInsertHandler.kt | 6 ++++ .../idea/completion/handlers/handlerUtils.kt | 29 ++++++++++++++++++- .../basic/stringTemplate/GlobalVal.kt | 9 ++++++ .../basic/stringTemplate/GlobalVal.kt.after | 9 ++++++ .../stringTemplate/GlobalValInCurlyBraces.kt | 9 ++++++ .../GlobalValInCurlyBraces.kt.after | 9 ++++++ .../InsertCurlyBracesBeforeLetter.kt | 9 ++++++ .../InsertCurlyBracesBeforeLetter.kt.after | 9 ++++++ .../basic/stringTemplate/ValInObject.kt | 14 +++++++++ .../basic/stringTemplate/ValInObject.kt.after | 14 +++++++++ .../TopLevelValImportInStringTemplate-1.kt | 5 ++++ .../TopLevelValImportInStringTemplate-2.kt | 3 ++ ...TopLevelValImportInStringTemplate.kt.after | 7 +++++ .../BasicCompletionHandlerTestGenerated.java | 24 +++++++++++++++ .../CompletionMultifileHandlerTest.kt | 4 +++ 15 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt.after create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt.after create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt.after create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt create mode 100644 idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt.after create mode 100644 idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-1.kt create mode 100644 idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-2.kt create mode 100644 idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate.kt.after diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinPropertyInsertHandler.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinPropertyInsertHandler.kt index 9c14e7b5a56..4752195125f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinPropertyInsertHandler.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinPropertyInsertHandler.kt @@ -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) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt index 005b50c275b..b01b0f5814b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt @@ -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) + } } } } diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt new file mode 100644 index 00000000000..22b0fbbba65 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "$" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt.after b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt.after new file mode 100644 index 00000000000..01f13022e12 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalVal.kt.after @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "$vvv" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt new file mode 100644 index 00000000000..de59ccf6950 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "${}" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt.after b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt.after new file mode 100644 index 00000000000..10448898875 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/GlobalValInCurlyBraces.kt.after @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "${vvv}" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt b/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt new file mode 100644 index 00000000000..f9f840d571e --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "$word" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt.after b/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt.after new file mode 100644 index 00000000000..955aae82b1e --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/InsertCurlyBracesBeforeLetter.kt.after @@ -0,0 +1,9 @@ +package p + +val vvv = 1 + +fun foo() { + val s = "${vvv}word" +} + +// ELEMENT: vvv diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt b/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt new file mode 100644 index 00000000000..57027779554 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt @@ -0,0 +1,14 @@ +package p + +object OOO { + val vvv = 1 +} + +class C { + fun foo() { + "$v" + } +} + +// ELEMENT: vvv +// INVOCATION_COUNT: 2 diff --git a/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt.after b/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt.after new file mode 100644 index 00000000000..862c6d76a23 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/stringTemplate/ValInObject.kt.after @@ -0,0 +1,14 @@ +package p + +object OOO { + val vvv = 1 +} + +class C { + fun foo() { + "${OOO.vvv}" + } +} + +// ELEMENT: vvv +// INVOCATION_COUNT: 2 diff --git a/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-1.kt b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-1.kt new file mode 100644 index 00000000000..cde36a1312e --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-1.kt @@ -0,0 +1,5 @@ +package some + +fun other() { + val v = "$somePr" +} diff --git a/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-2.kt b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-2.kt new file mode 100644 index 00000000000..d8e3e859c44 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate-2.kt @@ -0,0 +1,3 @@ +package other + +val someProp: Int = 1 diff --git a/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate.kt.after b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate.kt.after new file mode 100644 index 00000000000..363d683ac66 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/TopLevelValImportInStringTemplate.kt.after @@ -0,0 +1,7 @@ +package some + +import other.someProp + +fun other() { + val v = "$someProp" +} diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index 1365870a580..4ac5ddd1645 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -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") diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt index 4d33fe6358c..a0061c14bae 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt @@ -49,6 +49,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() { doTest() } + fun testTopLevelValImportInStringTemplate() { + doTest() + } + fun testNoParenthesisInImports() { doTest() }