From 58058eef219050a2284b63f1da0438ac62c99d5d Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Thu, 26 Dec 2019 19:33:45 +0700 Subject: [PATCH] Formatter: support trailing comma in indexing suffix #KT-34744 --- .../idea/formatter/KotlinCommonBlock.kt | 62 ++-- .../TrailingCommaPostFormatProcessor.kt | 4 + idea/testData/formatter/ArrayAccess.after.kt | 6 +- .../indices/IndicesAccess.after.inv.kt | 232 ++++++++++++ .../indices/IndicesAccess.after.kt | 329 ++++++++++++++++++ .../trailingComma/indices/IndicesAccess.kt | 171 +++++++++ .../formatter/FormatterTestGenerated.java | 36 ++ 7 files changed, 803 insertions(+), 37 deletions(-) create mode 100644 idea/testData/formatter/trailingComma/indices/IndicesAccess.after.inv.kt create mode 100644 idea/testData/formatter/trailingComma/indices/IndicesAccess.after.kt create mode 100644 idea/testData/formatter/trailingComma/indices/IndicesAccess.kt diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt index 47b186b6f7e..e4704607bc0 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt @@ -595,11 +595,7 @@ abstract class KotlinCommonBlock( wrapSetting, VALUE_ARGUMENT, node.withTrailingComma, - additionalWrap = trailingCommaWrappingStrategy( - LPAR, - RPAR, - thisOrPrevIsMultiLineElement(VALUE_ARGUMENT, COMMA, LPAR, RPAR) - ) + additionalWrap = trailingCommaWrappingStrategyWithMultiLineCheck(LPAR, RPAR) ) } @@ -611,21 +607,20 @@ abstract class KotlinCommonBlock( commonSettings.METHOD_PARAMETERS_WRAP, VALUE_PARAMETER, node.withTrailingComma, - additionalWrap = trailingCommaWrappingStrategy( - LPAR, - RPAR, - thisOrPrevIsMultiLineElement(VALUE_PARAMETER, COMMA, LPAR, RPAR) - ) + additionalWrap = trailingCommaWrappingStrategyWithMultiLineCheck(LPAR, RPAR) ) } + elementType === INDICES -> return { childElement -> + trailingCommaWrappingStrategyWithMultiLineCheck(LBRACKET, RBRACKET)(childElement) + } + elementType === SUPER_TYPE_LIST -> { val wrap = Wrap.createWrap(commonSettings.EXTENDS_LIST_WRAP, false) return { childElement -> if (childElement.psi is KtSuperTypeListEntry) wrap else null } } - elementType === CLASS_BODY -> - return getWrappingStrategyForItemList(commonSettings.ENUM_CONSTANTS_WRAP, ENUM_ENTRY) + elementType === CLASS_BODY -> return getWrappingStrategyForItemList(commonSettings.ENUM_CONSTANTS_WRAP, ENUM_ENTRY) elementType === MODIFIER_LIST -> { when (val parent = node.treeParent.psi) { @@ -723,19 +718,19 @@ abstract class KotlinCommonBlock( else -> false } - private fun ASTNode.getSiblingNodeInSequence( + private fun ASTNode.notDelimiterSiblingNodeInSequence( forward: Boolean, - withBreakElement: Boolean, - breakType: IElementType, + delimiterType: IElementType, barrier: IElementType ): ASTNode? { var sibling: ASTNode? = null for (element in siblings(forward).filter { it.elementType != WHITE_SPACE }.takeWhile { it.elementType != barrier }) { - if (withBreakElement) { + val elementType = element.elementType + if (!forward) { sibling = element - if (element.elementType == breakType) break + if (elementType != delimiterType && elementType !in COMMENTS) break } else { - if (element.elementType == breakType) break + if (elementType !in COMMENTS) break sibling = element } } @@ -744,34 +739,33 @@ abstract class KotlinCommonBlock( } private fun thisOrPrevIsMultiLineElement( - type: IElementType, delimiterType: IElementType, leftBarrier: IElementType, rightBarrier: IElementType ) = fun(childElement: ASTNode): Boolean { - if (childElement.elementType != type) return false + when (childElement.elementType) { + leftBarrier, + rightBarrier, + delimiterType, + in WHITE_SPACE_OR_COMMENT_BIT_SET + -> return false + } + val psi = childElement.psi ?: return false if (psi.isMultiline()) return true - val startOffset = childElement.getSiblingNodeInSequence( - forward = false, - withBreakElement = true, - breakType = type, - barrier = leftBarrier - )?.startOffset ?: psi.startOffset - - val endOffset = childElement.getSiblingNodeInSequence( - forward = true, - withBreakElement = false, - breakType = delimiterType, - barrier = rightBarrier - )?.psi?.endOffset ?: psi.endOffset - + val startOffset = childElement.notDelimiterSiblingNodeInSequence(false, delimiterType, leftBarrier)?.startOffset ?: psi.startOffset + val endOffset = childElement.notDelimiterSiblingNodeInSequence(true, delimiterType, rightBarrier)?.psi?.endOffset ?: psi.endOffset val treeParent = childElement.treeParent val textRange = TextRange.create(startOffset, endOffset).shiftLeft(treeParent.startOffset) return StringUtil.containsLineBreak(textRange.subSequence(treeParent.text)) } + private fun trailingCommaWrappingStrategyWithMultiLineCheck( + leftAnchor: IElementType, + rightAnchor: IElementType + ) = trailingCommaWrappingStrategy(leftAnchor, rightAnchor, thisOrPrevIsMultiLineElement(COMMA, leftAnchor, rightAnchor)) + private fun trailingCommaWrappingStrategy( leftAnchor: IElementType, rightAnchor: IElementType, diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt index 846fd18a542..7a3880ad5f7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt @@ -42,6 +42,10 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi super.visitValueArgumentList(list) } + override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) = processCommaOwnerIfInRange(expression.indicesNode) { + super.visitArrayAccessExpression(expression) + } + private fun processCommaOwnerIfInRange(element: KtElement, preHook: () -> Unit = {}) { if (myPostProcessor.isElementPartlyInRange(element)) { preHook() diff --git a/idea/testData/formatter/ArrayAccess.after.kt b/idea/testData/formatter/ArrayAccess.after.kt index 9f74d754037..6cb279cded6 100644 --- a/idea/testData/formatter/ArrayAccess.after.kt +++ b/idea/testData/formatter/ArrayAccess.after.kt @@ -5,19 +5,19 @@ fun test() { a[1, 2] a[ - 1, 2 + 1, 2, ] a[ - 1, 2 + 1, 2, ] a[ - 1, 2 + 1, 2, ] diff --git a/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.inv.kt b/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.inv.kt new file mode 100644 index 00000000000..c8818aad243 --- /dev/null +++ b/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.inv.kt @@ -0,0 +1,232 @@ +// SET_TRUE: ALLOW_TRAILING_COMMA + +fun foo() { + testtest[foofoo, foofoo, foofoo, + foofoo, bar] + + testtest[ + foofoo, foofoo, foofoo, foofoo, bar + ] + + testtest[foofoo, foofoo, foofoo, foofoo, bar + ] + + testtest[foofoo, foofoo, foofoo, foofoo, + bar + ] + + testtest[foofoo + ] + + testtest[ + foofoo] + + testtest[ + foofoo + ] + + testtest[ + foofoo, + ] + + testtest[foofoo, testtest[testtest[foofoo]]] + + testtest[ + foofoo, fososos, testtest[testtest[foofoo]], + ] + + testtest[foofoo, testtest[testtest[ + foofoo, + ]], testsa] + + testtest[foofoo, seee, testtest[testtest[ + foofoo, + ]], testsa] + + useCallable["A", Callable { println["Hello world"] }] + + useCallable["B", "C", Callable { + println["Hello world"] + }, Callable { + println["Hello world"] + }] + + useCallable[Callable { println["Hello world"] }] + + useCallable[ + Callable { println["Hello world"] }, + ] + + useCallable[Callable { println["Hello world"] } + ] + + useCallable[Callable { println["Hello world"] }]{ + + } + + useCallable[ + Callable { println["Hello world"] }] + + useCallable["A", { println["Hello world"] }] + + useCallable["B", "C", { + println["Hello world"] + }, { + println["Hello world"] + }] + + useCallable[{ println["Hello world"] }] + + useCallable[ + { println["Hello world"] }, + ] + + useCallable[{ println["Hello world"] } + ] + + useCallable[ + { println["Hello world"] }] + + useCallable["A", object : Callable { + override fun call() { + println["Hello world"] + } + }] + + useCallable["A", object : Callable { + override fun call() { + println["Hello world"] + } + }] + + useCallable["B", "C", object : Callable { + override fun call() { + println["Hello world"] + } + }, foo[ + 0, + ]] + + useCallable[object : Callable { + override fun call() { + println["Hello world"] + } + }] + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[object : Callable { + override fun call() { + println["Hello world"] + } + } + ] + + useCallable[object : Callable { + override fun call() { + println["Hello world"] + } + }] { + + } + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }] + + testtest[ + foofoo, foofoo, foofoo, foofoo, + bar /* + */, /* */ foo + ] + + testtest[/* + */foofoo, foofoo, foofoo, /* + + */ + foofoo, bar] + + testtest[foofoo, foofoo, foofoo, foofoo, bar/* + */] + + testtest[foofoo, foofoo, foofoo, foofoo, bar // awdawda + ] + + testtest[foofoo, foofoo, foofoo, foofoo, /* + + */ + bar + ] + + testtest[foofoo // fd + ] + + testtest[ /**/ + foofoo + ] + + testtest[ + foofoo,/**/ + ] + + testtest[foofoo, foofoo, foofoo, foofoo/* + */, /* */ bar + ] + + testtest[foofoo // fd + ] + + testtest[ /**/ + foofoo + ] + + testtest[ + foofoo,/**/ + ] + + testtest[ + foofoo, fososos,/* + */ + testtest[testtest[foofoo]], + ] + + testtest[foofoo, testtest[testtest[ + foofoo, + ]], /**/testsa] + + testtest[foofoo, testtest[testtest[ + foofoo, + ]]/* */, /**/testsa] + + testtest[foofoo, testtest[testtest[ + foofoo, + ]]/* + */, testsa] + + testtest[foofoo, seee, testtest[testtest[ + foofoo, + ]], /**/testsa] + + testtest[foofoo, seee, testtest[testtest[ + foofoo, + ]], /* + */testsa] + + useCallable["B", "C", Callable { + println["Hello world"] + }, /* */ Callable { + println["Hello world"] + }] + + useCallable[Callable { println["Hello world"] } // ffd + ] +} \ No newline at end of file diff --git a/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.kt b/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.kt new file mode 100644 index 00000000000..7dc62fc9db3 --- /dev/null +++ b/idea/testData/formatter/trailingComma/indices/IndicesAccess.after.kt @@ -0,0 +1,329 @@ +// SET_TRUE: ALLOW_TRAILING_COMMA + +fun foo() { + testtest[ + foofoo, foofoo, foofoo, + foofoo, bar, + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, bar, + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, bar, + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, + bar, + ] + + testtest[ + foofoo, + ] + + testtest[ + foofoo, + ] + + testtest[ + foofoo, + ] + + testtest[ + foofoo, + ] + + testtest[foofoo, testtest[testtest[foofoo]]] + + testtest[ + foofoo, fososos, testtest[testtest[foofoo]], + ] + + testtest[ + foofoo, + testtest[ + testtest[ + foofoo, + ], + ], + testsa, + ] + + testtest[ + foofoo, seee, + testtest[ + testtest[ + foofoo, + ], + ], + testsa, + ] + + useCallable["A", Callable { println["Hello world"] }] + + useCallable[ + "B", "C", + Callable { + println["Hello world"] + }, + Callable { + println["Hello world"] + }, + ] + + useCallable[Callable { println["Hello world"] }] + + useCallable[ + Callable { println["Hello world"] }, + ] + + useCallable[ + Callable { println["Hello world"] }, + ] + + useCallable[Callable { println["Hello world"] }]{ + + } + + useCallable[ + Callable { println["Hello world"] }, + ] + + useCallable["A", { println["Hello world"] }] + + useCallable[ + "B", "C", + { + println["Hello world"] + }, + { + println["Hello world"] + }, + ] + + useCallable[{ println["Hello world"] }] + + useCallable[ + { println["Hello world"] }, + ] + + useCallable[ + { println["Hello world"] }, + ] + + useCallable[ + { println["Hello world"] }, + ] + + useCallable[ + "A", + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[ + "A", + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[ + "B", "C", + object : Callable { + override fun call() { + println["Hello world"] + } + }, + foo[ + 0, + ], + ] + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] { + + } + + useCallable[ + object : Callable { + override fun call() { + println["Hello world"] + } + }, + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, + bar, /* + */ /* */ + foo, + ] + + testtest[ +/* + */ + foofoo, foofoo, foofoo, /* + + */ + foofoo, bar, + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, + bar,/* + */ + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, bar, // awdawda + ] + + testtest[ + foofoo, foofoo, foofoo, foofoo, /* + + */ + bar, + ] + + testtest[ + foofoo, // fd + ] + + testtest[ + /**/ + foofoo, + ] + + testtest[ + foofoo,/**/ + ] + + testtest[ + foofoo, foofoo, foofoo, + foofoo,/* + */ /* */ + bar, + ] + + testtest[ + foofoo, // fd + ] + + testtest[ + /**/ + foofoo, + ] + + testtest[ + foofoo,/**/ + ] + + testtest[ + foofoo, fososos,/* + */ + testtest[testtest[foofoo]], + ] + + testtest[ + foofoo, + testtest[ + testtest[ + foofoo, + ], + ], /**/ + testsa, + ] + + testtest[ + foofoo, + testtest[ + testtest[ + foofoo, + ], + ],/* */ /**/ + testsa, + ] + + testtest[ + foofoo, + testtest[ + testtest[ + foofoo, + ], + ],/* + */ + testsa, + ] + + testtest[ + foofoo, seee, + testtest[ + testtest[ + foofoo, + ], + ], /**/ + testsa, + ] + + testtest[ + foofoo, seee, + testtest[ + testtest[ + foofoo, + ], + ], /* + */ + testsa, + ] + + useCallable[ + "B", "C", + Callable { + println["Hello world"] + }, /* */ + Callable { + println["Hello world"] + }, + ] + + useCallable[ + Callable { println["Hello world"] }, // ffd + ] +} \ No newline at end of file diff --git a/idea/testData/formatter/trailingComma/indices/IndicesAccess.kt b/idea/testData/formatter/trailingComma/indices/IndicesAccess.kt new file mode 100644 index 00000000000..5a2410b99c5 --- /dev/null +++ b/idea/testData/formatter/trailingComma/indices/IndicesAccess.kt @@ -0,0 +1,171 @@ +// SET_TRUE: ALLOW_TRAILING_COMMA + +fun foo() { + testtest[foofoo, foofoo, foofoo, + foofoo, bar] + + testtest[ + foofoo, foofoo, foofoo, foofoo, bar + ] + + testtest[foofoo, foofoo, foofoo, foofoo, bar + ] + + testtest[foofoo, foofoo, foofoo, foofoo, + bar + ] + + testtest[foofoo + ] + + testtest[ + foofoo] + + testtest[ + foofoo + ] + + testtest[foofoo,] + + testtest[foofoo, testtest[testtest[foofoo]]] + + testtest[foofoo, fososos, testtest[testtest[foofoo]],] + + testtest[foofoo, testtest[testtest[foofoo,]], testsa] + + testtest[foofoo, seee, testtest[testtest[foofoo,]], testsa] + + useCallable["A", Callable { println["Hello world"] }] + + useCallable["B", "C", Callable { + println["Hello world"] + }, Callable { + println["Hello world"] + }] + + useCallable[Callable { println["Hello world"] }] + + useCallable[Callable { println["Hello world"] },] + + useCallable[Callable { println["Hello world"] } + ] + + useCallable[Callable { println["Hello world"] }]{ + + } + + useCallable[ + Callable { println["Hello world"] }] + + useCallable["A", { println["Hello world"] }] + + useCallable["B", "C", { + println["Hello world"] + }, { + println["Hello world"] + }] + + useCallable[{ println["Hello world"] }] + + useCallable[{ println["Hello world"] },] + + useCallable[{ println["Hello world"] } + ] + + useCallable[ + { println["Hello world"] }] + + useCallable["A", object : Callable { override fun call() { println["Hello world"] } }] + + useCallable["A", object : Callable { + override fun call() { + println["Hello world"] + } + }] + + useCallable["B", "C", object : Callable { override fun call() { println["Hello world"] } }, foo[0,]] + + useCallable[object : Callable { override fun call() { println["Hello world"] } }] + + useCallable[object : Callable { override fun call() { println["Hello world"] } },] + + useCallable[object : Callable { override fun call() { println["Hello world"] } } + ] + + useCallable[object : Callable { override fun call() { println["Hello world"] } }] { + + } + + useCallable[ + object : Callable { override fun call() { println["Hello world"] } }] + + testtest[ + foofoo, foofoo, foofoo, foofoo, + bar /* + */, /* */ foo + ] + + testtest[/* + */foofoo, foofoo, foofoo, /* + + */ + foofoo, bar] + + testtest[foofoo, foofoo, foofoo, foofoo, bar/* + */] + + testtest[foofoo, foofoo, foofoo, foofoo, bar // awdawda + ] + + testtest[foofoo, foofoo, foofoo, foofoo, /* + + */ + bar + ] + + testtest[foofoo // fd + ] + + testtest[ /**/ + foofoo + ] + + testtest[foofoo,/**/] + + testtest[foofoo, foofoo, foofoo, foofoo/* + */ , /* */ bar + ] + + testtest[foofoo // fd + ] + + testtest[ /**/ + foofoo + ] + + testtest[foofoo,/**/] + + testtest[foofoo, fososos,/* + */ testtest[testtest[foofoo]],] + + testtest[foofoo, testtest[testtest[foofoo,]], /**/testsa] + + testtest[foofoo, testtest[testtest[foofoo,]]/* */ , /**/testsa] + + testtest[foofoo, testtest[testtest[foofoo,]]/* + */ ,testsa] + + testtest[foofoo, seee, testtest[testtest[foofoo,]], /**/testsa] + + testtest[foofoo, seee, testtest[testtest[foofoo,]], /* + */testsa] + + useCallable["B", "C", Callable { + println["Hello world"] + }, /* */ Callable { + println["Hello world"] + }] + + useCallable[Callable { println["Hello world"] } // ffd + ] +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java index d1ac74a4ba2..ec2255738cd 100644 --- a/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/formatter/FormatterTestGenerated.java @@ -1164,6 +1164,24 @@ public class FormatterTestGenerated extends AbstractFormatterTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true); } + @TestMetadata("idea/testData/formatter/trailingComma/indices") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Indices extends AbstractFormatterTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInIndices() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/indices"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true); + } + + @TestMetadata("IndicesAccess.after.kt") + public void testIndicesAccess() throws Exception { + runTest("idea/testData/formatter/trailingComma/indices/IndicesAccess.after.kt"); + } + } + @TestMetadata("idea/testData/formatter/trailingComma/valueArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -1608,6 +1626,24 @@ public class FormatterTestGenerated extends AbstractFormatterTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true); } + @TestMetadata("idea/testData/formatter/trailingComma/indices") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Indices extends AbstractFormatterTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestInverted, this, testDataFilePath); + } + + public void testAllFilesPresentInIndices() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/indices"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true); + } + + @TestMetadata("IndicesAccess.after.inv.kt") + public void testIndicesAccess() throws Exception { + runTest("idea/testData/formatter/trailingComma/indices/IndicesAccess.after.inv.kt"); + } + } + @TestMetadata("idea/testData/formatter/trailingComma/valueArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)