From f833b4fe632685f3a213ca12eebb4884fe8f8b53 Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Tue, 9 Jun 2020 12:35:00 +0700 Subject: [PATCH] LineIndentProvider: support elvis operator Part of #KT-22211 --- .../lineIndent/KotlinIndentationAdjuster.kt | 4 + .../KotlinLikeLangLineIndentProvider.kt | 21 +++-- ...ormanceTypingIndentationTestGenerated.java | 38 ++++++++++ .../formatter/KotlinLineIndentProvider.kt | 3 + .../elvis/AfterElvis.after.inv.kt | 6 ++ .../elvis/AfterElvis.after.kt | 6 ++ .../indentationOnNewline/elvis/AfterElvis.kt | 5 ++ .../elvis/AfterElvis2.after.inv.kt | 8 ++ .../elvis/AfterElvis2.after.kt | 8 ++ .../indentationOnNewline/elvis/AfterElvis2.kt | 7 ++ .../AfterElvisInBinaryExpression.after.inv.kt | 6 ++ .../AfterElvisInBinaryExpression.after.kt | 6 ++ .../elvis/AfterElvisInBinaryExpression.kt | 5 ++ .../elvis/BeforeElvis.after.inv.kt | 6 ++ .../elvis/BeforeElvis.after.kt | 6 ++ .../indentationOnNewline/elvis/BeforeElvis.kt | 5 ++ ...BeforeElvisInBinaryExpression.after.inv.kt | 6 ++ .../BeforeElvisInBinaryExpression.after.kt | 6 ++ .../elvis/BeforeElvisInBinaryExpression.kt | 5 ++ .../TypingIndentationTestBaseGenerated.java | 76 +++++++++++++++++++ 20 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis.after.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis2.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis2.after.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvis2.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.kt create mode 100644 idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvis.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvis.after.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvis.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.inv.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.kt create mode 100644 idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.kt diff --git a/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinIndentationAdjuster.kt b/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinIndentationAdjuster.kt index 5772afa6e17..656840b82aa 100644 --- a/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinIndentationAdjuster.kt +++ b/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinIndentationAdjuster.kt @@ -13,4 +13,8 @@ interface KotlinIndentationAdjuster { // ALIGN_MULTILINE_BINARY_OPERATION val alignWhenMultilineBinaryExpression: Boolean get() = false + + // CONTINUATION_INDENT_IN_ELVIS + val continuationIndentInElvis: Boolean + get() = false } \ No newline at end of file diff --git a/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinLikeLangLineIndentProvider.kt b/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinLikeLangLineIndentProvider.kt index d35a0491fef..fa53d17ec27 100644 --- a/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinLikeLangLineIndentProvider.kt +++ b/idea/line-indent-provider/src/org/jetbrains/kotlin/idea/formatter/lineIndent/KotlinLikeLangLineIndentProvider.kt @@ -68,6 +68,18 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider val before = currentPosition.beforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET) val after = currentPosition.afterOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET) when { + after.isAt(Quest) && after.after().isAt(Colon) -> { + val indent = if (settings.continuationIndentInElvis) + Indent.getContinuationIndent() + else + Indent.getNormalIndent() + + return factory.createIndentCalculator(indent, before.startOffset) + } + + before.isAt(Colon) && before.before().isAt(Quest) -> + return factory.createIndentCalculator(Indent.getNoneIndent(), before.startOffset) + before.isAt(TemplateEntryOpen) -> { val indent = if (!currentPosition.hasLineBreaksAfter(offset) && after.isAt(TemplateEntryClose)) Indent.getNoneIndent() @@ -77,10 +89,8 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider return factory.createIndentCalculator(indent, before.startOffset) } - before.isAtAnyOf(TryKeyword) || before.isFinallyKeyword() -> return factory.createIndentCalculator( - Indent.getNoneIndent(), - IndentCalculator.LINE_BEFORE, - ) + before.isAtAnyOf(TryKeyword) || before.isFinallyKeyword() -> + return factory.createIndentCalculator(Indent.getNoneIndent(), IndentCalculator.LINE_BEFORE) after.isAt(TemplateEntryClose) -> { val indent = if (currentPosition.hasEmptyLineAfter(offset)) Indent.getNormalIndent() else Indent.getNoneIndent() @@ -456,8 +466,7 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider private fun JavaLikeLangLineIndentProvider.IndentCalculatorFactory.createIndentCalculator( indent: Indent, baseLineOffset: Int, -): IndentCalculator = - createIndentCalculator(indent) { baseLineOffset } ?: error("Contract (null, _ -> null) is broken") +): IndentCalculator = createIndentCalculator(indent) { baseLineOffset } ?: error("Contract (null, _ -> null) is broken") private fun SemanticEditorPosition.textOfCurrentPosition(): String = if (isAtEnd) "" else chars.subSequence(startOffset, after().startOffset).toString() \ No newline at end of file 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 1b488860687..0de528a995a 100644 --- a/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java +++ b/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/PerformanceTypingIndentationTestGenerated.java @@ -903,6 +903,44 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman runTest("idea/testData/indentationOnNewline/WhileWithoutCondition.kt"); } + @TestMetadata("idea/testData/indentationOnNewline/elvis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Elvis extends AbstractPerformanceTypingIndentationTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doPerfTest, this, testDataFilePath); + } + + @TestMetadata("AfterElvis.kt") + public void testAfterElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis.kt"); + } + + @TestMetadata("AfterElvis2.kt") + public void testAfterElvis2() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis2.kt"); + } + + @TestMetadata("AfterElvisInBinaryExpression.kt") + public void testAfterElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.kt"); + } + + public void testAllFilesPresentInElvis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline/elvis"), Pattern.compile("^([^.]+)\\.(kt|kts)$"), null, true); + } + + @TestMetadata("BeforeElvis.kt") + public void testBeforeElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvis.kt"); + } + + @TestMetadata("BeforeElvisInBinaryExpression.kt") + public void testBeforeElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.kt"); + } + } + @TestMetadata("idea/testData/indentationOnNewline/script") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt index caba01ddc72..5f52e499332 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinLineIndentProvider.kt @@ -28,6 +28,9 @@ class KotlinLineIndentProvider : KotlinLikeLangLineIndentProvider() { override val alignWhenMultilineBinaryExpression: Boolean get() = settings.kotlinCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION + + override val continuationIndentInElvis: Boolean + get() = settings.kotlinCustomSettings.CONTINUATION_INDENT_IN_ELVIS } companion object { diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis.after.inv.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis.after.inv.kt new file mode 100644 index 00000000000..fd6ae8128fa --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis.after.inv.kt @@ -0,0 +1,6 @@ +fun test(some: Any?, error: Int) { + val test = some ?: + error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis.after.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis.after.kt new file mode 100644 index 00000000000..fd6ae8128fa --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis.after.kt @@ -0,0 +1,6 @@ +fun test(some: Any?, error: Int) { + val test = some ?: + error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis.kt new file mode 100644 index 00000000000..d5df44e84ae --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis.kt @@ -0,0 +1,5 @@ +fun test(some: Any?, error: Int) { + val test = some ?: error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.inv.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.inv.kt new file mode 100644 index 00000000000..e143a027d86 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.inv.kt @@ -0,0 +1,8 @@ +fun test(some: Any?, error: Int) { + some?.let { + println(some) + } ?: + println(error) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.kt new file mode 100644 index 00000000000..e143a027d86 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis2.after.kt @@ -0,0 +1,8 @@ +fun test(some: Any?, error: Int) { + some?.let { + println(some) + } ?: + println(error) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvis2.kt b/idea/testData/indentationOnNewline/elvis/AfterElvis2.kt new file mode 100644 index 00000000000..368643137b6 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvis2.kt @@ -0,0 +1,7 @@ +fun test(some: Any?, error: Int) { + some?.let { + println(some) + } ?: println(error) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.inv.kt b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.inv.kt new file mode 100644 index 00000000000..fc2be776e57 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.inv.kt @@ -0,0 +1,6 @@ +fun a() { + println(1 + null ?: + 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.kt b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.kt new file mode 100644 index 00000000000..fc2be776e57 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.kt @@ -0,0 +1,6 @@ +fun a() { + println(1 + null ?: + 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.kt b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.kt new file mode 100644 index 00000000000..e5401b0fcdf --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.kt @@ -0,0 +1,5 @@ +fun a() { + println(1 + null ?: 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.inv.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.inv.kt new file mode 100644 index 00000000000..d8010681465 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.inv.kt @@ -0,0 +1,6 @@ +fun test(some: Any?, error: Int) { + val test = some + ?: error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.kt new file mode 100644 index 00000000000..d92dac8561b --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvis.after.kt @@ -0,0 +1,6 @@ +fun test(some: Any?, error: Int) { + val test = some + ?: error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvis.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvis.kt new file mode 100644 index 00000000000..60e3e43ab5d --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvis.kt @@ -0,0 +1,5 @@ +fun test(some: Any?, error: Int) { + val test = some ?: error +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.inv.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.inv.kt new file mode 100644 index 00000000000..91432a2b471 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.inv.kt @@ -0,0 +1,6 @@ +fun a() { + println(1 + null + ?: 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.kt new file mode 100644 index 00000000000..97fbd9038c0 --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.kt @@ -0,0 +1,6 @@ +fun a() { + println(1 + null + ?: 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.kt b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.kt new file mode 100644 index 00000000000..19a166fc14a --- /dev/null +++ b/idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.kt @@ -0,0 +1,5 @@ +fun a() { + println(1 + null ?: 2) +} + +// SET_FALSE: CONTINUATION_INDENT_IN_ELVIS \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java b/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java index bc16660aba3..fd1ef626a0e 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/formatter/TypingIndentationTestBaseGenerated.java @@ -900,6 +900,44 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio runTest("idea/testData/indentationOnNewline/WhileWithoutCondition.after.kt"); } + @TestMetadata("idea/testData/indentationOnNewline/elvis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Elvis extends AbstractTypingIndentationTestBase { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doNewlineTest, this, testDataFilePath); + } + + @TestMetadata("AfterElvis.after.kt") + public void testAfterElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis.after.kt"); + } + + @TestMetadata("AfterElvis2.after.kt") + public void testAfterElvis2() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis2.after.kt"); + } + + @TestMetadata("AfterElvisInBinaryExpression.after.kt") + public void testAfterElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.kt"); + } + + public void testAllFilesPresentInElvis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline/elvis"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true); + } + + @TestMetadata("BeforeElvis.after.kt") + public void testBeforeElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvis.after.kt"); + } + + @TestMetadata("BeforeElvisInBinaryExpression.after.kt") + public void testBeforeElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.kt"); + } + } + @TestMetadata("idea/testData/indentationOnNewline/script") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -1076,6 +1114,44 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio runTest("idea/testData/indentationOnNewline/SettingAlignMultilineParametersInCalls.after.inv.kt"); } + @TestMetadata("idea/testData/indentationOnNewline/elvis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Elvis extends AbstractTypingIndentationTestBase { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doNewlineTestWithInvert, this, testDataFilePath); + } + + @TestMetadata("AfterElvis.after.inv.kt") + public void testAfterElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis.after.inv.kt"); + } + + @TestMetadata("AfterElvis2.after.inv.kt") + public void testAfterElvis2() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvis2.after.inv.kt"); + } + + @TestMetadata("AfterElvisInBinaryExpression.after.inv.kt") + public void testAfterElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/AfterElvisInBinaryExpression.after.inv.kt"); + } + + public void testAllFilesPresentInElvis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline/elvis"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true); + } + + @TestMetadata("BeforeElvis.after.inv.kt") + public void testBeforeElvis() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvis.after.inv.kt"); + } + + @TestMetadata("BeforeElvisInBinaryExpression.after.inv.kt") + public void testBeforeElvisInBinaryExpression() throws Exception { + runTest("idea/testData/indentationOnNewline/elvis/BeforeElvisInBinaryExpression.after.inv.kt"); + } + } + @TestMetadata("idea/testData/indentationOnNewline/script") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)