Formatter: support trailing comma in lambda value parameters & fix comments in KtParameter

#KT-34744
This commit is contained in:
Dmitry Gridin
2019-12-27 19:32:08 +07:00
parent d013fc2234
commit 96a11707ca
21 changed files with 468 additions and 50 deletions
@@ -609,6 +609,7 @@ abstract class KotlinCommonBlock(
node.withTrailingComma,
additionalWrap = trailingCommaWrappingStrategyWithMultiLineCheck(LPAR, RPAR)
)
FUNCTION_TYPE -> return defaultTrailingCommaWrappingStrategy(LPAR, RPAR)
FUNCTION_LITERAL -> {
if (nodePsi.parent?.safeAs<KtFunctionLiteral>()?.needTrailingComma(settings) == true) {
val check = thisOrPrevIsMultiLineElement(COMMA, LBRACE /* not necessary */, ARROW /* not necessary */)
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.idea.formatter
import com.intellij.lang.ASTNode
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor
@@ -81,8 +83,9 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
if (elementType === KtTokens.COMMA || parent.needComma) {
// add a missing comma
if (elementType !== KtTokens.COMMA) {
changePsi(parent) { element, factory ->
element.addAfter(factory.createComma(), lastElement)
changePsi(parent) { factory ->
lastElement.addCommaAfter(factory)
true
}
}
@@ -97,31 +100,31 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
else -> settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA && isMultiline()
}
private fun changePsi(element: KtElement, update: (KtElement, KtPsiFactory) -> Unit) {
private fun changePsi(element: KtElement, update: (KtPsiFactory) -> Boolean) {
val oldLength = element.textLength
update(element, KtPsiFactory(element))
val result = CodeStyleManager.getInstance(element.project).reformat(element)
myPostProcessor.updateResultRange(oldLength, result.textLength)
if (update(KtPsiFactory(element))) {
val result = CodeStyleManager.getInstance(element.project).reformat(element)
myPostProcessor.updateResultRange(oldLength, result.textLength)
}
}
private fun correctCommaPosition(parent: KtElement) {
val invalidElements = parent.firstChild?.siblings(withItself = false)?.mapNotNull {
if (it !is ASTNode || it.elementType != KtTokens.COMMA) return@mapNotNull null
val prevWithComment = it.getPrevSiblingIgnoringWhitespace(false)
val prevWithoutComment = it.getPrevSiblingIgnoringWhitespaceAndComments(false)
if (prevWithoutComment?.equals(prevWithComment) == false) {
it.createSmartPointer() to prevWithoutComment.createSmartPointer()
} else
null
if (it.safeAs<ASTNode>()?.elementType != KtTokens.COMMA) return@mapNotNull null
it.createSmartPointer()
}?.toList() ?: return
if (invalidElements.isNotEmpty()) {
changePsi(parent) { element, factory ->
for ((pointToComma, pointToElement) in invalidElements) {
element.addAfter(factory.createComma(), pointToElement.element)
pointToComma.element?.delete()
changePsi(parent) { factory ->
var change = false
for (pointerToComma in invalidElements) {
pointerToComma.element?.let {
if (correctComma(it, factory)) {
change = true
}
}
}
change
}
}
}
@@ -146,6 +149,33 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
}
}
private fun PsiElement.addCommaAfter(factory: KtPsiFactory) {
val comma = factory.createComma()
parent.addAfter(comma, this)
}
private fun correctComma(comma: PsiElement, factory: KtPsiFactory): Boolean {
val prevWithComment = comma.getPrevSiblingIgnoringWhitespace(false)
val prevWithoutComment = comma.getPrevSiblingIgnoringWhitespaceAndComments(false)
return when {
prevWithoutComment?.equals(prevWithComment) == false -> {
prevWithoutComment.addCommaAfter(factory)
comma.delete()
true
}
prevWithoutComment is KtParameter -> {
val check = { element: PsiElement -> element is PsiWhiteSpace || element is PsiComment }
val lastElement = prevWithoutComment.lastChild?.takeIf(check) ?: return false
val firstElement = lastElement.siblings(forward = false, withItself = true).takeWhile(check).last()
comma.parent.addRangeAfter(firstElement, lastElement, comma)
prevWithoutComment.deleteChildRange(firstElement, lastElement)
true
}
else ->
false
}
}
private val PsiElement.lastCommaOwnerOrComma: PsiElement?
get() {
val lastChild = lastSignificantChild ?: return null
@@ -239,4 +239,42 @@ class L<
x : String, y : String, z : String
>
class C<
x : Int // adad
>
class G<
x : String, y : String, /* */ z : String // adad
>
class G<
x : String,
y : String,
/* */ /* */
z : String, /**/
>()
class H<
x : String, /*
*/
y : String,
z : String, /*
*/
>
class J<
x : String, y : String,
z : String, /*
*/ /**/
>
class K<
x : String, y : String,
z : String, // aw
>
class L<
x : String, y : String, z : String //awd
>
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -247,4 +247,43 @@ class L<
x : String, y : String, z : String,
>
class C<
x : Int, // adad
>
class G<
x : String, y : String, /* */
z : String, // adad
>
class G<
x : String,
y : String,
/* */ /* */
z : String, /**/
>()
class H<
x : String, /*
*/
y : String,
z : String, /*
*/
>
class J<
x : String, y : String,
z : String, /*
*/ /**/
>
class K<
x : String, y : String,
z : String, // aw
>
class L<
x : String, y : String, z : String, //awd
>
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -226,4 +226,38 @@ class L<
x: String, y: String, z: String
>
class C<
x: Int // adad
>
class G<
x: String, y: String
, /* */ z: String // adad
>
class G<
x: String, y: String
/* */, /* */ z: String /**/,
>()
class H<
x: String, /*
*/ y: String,
z: String /*
*/ ,>
class J<
x: String, y: String , z: String /*
*/
, /**/ >
class K<
x: String, y: String,
z: String // aw
, >
class L<
x: String, y: String, z: String //awd
>
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,89 @@
fun main() {
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>> // trailing comma
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>>
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>,
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>, //
z: Iterable<Iterable<Number>> // /**/
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>
// wd
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,/*
*/
z: Iterable<Iterable<Number>>, /* //
*/
) -> Int = {
10
}
val x: (/**/y: Comparable<Comparable<Number>>/**/) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,/**/
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>
) -> Int = {
10
}
val x: ( /*
*/y: Comparable<Comparable<Number>>) -> Int = {
10
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,97 @@
fun main() {
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>>, // trailing comma
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>>,
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>,
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>, //
z: Iterable<Iterable<Number>>, // /**/
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>>,
// wd
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,/*
*/
z: Iterable<Iterable<Number>>, /* //
*/
) -> Int = {
10
}
val x: (/**/y: Comparable<Comparable<Number>>/**/) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,/**/
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
val x: (
/*
*/
y: Comparable<Comparable<Number>>,
) -> Int = {
10
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,80 @@
fun main() {
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>> // trailing comma
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>,
z: Iterable<Iterable<Number>>
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>,) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>,) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>
) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>) -> Int = {
10
}
val x: (
y: Comparable<Comparable<Number>>, //
z: Iterable<Iterable<Number>> // /**/
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>, z: Iterable<Iterable<Number>>
// wd
) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>/*
*/, z: Iterable<Iterable<Number>>, /* //
*/) -> Int = {
10
}
val x: (/**/y: Comparable<Comparable<Number>>/**/) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>/**/,) -> Int = {
10
}
val x: (y: Comparable<Comparable<Number>>
) -> Int = {
10
}
val x: ( /*
*/y: Comparable<Comparable<Number>>) -> Int = {
10
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -468,8 +468,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -478,7 +478,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -462,7 +462,7 @@ class G(
class G(
val x: String,
val y: String
= "" /* */, /* */
= "", /* */ /* */
val z: String,
)
@@ -475,8 +475,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -485,7 +485,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String,
val x: String, val y: String, val z: String, // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -454,7 +454,7 @@ class K(
, )
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -468,8 +468,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -478,7 +478,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -462,7 +462,7 @@ class G(
class G(
val x: String,
val y: String
= "" /* */, /* */
= "", /* */ /* */
val z: String,
)
@@ -475,8 +475,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -485,7 +485,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String,
val x: String, val y: String, val z: String, // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -454,7 +454,7 @@ class K(
, )
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -490,8 +490,8 @@ class H(
class J(
val x: String,
val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -503,7 +503,7 @@ class K(
class L(
val x: String,
val y: String,
val z: String
val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -477,7 +477,7 @@ class G(
class G(
val x: String,
val y: String
= "" /* */, /* */
= "", /* */ /* */
val z: String,
)
@@ -491,8 +491,8 @@ class H(
class J(
val x: String,
val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -504,7 +504,7 @@ class K(
class L(
val x: String,
val y: String,
val z: String,
val z: String, // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -454,7 +454,7 @@ class K(
, )
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -468,8 +468,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -478,7 +478,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -462,7 +462,7 @@ class G(
class G(
val x: String,
val y: String
= "" /* */, /* */
= "", /* */ /* */
val z: String,
)
@@ -475,8 +475,8 @@ class H(
class J(
val x: String, val y: String,
val z: String /*
*/,
val z: String, /*
*/
)
class K(
@@ -485,7 +485,7 @@ class K(
)
class L(
val x: String, val y: String, val z: String,
val x: String, val y: String, val z: String, // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -454,7 +454,7 @@ class K(
, )
class L(
val x: String, val y: String, val z: String
val x: String, val y: String, val z: String // adwd
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -1299,6 +1299,11 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/valueParameters"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("LambdaValueParameters.after.kt")
public void testLambdaValueParameters() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/LambdaValueParameters.after.kt");
}
@TestMetadata("ParameterListChopAsNeeded.after.kt")
public void testParameterListChopAsNeeded() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/ParameterListChopAsNeeded.after.kt");
@@ -1851,6 +1856,11 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/valueParameters"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true);
}
@TestMetadata("LambdaValueParameters.after.inv.kt")
public void testLambdaValueParameters() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/LambdaValueParameters.after.inv.kt");
}
@TestMetadata("ParameterListChopAsNeeded.after.inv.kt")
public void testParameterListChopAsNeeded() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/ParameterListChopAsNeeded.after.inv.kt");