Formatter: support trailing comma in destruction declarations

#KT-34744
This commit is contained in:
Dmitry Gridin
2019-12-30 20:52:54 +07:00
parent 96a11707ca
commit b72da6c4fc
11 changed files with 700 additions and 18 deletions
@@ -642,6 +642,23 @@ abstract class KotlinCommonBlock(
}
}
elementType === DESTRUCTURING_DECLARATION -> {
nodePsi as KtDestructuringDeclaration
if (nodePsi.valOrVarKeyword == null) return defaultTrailingCommaWrappingStrategy(LPAR, RPAR)
else if (nodePsi.needTrailingComma(settings)) {
val check = thisOrPrevIsMultiLineElement(COMMA, LPAR, RPAR)
return block@{ childElement ->
val childElementType = childElement.elementType
if (childElementType === EQ) return@block null
createWrapAlwaysIf(
childElementType === RPAR ||
getSiblingWithoutWhitespaceAndComments(childElement)?.elementType === LPAR ||
getSiblingWithoutWhitespaceAndComments(childElement, true) != null && check(childElement)
)
}
}
}
elementType === INDICES -> return defaultTrailingCommaWrappingStrategy(LBRACKET, RBRACKET)
elementType === TYPE_PARAMETER_LIST -> return defaultTrailingCommaWrappingStrategy(LT, GT)
@@ -13,12 +13,11 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettings
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
/*
* ASTBlock.node is nullable, this extension was introduced to minimize changes
@@ -48,22 +47,38 @@ fun PsiElement.getLineCount(): Int {
fun PsiElement.isMultiline() = getLineCount() > 1
fun KtFunctionLiteral.needTrailingComma(settings: CodeStyleSettings): Boolean = valueParameterList?.trailingComma != null ||
settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA &&
run(fun(): Boolean {
val startOffset = valueParameterList?.startOffset ?: return false
val endOffset = arrow?.endOffset ?: return false
return containsLineBreakInThis(startOffset, endOffset)
})
fun KtFunctionLiteral.needTrailingComma(settings: CodeStyleSettings): Boolean = needTrailingComma(
settings = settings,
trailingComma = { valueParameterList?.trailingComma },
globalStartOffset = { valueParameterList?.startOffset },
globalEndOffset = { arrow?.endOffset }
)
fun KtWhenEntry.needTrailingComma(settings: CodeStyleSettings): Boolean = trailingComma != null ||
!isElse &&
settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA &&
parent.safeAs<KtWhenExpression>()?.leftParenthesis != null &&
run(fun(): Boolean {
val endOffset = arrow?.endOffset ?: return false
return containsLineBreakInThis(startOffset, endOffset)
})
fun KtWhenEntry.needTrailingComma(settings: CodeStyleSettings): Boolean = needTrailingComma(
settings = settings,
trailingComma = { trailingComma },
additionalCheck = { !isElse },
globalEndOffset = { arrow?.endOffset }
)
fun KtDestructuringDeclaration.needTrailingComma(settings: CodeStyleSettings): Boolean = needTrailingComma(
settings = settings,
trailingComma = { trailingComma },
globalStartOffset = { lPar?.startOffset },
globalEndOffset = { rPar?.endOffset }
)
fun <T : PsiElement> T.needTrailingComma(
settings: CodeStyleSettings,
trailingComma: T.() -> PsiElement?,
additionalCheck: () -> Boolean = { true },
globalStartOffset: T.() -> Int? = PsiElement::startOffset,
globalEndOffset: T.() -> Int? = PsiElement::endOffset
) = trailingComma() != null || settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA && additionalCheck() && run(fun(): Boolean {
val startOffset = globalStartOffset() ?: return false
val endOffset = globalEndOffset() ?: return false
return containsLineBreakInThis(startOffset, endOffset)
})
fun PsiElement.containsLineBreakInThis(globalStartOffset: Int, globalEndOffset: Int): Boolean {
val textRange = TextRange.create(globalStartOffset, globalEndOffset).shiftLeft(startOffset)
@@ -66,6 +66,11 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
super.visitWhenEntry(jetWhenEntry)
}
override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) =
processCommaOwnerIfInRange(destructuringDeclaration) {
super.visitDestructuringDeclaration(destructuringDeclaration)
}
private fun processCommaOwnerIfInRange(element: KtElement, preHook: () -> Unit = {}) = processIfInRange(element) {
preHook()
processCommaOwner(element)
@@ -97,6 +102,7 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
get() = when {
this is KtWhenEntry -> needTrailingComma(settings)
parent is KtFunctionLiteral -> parent.cast<KtFunctionLiteral>().needTrailingComma(settings)
this is KtDestructuringDeclaration -> needTrailingComma(settings)
else -> settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA && isMultiline()
}
@@ -191,5 +197,6 @@ private val PsiElement.lastCommaOwnerOrComma: PsiElement?
private val PsiElement.lastSignificantChild: PsiElement?
get() = when (this) {
is KtWhenEntry -> arrow
is KtDestructuringDeclaration -> rPar
else -> lastChild
}
+1 -1
View File
@@ -10,6 +10,6 @@ fun test() {
items,
shippingMethods,
addresses,
preferredAddressId
preferredAddressId,
) = argument
}
@@ -0,0 +1,102 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, y,
), /* FIXME: The result should converge in one iteration */ z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x,
y),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x,
y),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, // adw
y,
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, /* val x: (Pair<Int, Int>, Int) -> Unit = { (x, y), z, ->
println(x)
}*/
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y/**/), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y/*
*/),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(/**/x /**/ /*
*/, // awdawd
y/*
*/),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(/**/x /**/ /*
*/, // awdawd
y/*
*/),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, y,
),
z,
->
println(x)
}
@@ -0,0 +1,114 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, y,
), /* FIXME: The result should converge in one iteration */ z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x,
y,
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x,
y,
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, // adw
y,
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, /* val x: (Pair<Int, Int>, Int) -> Unit = { (x, y), z, ->
println(x)
}*/
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(x, y/**/), z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x,
y,/*
*/
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
/**/
x, /**/ /*
*/ // awdawd
y,/*
*/
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
/**/
x, /**/ /*
*/ // awdawd
y,/*
*/
),
z,
->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(
x, y,
),
z,
->
println(x)
}
@@ -0,0 +1,66 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y,), /* FIXME: The result should converge in one iteration */ z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x,
y), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (
x,
y), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (
x // adw
,y,), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x /* val x: (Pair<Int, Int>, Int) -> Unit = { (x, y), z, ->
println(x)
}*/,), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y/**/), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y/*
*/), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (/**/x /**/ /*
*/, // awdawd
y/*
*/), z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = {
(/**/x /**/ /*
*/, // awdawd
y/*
*/),
z, ->
println(x)
}
val x: (Pair<Int, Int>, Int) -> Unit = { (x, y,),
z, ->
println(x)
}
@@ -0,0 +1,105 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
fun test() {
val (a, b) = 1 to 2
val (a, b) = 1 to
2
val (a, b) = 1
to
2
val (
a, b,
) = 1 to 2
val (
a,
) =
b
val (
a,
) = b
val (a, b
) = 1 to 2
val (a,
b) = 1 to 2
val (
a, b
) = 1 to 2
val (
a, b,
) = 1 to 2
val (a, b, c,
d, f
) = 1 to 2
val (
a, b, c,
d, f,
) = 1 to 2
val (a, b/**/) = 1 to 2
val (a, /**/b/**/) /**/ =/**/ 1 to
2
val (a,/**/ b) = 1
to
2
val (
a, b,/**/
) = 1 to 2
val (
a,/**/ b,/**//**/
) = 1 to 2
val (
a,/**//**/
) =
b
val (
a,
) = b
val (a, b/**/
) = 1 to 2
val (a, b// awd
) = 1 to 2
val (a,/**/ b /**/ // awd
) = 1 to 2
val (a, // ad
/**/b) = 1 to 2
val (
a, b // fe
/**/)/**/ = 1 to 2
val (
a, b, // awd
) = 1 to 2
val (a, b, c,
d, f // awd
/*
*/) = 1 to 2
val (
a, b, c,
d, f, // awd
) = 1 to 2
}
@@ -0,0 +1,120 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
fun test() {
val (a, b) = 1 to 2
val (a, b) = 1 to
2
val (a, b) = 1
to
2
val (
a, b,
) = 1 to 2
val (
a,
) =
b
val (
a,
) = b
val (
a, b,
) = 1 to 2
val (
a,
b,
) = 1 to 2
val (
a, b,
) = 1 to 2
val (
a, b,
) = 1 to 2
val (
a, b, c,
d, f,
) = 1 to 2
val (
a, b, c,
d, f,
) = 1 to 2
val (a, b/**/) = 1 to 2
val (a, /**/b/**/) /**/ =/**/ 1 to
2
val (a,/**/ b) = 1
to
2
val (
a, b,/**/
) = 1 to 2
val (
a,/**/ b,/**//**/
) = 1 to 2
val (
a,/**//**/
) =
b
val (
a,
) = b
val (
a, b,/**/
) = 1 to 2
val (
a, b,// awd
) = 1 to 2
val (
a,/**/ b, /**/ // awd
) = 1 to 2
val (
a, // ad
/**/
b,
) = 1 to 2
val (
a,
b, // fe
/**/
)/**/ = 1 to 2
val (
a, b, // awd
) = 1 to 2
val (
a, b, c,
d,
f, // awd
/*
*/
) = 1 to 2
val (
a, b, c,
d, f, // awd
) = 1 to 2
}
@@ -0,0 +1,90 @@
// SET_TRUE: ALLOW_TRAILING_COMMA
fun test() {
val (a, b) = 1 to 2
val (a, b) = 1 to
2
val (a, b) = 1
to
2
val (a, b,) = 1 to 2
val (a,) =
b
val (a,) = b
val (a, b
) = 1 to 2
val (a,
b) = 1 to 2
val (
a, b
) = 1 to 2
val (
a, b,
) = 1 to 2
val (a, b, c,
d, f
) = 1 to 2
val (a, b, c,
d, f,
) = 1 to 2
val (a, b/**/) = 1 to 2
val (a, /**/b/**/) /**/=/**/ 1 to
2
val (a,/**/ b) = 1
to
2
val (a, b/**/,) = 1 to 2
val (a/**/, b/**/,/**/) = 1 to 2
val (a/**/,/**/) =
b
val (a,) = b
val (a, b/**/
) = 1 to 2
val (a, b// awd
) = 1 to 2
val (a,/**/ b /**/ // awd
) = 1 to 2
val (a, // ad
/**/b) = 1 to 2
val (
a, b // fe
/**/)/**/ = 1 to 2
val (
a, b, // awd
) = 1 to 2
val (a, b, c,
d, f // awd
/*
*/) = 1 to 2
val (a, b, c,
d, f // awd
,
) = 1 to 2
}
@@ -1182,6 +1182,29 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
}
}
@TestMetadata("idea/testData/formatter/trailingComma/destructionDeclaration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DestructionDeclaration extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDestructionDeclaration() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/destructionDeclaration"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("DestructionDeclarationsInLambda.after.kt")
public void testDestructionDeclarationsInLambda() throws Exception {
runTest("idea/testData/formatter/trailingComma/destructionDeclaration/DestructionDeclarationsInLambda.after.kt");
}
@TestMetadata("MultiVariableDeclaration.after.kt")
public void testMultiVariableDeclaration() throws Exception {
runTest("idea/testData/formatter/trailingComma/destructionDeclaration/MultiVariableDeclaration.after.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma/indices")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1739,6 +1762,29 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
}
}
@TestMetadata("idea/testData/formatter/trailingComma/destructionDeclaration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DestructionDeclaration extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestInverted, this, testDataFilePath);
}
public void testAllFilesPresentInDestructionDeclaration() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/destructionDeclaration"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true);
}
@TestMetadata("DestructionDeclarationsInLambda.after.inv.kt")
public void testDestructionDeclarationsInLambda() throws Exception {
runTest("idea/testData/formatter/trailingComma/destructionDeclaration/DestructionDeclarationsInLambda.after.inv.kt");
}
@TestMetadata("MultiVariableDeclaration.after.inv.kt")
public void testMultiVariableDeclaration() throws Exception {
runTest("idea/testData/formatter/trailingComma/destructionDeclaration/MultiVariableDeclaration.after.inv.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma/indices")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)