From 9d98240272e54e1c47731855efbade88ec77db54 Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Tue, 19 May 2020 17:06:43 +0700 Subject: [PATCH] Create KotlinLineIndentProvider and delegate it to formatter #KT-22211 --- ...ormanceTypingIndentationTestGenerated.java | 10 ++++ idea/resources/META-INF/idea.xml | 1 + .../formatter/KotlinLineIndentProvider.kt | 31 ++++++++++ .../ArgumentListNormalIndent.after.inv.kt | 7 +++ .../indentationOnNewline/Semicolon.after.kt | 4 ++ .../indentationOnNewline/Semicolon.kt | 3 + .../indentationOnNewline/Semicolon2.after.kt | 4 ++ .../indentationOnNewline/Semicolon2.kt | 3 + .../AbstractTypingIndentationTestBase.kt | 57 ++++++++++++++++--- .../TypingIndentationTestBaseGenerated.java | 15 +++++ 10 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt create mode 100644 idea/testData/indentationOnNewline/ArgumentListNormalIndent.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/Semicolon.after.kt create mode 100644 idea/testData/indentationOnNewline/Semicolon.kt create mode 100644 idea/testData/indentationOnNewline/Semicolon2.after.kt create mode 100644 idea/testData/indentationOnNewline/Semicolon2.kt diff --git a/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java b/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java index d77564e6a32..a88fbfba9da 100644 --- a/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java +++ b/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java @@ -268,6 +268,16 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman runTest("idea/testData/indentationOnNewline/ReturnContinue.kt"); } + @TestMetadata("Semicolon.kt") + public void testSemicolon() throws Exception { + runTest("idea/testData/indentationOnNewline/Semicolon.kt"); + } + + @TestMetadata("Semicolon2.kt") + public void testSemicolon2() throws Exception { + runTest("idea/testData/indentationOnNewline/Semicolon2.kt"); + } + @TestMetadata("SettingAlignMultilineParametersInCalls.kt") public void testSettingAlignMultilineParametersInCalls() throws Exception { runTest("idea/testData/indentationOnNewline/SettingAlignMultilineParametersInCalls.kt"); diff --git a/idea/resources/META-INF/idea.xml b/idea/resources/META-INF/idea.xml index 2ba09b4fecf..fbec2d6780a 100644 --- a/idea/resources/META-INF/idea.xml +++ b/idea/resources/META-INF/idea.xml @@ -15,6 +15,7 @@ factoryClass="org.jetbrains.kotlin.idea.structureView.KtClsStructureViewBuilderProvider"/> + diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt new file mode 100644 index 00000000000..8cad6b6cb56 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt @@ -0,0 +1,31 @@ +/* + * 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.formatter + +import com.intellij.lang.Language +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.codeStyle.lineIndent.LineIndentProvider +import com.intellij.psi.impl.source.codeStyle.lineIndent.FormatterBasedLineIndentProvider +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.idea.KotlinLanguage + +class KotlinLineIndentProvider : LineIndentProvider { + private val formatterBasedProvider = FormatterBasedLineIndentProvider() + + override fun getLineIndent(project: Project, editor: Editor, language: Language?, offset: Int): String? { + val lineIndent = formatterBasedProvider.getLineIndent(project, editor, language, offset) + return if (useFormatter) lineIndent else lineIndent + } + + override fun isSuitableFor(language: Language?): Boolean = language?.isKindOf(KotlinLanguage.INSTANCE) == true + + companion object { + @get:TestOnly + @set:TestOnly + internal var useFormatter: Boolean = false + } +} \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/ArgumentListNormalIndent.after.inv.kt b/idea/testData/indentationOnNewline/ArgumentListNormalIndent.after.inv.kt new file mode 100644 index 00000000000..1675648e32a --- /dev/null +++ b/idea/testData/indentationOnNewline/ArgumentListNormalIndent.after.inv.kt @@ -0,0 +1,7 @@ +fun test(i: Int) { + test( + + ) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ARGUMENT_LISTS diff --git a/idea/testData/indentationOnNewline/Semicolon.after.kt b/idea/testData/indentationOnNewline/Semicolon.after.kt new file mode 100644 index 00000000000..852f609c908 --- /dev/null +++ b/idea/testData/indentationOnNewline/Semicolon.after.kt @@ -0,0 +1,4 @@ +fun test() { + val a = 44; + +} diff --git a/idea/testData/indentationOnNewline/Semicolon.kt b/idea/testData/indentationOnNewline/Semicolon.kt new file mode 100644 index 00000000000..50a033d7c1e --- /dev/null +++ b/idea/testData/indentationOnNewline/Semicolon.kt @@ -0,0 +1,3 @@ +fun test() { + val a = 44; +} diff --git a/idea/testData/indentationOnNewline/Semicolon2.after.kt b/idea/testData/indentationOnNewline/Semicolon2.after.kt new file mode 100644 index 00000000000..7eeae566db9 --- /dev/null +++ b/idea/testData/indentationOnNewline/Semicolon2.after.kt @@ -0,0 +1,4 @@ +fun test() { + val a = 44; + val b = 42; +} diff --git a/idea/testData/indentationOnNewline/Semicolon2.kt b/idea/testData/indentationOnNewline/Semicolon2.kt new file mode 100644 index 00000000000..6b13f087b93 --- /dev/null +++ b/idea/testData/indentationOnNewline/Semicolon2.kt @@ -0,0 +1,3 @@ +fun test() { + val a = 44; val b = 42; +} diff --git a/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.kt b/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.kt index cbef735b5c5..d1718b880bb 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.kt +++ b/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.kt @@ -5,13 +5,22 @@ package org.jetbrains.kotlin.formatter import com.intellij.application.options.CodeStyle +import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.util.io.FileUtil +import com.intellij.psi.codeStyle.lineIndent.LineIndentProvider import com.intellij.testFramework.EditorTestUtil +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.formatter.KotlinLineIndentProvider import org.jetbrains.kotlin.idea.test.KotlinLightPlatformCodeInsightTestCase +import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.testFramework.runWriteAction +import org.junit.Assert import java.io.File abstract class AbstractTypingIndentationTestBase : KotlinLightPlatformCodeInsightTestCase() { + private val customLineIndentProvider: LineIndentProvider = KotlinLineIndentProvider() + fun doNewlineTestWithInvert(afterInvFilePath: String) { doNewlineTest(afterInvFilePath, true) } @@ -22,6 +31,11 @@ abstract class AbstractTypingIndentationTestBase : KotlinLightPlatformCodeInsigh val testFileExtension = afterFilePath.substring(afterFilePath.lastIndexOf(".")) val originFilePath = testFileName + testFileExtension val originalFileText = FileUtil.loadFile(File(originFilePath), true) + val withoutCustomLineIndentProvider = InTextDirectivesUtils.findStringWithPrefixes( + originalFileText, + "// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER" + ) != null + try { val configurator = FormatSettingsUtil.createConfigurator(originalFileText, CodeStyle.getSettings(project)) if (!inverted) { @@ -30,19 +44,48 @@ abstract class AbstractTypingIndentationTestBase : KotlinLightPlatformCodeInsigh configurator.configureInvertedSettings() } - doNewlineTest(originFilePath, afterFilePath) + doNewlineTest(originFilePath, afterFilePath, withoutCustomLineIndentProvider) } finally { CodeStyle.getSettings(project).clearCodeStyleSettings() } } - private fun doNewlineTest(beforeFilePath: String, afterFilePath: String) { + private fun doNewlineTest(beforeFilePath: String, afterFilePath: String, withoutCustomLineIndentProvider: Boolean) { + KotlinLineIndentProvider.useFormatter = true + typeAndCheck(beforeFilePath, afterFilePath, "with FormatterBasedLineIndentProvider") + KotlinLineIndentProvider.useFormatter = false + + if (!withoutCustomLineIndentProvider) { + typeAndCheck(beforeFilePath, afterFilePath, "with ${customLineIndentProvider.javaClass.simpleName}") + } + configureByFile(beforeFilePath) - type('\n') - val caretModel = editor.caretModel - val offset = caretModel.offset - val actualTextWithCaret = StringBuilder(editor.document.text).insert(offset, EditorTestUtil.CARET_TAG).toString() - KotlinTestUtils.assertEqualsToFile(File(afterFilePath), actualTextWithCaret) + assertCustomIndentExist(withoutCustomLineIndentProvider) + } + + private fun assertCustomIndentExist(withoutCustomLineIndentProvider: Boolean) { + val offset = editor.caretModel.offset + runWriteAction { + editor.document.insertString(offset, "\n") + } + + val customIndent = customLineIndentProvider.getLineIndent(project, editor, KotlinLanguage.INSTANCE, offset + 1) + val condition = customIndent == null + Assert.assertTrue( + "${if (withoutCustomLineIndentProvider) "Remove" else "Add"} \"// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER\" or fix ${customLineIndentProvider.javaClass.simpleName}", + if (withoutCustomLineIndentProvider) condition else !condition + ) + } + + private fun typeAndCheck(beforeFilePath: String, afterFilePath: String, errorMessage: String) { + configureByFile(beforeFilePath) + executeAction(IdeActions.ACTION_EDITOR_ENTER) + val actualTextWithCaret = StringBuilder(editor.document.text).insert( + editor.caretModel.offset, + EditorTestUtil.CARET_TAG + ).toString() + + KotlinTestUtils.assertEqualsToFile(errorMessage, File(afterFilePath), actualTextWithCaret) } override fun getTestDataPath(): String = "" diff --git a/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java b/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java index 99266749f9b..abed44fd278 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java @@ -270,6 +270,16 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio runTest("idea/testData/indentationOnNewline/ReturnContinue.after.kt"); } + @TestMetadata("Semicolon.after.kt") + public void testSemicolon() throws Exception { + runTest("idea/testData/indentationOnNewline/Semicolon.after.kt"); + } + + @TestMetadata("Semicolon2.after.kt") + public void testSemicolon2() throws Exception { + runTest("idea/testData/indentationOnNewline/Semicolon2.after.kt"); + } + @TestMetadata("SettingAlignMultilineParametersInCalls.after.kt") public void testSettingAlignMultilineParametersInCalls() throws Exception { runTest("idea/testData/indentationOnNewline/SettingAlignMultilineParametersInCalls.after.kt"); @@ -336,6 +346,11 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true); } + @TestMetadata("ArgumentListNormalIndent.after.inv.kt") + public void testArgumentListNormalIndent() throws Exception { + runTest("idea/testData/indentationOnNewline/ArgumentListNormalIndent.after.inv.kt"); + } + @TestMetadata("AssignmentAfterEq.after.inv.kt") public void testAssignmentAfterEq() throws Exception { runTest("idea/testData/indentationOnNewline/AssignmentAfterEq.after.inv.kt");