KT-31553 Complete Statement: Wrong auto-insertion of closing curly brace for a code block (#2378)
* Complete statement: wrap property initializer by run/when block #KT-31553 Fixed
This commit is contained in:
committed by
GitHub
parent
14bdcb1e26
commit
37ba9eccc4
@@ -82,6 +82,7 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractCommonDecompiled
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractCommonDecompiledTextTest
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractJsDecompiledTextFromJsMetadataTest
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractJvmDecompiledTextTest
|
||||
import org.jetbrains.kotlin.idea.editor.AbstractEnterAfterUnmatchedBraceHandlerTest
|
||||
import org.jetbrains.kotlin.idea.editor.AbstractMultiLineStringIndentTest
|
||||
import org.jetbrains.kotlin.idea.editor.backspaceHandler.AbstractBackspaceHandlerTest
|
||||
import org.jetbrains.kotlin.idea.editor.quickDoc.AbstractQuickDocProviderTest
|
||||
@@ -547,6 +548,10 @@ fun main(args: Array<String>) {
|
||||
model("editor/backspaceHandler")
|
||||
}
|
||||
|
||||
testClass<AbstractEnterAfterUnmatchedBraceHandlerTest> {
|
||||
model("editor/enterHandler/afterUnmatchedBrace")
|
||||
}
|
||||
|
||||
testClass<AbstractMultiLineStringIndentTest> {
|
||||
model("editor/enterHandler/multilineString")
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
id="KotlinEnterHandler" order="before EnterBetweenBracesHandler"/>
|
||||
<enterHandlerDelegate implementation="org.jetbrains.kotlin.idea.editor.KotlinMultilineStringEnterHandler"
|
||||
id="KotlinMultilineStringEnterHandler" order="before EnterBetweenBracesHandler"/>
|
||||
<enterHandlerDelegate implementation="org.jetbrains.kotlin.idea.editor.KotlinEnterAfterUnmatchedBraceHandler"
|
||||
id="KotlinEnterAfterUnmatchedBraceHandler" order="before afterUnmatchedBrace"/>
|
||||
<enterBetweenBracesDelegate language="kotlin"
|
||||
implementationClass="org.jetbrains.kotlin.idea.editor.EnterBetweenBracesAndBracketsNoCommitDelegate"/>
|
||||
<lang.smartEnterProcessor language="kotlin" implementationClass="org.jetbrains.kotlin.idea.editor.KotlinSmartEnterHandler"/>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.editor
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
class KotlinEnterAfterUnmatchedBraceHandler : EnterAfterUnmatchedBraceHandler() {
|
||||
override fun preprocessEnter(
|
||||
file: PsiFile,
|
||||
editor: Editor,
|
||||
caretOffsetRef: Ref<Int>,
|
||||
caretAdvance: Ref<Int>,
|
||||
dataContext: DataContext,
|
||||
originalHandler: EditorActionHandler?
|
||||
): EnterHandlerDelegate.Result {
|
||||
val caretOffset = caretOffsetRef.get() - 1
|
||||
val element = file.findElementAt(caretOffset)
|
||||
if (element?.node?.elementType == KtTokens.LBRACE) {
|
||||
return super.preprocessEnter(file, editor, caretOffsetRef, caretAdvance, dataContext, originalHandler)
|
||||
}
|
||||
if (element !is PsiWhiteSpace) {
|
||||
return EnterHandlerDelegate.Result.Continue
|
||||
}
|
||||
val prevElement = CodeInsightUtils.getElementAtOffsetIgnoreWhitespaceAfter(file, caretOffset)
|
||||
if (prevElement != null && prevElement.node.elementType == KtTokens.LBRACE) {
|
||||
return super.preprocessEnter(file, editor, Ref(prevElement.startOffset + 1), caretAdvance, dataContext, originalHandler)
|
||||
}
|
||||
return EnterHandlerDelegate.Result.Continue
|
||||
}
|
||||
|
||||
override fun getRBraceOffset(file: PsiFile, editor: Editor, caretOffset: Int): Int {
|
||||
val element = file.findElementAt(caretOffset - 1)
|
||||
val endOffset = when (val parent = element?.parent) {
|
||||
is KtFunctionLiteral -> {
|
||||
val call = parent.getStrictParentOfType<KtCallExpression>()
|
||||
if (call?.isDeclarationInitializer() == true) {
|
||||
(parent.parent as? KtLambdaExpression)?.bodyExpression?.statements?.firstOrNull()?.endOffset
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is KtWhenExpression -> {
|
||||
if (parent.isDeclarationInitializer()) {
|
||||
(parent.entries.firstOrNull()?.conditions?.firstOrNull() as? KtWhenConditionWithExpression)?.endOffset
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
return endOffset ?: super.getRBraceOffset(file, editor, caretOffset)
|
||||
}
|
||||
|
||||
private fun KtExpression.isDeclarationInitializer(): Boolean {
|
||||
return (parent as? KtDeclarationWithInitializer)?.initializer == this
|
||||
}
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
fun test(): Int = bar { <caret>foo()
|
||||
|
||||
fun foo() = 42
|
||||
|
||||
fun bar(f: () -> Int) = f()
|
||||
//-----
|
||||
// WITH_RUNTIME
|
||||
fun test(): Int = bar {
|
||||
<caret>foo()
|
||||
}
|
||||
|
||||
fun foo() = 42
|
||||
|
||||
fun bar(f: () -> Int) = f()
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
fun test() {
|
||||
val test = run { <caret>1
|
||||
}
|
||||
//-----
|
||||
// WITH_RUNTIME
|
||||
fun test() {
|
||||
val test = run {
|
||||
<caret>1
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
val test = run {<caret>foo()
|
||||
|
||||
fun foo(): Int {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
//-----
|
||||
// WITH_RUNTIME
|
||||
class Test {
|
||||
val test = run {
|
||||
<caret>foo()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
val test = run {<caret>foo()
|
||||
|
||||
fun foo() = 42
|
||||
//-----
|
||||
// WITH_RUNTIME
|
||||
val test = run {
|
||||
<caret>foo()
|
||||
}
|
||||
|
||||
fun foo() = 42
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// WITH_RUNTIME
|
||||
fun test(): Int = bar { f<caret>oo()
|
||||
|
||||
fun foo() = 42
|
||||
|
||||
fun bar(f: () -> Int) = f()
|
||||
//-----
|
||||
// WITH_RUNTIME
|
||||
fun test(): Int = bar { f
|
||||
<caret>oo()
|
||||
|
||||
fun foo() = 42
|
||||
|
||||
fun bar(f: () -> Int) = f()
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
fun test() {
|
||||
val test = when { <caret>1
|
||||
}
|
||||
//-----
|
||||
fun test() {
|
||||
val test = when {
|
||||
<caret>1
|
||||
}
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
class Test {
|
||||
val test = when {<caret>foo()
|
||||
|
||||
fun foo(): Int {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
//-----
|
||||
class Test {
|
||||
val test = when {
|
||||
<caret>foo()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
val test = when {<caret>foo()
|
||||
|
||||
fun foo() = 42
|
||||
//-----
|
||||
val test = when {
|
||||
<caret>foo()
|
||||
}
|
||||
|
||||
fun foo() = 42
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.editor
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.testFramework.EditorTestUtil
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.utils.rethrow
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
abstract class AbstractEnterAfterUnmatchedBraceHandlerTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
companion object {
|
||||
private const val FILE_SEPARATOR = "//-----"
|
||||
}
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
val multiFileText = FileUtil.loadFile(File(path), true)
|
||||
|
||||
val beforeFile = multiFileText.substringBefore(FILE_SEPARATOR).trim()
|
||||
val afterFile = multiFileText.substringAfter(FILE_SEPARATOR).trim()
|
||||
|
||||
myFixture.setCaresAboutInjection(false)
|
||||
myFixture.configureByText(KotlinFileType.INSTANCE, beforeFile)
|
||||
myFixture.type('\n')
|
||||
|
||||
val caretModel = myFixture.editor.caretModel
|
||||
val offset = caretModel.offset
|
||||
val actualTextWithCaret = StringBuilder(myFixture.editor.document.text).insert(offset, EditorTestUtil.CARET_TAG).toString()
|
||||
|
||||
if (afterFile != actualTextWithCaret) {
|
||||
KotlinTestUtils.assertEqualsToFile(File(path), "$beforeFile\n$FILE_SEPARATOR\n$actualTextWithCaret")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
if (isAllFilesPresentInTest()) return KotlinLightProjectDescriptor.INSTANCE
|
||||
return try {
|
||||
val fileText = FileUtil.loadFile(File(testDataPath, fileName()), true)
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(fileText, "WITH_RUNTIME")) {
|
||||
KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
} else {
|
||||
JAVA_LATEST
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw rethrow(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.editor;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/editor/enterHandler/afterUnmatchedBrace")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class EnterAfterUnmatchedBraceHandlerTestGenerated extends AbstractEnterAfterUnmatchedBraceHandlerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAfterUnmatchedBrace() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/editor/enterHandler/afterUnmatchedBrace"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentBeforeFunctionInitializer.kt")
|
||||
public void testLambdaArgumentBeforeFunctionInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/lambdaArgumentBeforeFunctionInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentBeforeLocalPropertyInitializer.kt")
|
||||
public void testLambdaArgumentBeforeLocalPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/lambdaArgumentBeforeLocalPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentBeforeMemberPropertyInitializer.kt")
|
||||
public void testLambdaArgumentBeforeMemberPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/lambdaArgumentBeforeMemberPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentBeforeTopLevelPropertyInitializer.kt")
|
||||
public void testLambdaArgumentBeforeTopLevelPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/lambdaArgumentBeforeTopLevelPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicableOnInitializer.kt")
|
||||
public void testNotApplicableOnInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/notApplicableOnInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenBeforeLocalPropertyInitializer.kt")
|
||||
public void testWhenBeforeLocalPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/whenBeforeLocalPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenBeforeMemberPropertyInitializer.kt")
|
||||
public void testWhenBeforeMemberPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/whenBeforeMemberPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenBeforeTopLevelPropertyInitializer.kt")
|
||||
public void testWhenBeforeTopLevelPropertyInitializer() throws Exception {
|
||||
runTest("idea/testData/editor/enterHandler/afterUnmatchedBrace/whenBeforeTopLevelPropertyInitializer.kt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user