From fd013517403136f6acee09c1dda995b027e621d1 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 3 Jan 2018 16:37:21 +0100 Subject: [PATCH] Implement display of code construct start for Kotlin #KT-20470 Fixed --- idea/src/META-INF/plugin.xml | 6 +++ .../kotlin/idea/KotlinPairMatcher.kt | 11 ++++- .../KotlinDeclarationRangeHandler.kt | 41 +++++++++++++++++++ .../idea/codeInsight/KotlinPairMatcherTest.kt | 41 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinDeclarationRangeHandler.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/KotlinPairMatcherTest.kt diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 95944843f96..16d9eadb1b4 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -774,6 +774,12 @@ + + + + org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPairMatcher.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinPairMatcher.kt index 50caf00e354..8ea3290589d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinPairMatcher.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinPairMatcher.kt @@ -5,11 +5,14 @@ package org.jetbrains.kotlin.idea +import com.intellij.codeInsight.hint.DeclarationRangeUtil import com.intellij.lang.BracePair import com.intellij.lang.PairedBraceMatcher import com.intellij.psi.PsiFile import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtClassBody class KotlinPairMatcher : PairedBraceMatcher { private val pairs = arrayOf( @@ -37,6 +40,12 @@ class KotlinPairMatcher : PairedBraceMatcher { } override fun getCodeConstructStart(file: PsiFile, openingBraceOffset: Int): Int { - return openingBraceOffset + val element = file.findElementAt(openingBraceOffset) + if (element == null || element is PsiFile) return openingBraceOffset + val parent = element.parent + return when (parent) { + is KtClassBody, is KtBlockExpression -> DeclarationRangeUtil.getDeclarationRange(parent.parent).startOffset + else -> openingBraceOffset + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinDeclarationRangeHandler.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinDeclarationRangeHandler.kt new file mode 100644 index 00000000000..69f298acbea --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinDeclarationRangeHandler.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.codeInsight + +import com.intellij.codeInsight.hint.DeclarationRangeHandler +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclarationWithBody +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +class KotlinClassDeclarationRangeHandler : DeclarationRangeHandler { + override fun getDeclarationRange(container: KtClassOrObject): TextRange { + val body = container.getBody() ?: return container.textRange + val prevSibling = body.getPrevSiblingIgnoringWhitespaceAndComments() + return TextRange( + container.getDeclarationKeyword()?.startOffset ?: container.startOffset, + prevSibling?.endOffset ?: body.startOffset + ) + } +} + +class KotlinFunDeclarationRangeHandler : DeclarationRangeHandler { + override fun getDeclarationRange(container: KtDeclarationWithBody): TextRange { + val body = container.bodyExpression ?: return container.textRange + val prevSibling = body.getPrevSiblingIgnoringWhitespaceAndComments() + return TextRange( + (container as? KtNamedFunction)?.funKeyword?.startOffset + ?: (container as? PsiNameIdentifierOwner)?.nameIdentifier?.startOffset + ?: container.modifierList?.startOffset + ?: container.startOffset, + prevSibling?.endOffset ?: body.startOffset + ) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/KotlinPairMatcherTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/KotlinPairMatcherTest.kt new file mode 100644 index 00000000000..e7f07d6987e --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/KotlinPairMatcherTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.codeInsight + +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.KotlinPairMatcher +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import java.lang.IllegalArgumentException + +class KotlinPairMatcherTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): KotlinLightProjectDescriptor = KotlinLightProjectDescriptor.INSTANCE + + private fun doTest(testData: String) { + var docText = testData + val startPos = docText.indexOf("") + if (startPos == -1) { + throw IllegalArgumentException(" marker not found in testdata") + } + docText = docText.replace("", "") + val bracePos = docText.indexOf("") + if (bracePos == -1) { + throw IllegalArgumentException(" marker not found in testdata") + } + docText = docText.replace("", "") + myFixture.configureByText(KotlinFileType.INSTANCE, docText) + val pos = KotlinPairMatcher().getCodeConstructStart(myFixture.file, bracePos) + assertEquals(startPos, pos) + } + + fun testClass() { + doTest("/* Doc comment */ class Foo : Bar, Baz { fun xyzzy() { } }") + } + + fun testFun() { + doTest("/* Doc comment */ fun xyzzy(x: Int, y: String): Any { }") + } +}