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() }