Formatter: support trailing comma in indexing suffix

#KT-34744
This commit is contained in:
Dmitry Gridin
2019-12-26 19:33:45 +07:00
parent c195cd46f2
commit 58058eef21
7 changed files with 803 additions and 37 deletions
@@ -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,
@@ -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()
+3 -3
View File
@@ -5,19 +5,19 @@ fun test() {
a[1, 2]
a[
1, 2
1, 2,
]
a[
1, 2
1, 2,
]
a[
1, 2
1, 2,
]
@@ -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<Unit> {
override fun call() {
println["Hello world"]
}
}]
useCallable["A", object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}]
useCallable["B", "C", object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}, foo[
0,
]]
useCallable[object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}]
useCallable[
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}
]
useCallable[object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}] {
}
useCallable[
object : Callable<Unit> {
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
]
}
@@ -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<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[
"A",
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[
"B", "C",
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
foo[
0,
],
]
useCallable[
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
]
useCallable[
object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
},
] {
}
useCallable[
object : Callable<Unit> {
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
]
}
@@ -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<Unit> { override fun call() { println["Hello world"] } }]
useCallable["A", object : Callable<Unit> {
override fun call() {
println["Hello world"]
}
}]
useCallable["B", "C", object : Callable<Unit> { override fun call() { println["Hello world"] } }, foo[0,]]
useCallable[object : Callable<Unit> { override fun call() { println["Hello world"] } }]
useCallable[object : Callable<Unit> { override fun call() { println["Hello world"] } },]
useCallable[object : Callable<Unit> { override fun call() { println["Hello world"] } }
]
useCallable[object : Callable<Unit> { override fun call() { println["Hello world"] } }] {
}
useCallable[
object : Callable<Unit> { 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
]
}
@@ -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)