From d44bf27ec3ab0c83bc7a9a67c7eb3635fffde8f4 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Mon, 10 Apr 2017 23:08:42 +0200 Subject: [PATCH] Completion of keywords (like `constructor`) doesn't insert parentheses/braces if they're already there #KT-15603 Fixed #KT-13524 Fixed --- .../jetbrains/kotlin/psi/psiUtil/psiUtils.kt | 6 ++++- .../idea/completion/handlers/handlerUtils.kt | 22 ++++++++++++++++--- .../handlers/keywords/ConstructorPrimary.kt | 5 +++++ .../keywords/ConstructorPrimary.kt.after | 5 +++++ .../handlers/keywords/IfParansOnNextLine.kt | 6 +++++ .../keywords/IfParansOnNextLine.kt.after | 6 +++++ ...KeywordCompletionHandlerTestGenerated.java | 12 ++++++++++ 7 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt create mode 100644 idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt.after create mode 100644 idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt create mode 100644 idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index bbc5c991091..3ff68abddf0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,6 +145,10 @@ fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(withItself: Boolean = return siblings(withItself = withItself).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() } +fun PsiElement.getNextSiblingIgnoringWhitespace(withItself: Boolean = false): PsiElement? { + return siblings(withItself = withItself).filter { it !is PsiWhiteSpace }.firstOrNull() +} + fun PsiElement.getPrevSiblingIgnoringWhitespaceAndComments(withItself: Boolean = false): PsiElement? { return siblings(withItself = withItself, forward = false).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() } 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 f5089e0492b..2b2e43d7f1b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import com.intellij.openapi.editor.Document import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.idea.completion.KeywordLookupObject import org.jetbrains.kotlin.idea.core.moveCaret @@ -33,6 +34,8 @@ 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 +import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace +import org.jetbrains.kotlin.psi.psiUtil.startOffset fun surroundWithBracesIfInStringTemplate(context: InsertionContext): Boolean { val startOffset = context.startOffset @@ -144,8 +147,21 @@ fun createKeywordConstructLookupElement( val beforeCaret = tailBeforeCaret.indentLinesAfterFirst(newIndent) val afterCaret = tailAfterCaret.indentLinesAfterFirst(newIndent) - insertionContext.document.insertString(offset, beforeCaret + afterCaret) - insertionContext.editor.moveCaret(offset + beforeCaret.length) + val element = insertionContext.file.findElementAt(offset) + + val sibling = when { + element !is PsiWhiteSpace -> element + element.textContains('\n') -> null + else -> element.getNextSiblingIgnoringWhitespace(true) + } + + if (sibling != null && beforeCaret.trimStart().startsWith(insertionContext.document.getText(TextRange.from(sibling.startOffset, 1)))) { + insertionContext.editor.moveCaret(sibling.startOffset + 1) + } + else { + insertionContext.document.insertString(offset, beforeCaret + afterCaret) + insertionContext.editor.moveCaret(offset + beforeCaret.length) + } } } } diff --git a/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt b/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt new file mode 100644 index 00000000000..aa563807e4b --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt @@ -0,0 +1,5 @@ +class C c() { + +} + +// ELEMENT: constructor \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt.after b/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt.after new file mode 100644 index 00000000000..ec6203cb2e6 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt.after @@ -0,0 +1,5 @@ +class C constructor() { + +} + +// ELEMENT: constructor \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt b/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt new file mode 100644 index 00000000000..60408b37ca9 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt @@ -0,0 +1,6 @@ +fun other(a: Any) { + i + (a as Int).let { println(a + 12) } +} + +// ELEMENT: if \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt.after b/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt.after new file mode 100644 index 00000000000..dd54a9f9f15 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt.after @@ -0,0 +1,6 @@ +fun other(a: Any) { + if () + (a as Int).let { println(a + 12) } +} + +// ELEMENT: if \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java index ca617fa8e7c..cd2099b83e2 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java @@ -66,6 +66,12 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple doTest(fileName); } + @TestMetadata("ConstructorPrimary.kt") + public void testConstructorPrimary() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/ConstructorPrimary.kt"); + doTest(fileName); + } + @TestMetadata("Do.kt") public void testDo() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/Do.kt"); @@ -114,6 +120,12 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple doTest(fileName); } + @TestMetadata("IfParansOnNextLine.kt") + public void testIfParansOnNextLine() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/IfParansOnNextLine.kt"); + doTest(fileName); + } + @TestMetadata("IfSpace.kt") public void testIfSpace() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/IfSpace.kt");