Formatter: support trailing comma in value parameters

#KT-34744
This commit is contained in:
Dmitry Gridin
2019-12-18 21:01:08 +07:00
parent 463728a96a
commit 13ae4a28a8
51 changed files with 1644 additions and 70 deletions
@@ -11,7 +11,10 @@ import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
import com.intellij.psi.codeStyle.PackageEntry;
import com.intellij.psi.codeStyle.PackageEntryTable;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -50,6 +53,7 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
public int WRAP_EXPRESSION_BODY_FUNCTIONS = 0;
public int WRAP_ELVIS_EXPRESSIONS = 1;
public boolean IF_RPAREN_ON_NEW_LINE = false;
public boolean ALLOW_TRAILING_COMMA = true;
@ReflectionUtil.SkipInEquals
public String CODE_STYLE_DEFAULTS = null;
@@ -19,6 +19,7 @@ import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.Companion.strategy
import org.jetbrains.kotlin.idea.util.getLineCount
import org.jetbrains.kotlin.idea.util.requireNode
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes
@@ -598,7 +599,14 @@ abstract class KotlinCommonBlock(
parentElementType === SECONDARY_CONSTRUCTOR
) {
val wrap = Wrap.createWrap(commonSettings.METHOD_PARAMETERS_WRAP, false)
return { childElement -> wrap.takeIf { childElement.elementType === VALUE_PARAMETER } }
val withTrailingComma = node.withTrailingComma
return { childElement ->
val childElementType = childElement.elementType
if (withTrailingComma && (childElementType === RPAR || getPrevWithoutWhitespaceAndComments(childElement)?.elementType === LPAR))
Wrap.createWrap(WrapType.ALWAYS, true)
else
wrap.takeIf { childElementType === VALUE_PARAMETER }
}
}
}
@@ -698,6 +706,13 @@ abstract class KotlinCommonBlock(
return ::noWrapping
}
private val ASTNode.withTrailingComma: Boolean
get() = when {
lastChildNode?.let { getPrevWithoutWhitespaceAndComments(it) }?.elementType === COMMA -> true
settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA -> psi?.getLineCount()?.let { it > 1 } == true
else -> false
}
}
private fun ASTNode.isFirstParameter(): Boolean = treePrev?.elementType == LPAR
@@ -271,10 +271,9 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing
after(DOC_COMMENT).lineBreakInCode()
// =============== Spacing ================
before(COMMA).spaceIf(kotlinCommonSettings.SPACE_BEFORE_COMMA)
before(COMMA).spacesNoLineBreak(if (kotlinCommonSettings.SPACE_BEFORE_COMMA) 1 else 0)
after(COMMA).spaceIf(kotlinCommonSettings.SPACE_AFTER_COMMA)
val spacesAroundAssignment = if (kotlinCommonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS) 1 else 0
beforeInside(EQ, PROPERTY).spacesNoLineBreak(spacesAroundAssignment)
beforeInside(EQ, FUN).spacing(spacesAroundAssignment, spacesAroundAssignment, 0, false, 0)
@@ -1,11 +1,15 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.util
import com.intellij.formatting.ASTBlock
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
/*
@@ -16,4 +20,20 @@ fun ASTBlock.requireNode() = node ?: error("ASTBlock.getNode() returned null")
/**
* Can be removed with all usages after moving master to 1.3 with new default code style settings.
*/
val isDefaultOfficialCodeStyle by lazy { !KotlinCodeStyleSettings.defaultSettings().CONTINUATION_INDENT_FOR_CHAINED_CALLS }
val isDefaultOfficialCodeStyle by lazy { !KotlinCodeStyleSettings.defaultSettings().CONTINUATION_INDENT_FOR_CHAINED_CALLS }
fun PsiElement.getLineCount(): Int {
val doc = containingFile?.let { file -> file.viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(file) }
if (doc != null) {
val spaceRange = textRange ?: TextRange.EMPTY_RANGE
if (spaceRange.endOffset <= doc.textLength) {
val startLine = doc.getLineNumber(spaceRange.startOffset)
val endLine = doc.getLineNumber(spaceRange.endOffset)
return endLine - startLine + 1
}
}
return StringUtil.getLineBreakCount(text ?: "") + 1
}
@@ -430,6 +430,7 @@
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.idea.KotlinFoldingBuilder"/>
<lang.formatter language="kotlin" implementationClass="org.jetbrains.kotlin.idea.formatter.KotlinFormattingModelBuilder"/>
<preFormatProcessor implementation="org.jetbrains.kotlin.idea.formatter.KotlinPreFormatProcessor"/>
<postFormatProcessor implementation="org.jetbrains.kotlin.idea.formatter.TrailingCommaPostFormatProcessor"/>
<lang.findUsagesProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesProvider"/>
<lang.elementManipulator forClass="org.jetbrains.kotlin.psi.KtStringTemplateExpression"
implementationClass="org.jetbrains.kotlin.psi.psiUtil.KtStringTemplateExpressionManipulator"/>
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -293,6 +293,8 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide
consumer.renameStandardOption("METHOD_ANNOTATION_WRAP", "Function annotations")
consumer.renameStandardOption(CodeStyleSettingsCustomizable.WRAPPING_METHOD_PARENTHESES, "Function parentheses")
showCustomOption(KotlinCodeStyleSettings::ALLOW_TRAILING_COMMA, "Use trailing comma")
showCustomOption(
KotlinCodeStyleSettings::ALIGN_IN_COLUMNS_CASE_BRANCH,
"Align 'when' branches in columns",
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessorHelper
import org.jetbrains.kotlin.idea.util.getLineCount
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtParameterList
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class TrailingCommaPostFormatProcessor : PostFormatProcessor {
override fun processElement(source: PsiElement, settings: CodeStyleSettings): PsiElement =
TrailingCommaVisitor(settings).process(source)
override fun processText(source: PsiFile, rangeToReformat: TextRange, settings: CodeStyleSettings): TextRange =
TrailingCommaVisitor(settings).processText(source, rangeToReformat)
}
private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisitorVoid() {
private val myPostProcessor = PostFormatProcessorHelper(settings.kotlinCommonSettings)
override fun visitParameterList(list: KtParameterList) = processCommaOwnerIfInRange(list) {
super.visitParameterList(list)
}
private fun processCommaOwnerIfInRange(element: KtElement, preHook: () -> Unit = {}) {
if (myPostProcessor.isElementPartlyInRange(element)) {
preHook()
processCommaOwner(element)
}
}
private fun processCommaOwner(parent: KtElement) {
val previous = parent.lastChild?.getPrevSiblingIgnoringWhitespaceAndComments() ?: return
if (previous.safeAs<ASTNode>()?.elementType !== KtTokens.COMMA &&
settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA &&
parent.getLineCount() > 1
) {
val oldLength = parent.textLength
parent.addAfter(KtPsiFactory(parent).createComma(), previous)
myPostProcessor.updateResultRange(oldLength, parent.textLength)
}
}
fun process(formatted: PsiElement): PsiElement {
LOG.assertTrue(formatted.isValid)
formatted.accept(this)
return formatted
}
fun processText(
source: PsiFile,
rangeToReformat: TextRange
): TextRange {
myPostProcessor.resultTextRange = rangeToReformat
source.accept(this)
return myPostProcessor.resultTextRange
}
companion object {
private val LOG = Logger.getInstance(TrailingCommaVisitor::class.java)
}
}
@@ -19,7 +19,7 @@ class FooM
}
class FooC(
val x: String
val x: String,
) {
fun bar()
@@ -1,5 +1,5 @@
fun foo(
x: Int
x: Int,
) {
}
@@ -1,3 +1,5 @@
fun test(a: Int,
b: Int) {
}
}
// TRAILING_COMMA: false
+3 -1
View File
@@ -1,3 +1,5 @@
fun test(a : Int,
b : Int) {
}
}
// TRAILING_COMMA: false
+4 -2
View File
@@ -28,8 +28,10 @@ fun test() {
test(fun test() = 4)
}
fun d = fun(a: Int,
b: String) {
fun d = fun(
a: Int,
b: String,
) {
}
fun e = fun() {
@@ -1,4 +1,4 @@
open class GFMOutputBuilder(
to: StringBuilder
to: StringBuilder,
) : MarkdownOutputBuilder(to) {
}
@@ -1,5 +1,5 @@
open class GFMOutputBuilder(
to: StringBuilder
to: StringBuilder,
) : MarkdownOutputBuilder(to) {
}
@@ -2,3 +2,4 @@ fun foo(a: String,
b: String)
// SET_TRUE: METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
@@ -3,3 +3,4 @@ fun foo(
b: String)
// SET_TRUE: METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
+1
View File
@@ -2,3 +2,4 @@ fun foo(a: String,
b: String)
// SET_TRUE: METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
@@ -2,3 +2,4 @@ fun foo(a: String,
b: String)
// SET_TRUE: METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
@@ -3,3 +3,4 @@ fun foo(a: String,
)
// SET_TRUE: METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
+1
View File
@@ -2,3 +2,4 @@ fun foo(a: String,
b: String)
// SET_TRUE: METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE
// TRAILING_COMMA: false
+3 -1
View File
@@ -45,4 +45,6 @@ fun commented1() {}
fun commented2() {}
// Comment
fun commented3() {}
fun commented3() {}
// TRAILING_COMMA: false
+3 -1
View File
@@ -39,4 +39,6 @@ fun commented1() {}
fun commented2() {}
// Comment
fun commented3() {}
fun commented3() {}
// TRAILING_COMMA: false
+2 -1
View File
@@ -2,6 +2,7 @@ fun foo(@Deprecated("x")
x: Int,
@Deprecated("y")
@Deprecated("z")
y: Int)
y: Int,
)
// SET_INT: PARAMETER_ANNOTATION_WRAP = 2
+3 -2
View File
@@ -8,7 +8,8 @@ class A(
/**
* Doc
*/
val p2: String)
val p2: String,
)
class B(
/** Doc1 */
@@ -17,5 +18,5 @@ class B(
/**
* Doc2
*/
val v2: Int
val v2: Int,
)
+1 -1
View File
@@ -1,7 +1,7 @@
fun some(
a :Int,
b :String, c :Int,
d :String, e :Int
d :String, e :Int,
) {
}
+1 -1
View File
@@ -1,7 +1,7 @@
fun some(
a: Int,
b: String, c: Int,
d: String, e: Int
d: String, e: Int,
) {
}
+2 -1
View File
@@ -4,4 +4,5 @@ class Test {
}
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// TRAILING_COMMA: false
+2 -1
View File
@@ -4,4 +4,5 @@ class Test {
}
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// TRAILING_COMMA: false
+2 -1
View File
@@ -4,4 +4,5 @@ fun someLong(a : Int
}
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
// TRAILING_COMMA: false
+3 -1
View File
@@ -2,4 +2,6 @@ class Foo(val x: Int,
var y: Int) {
val a = 1
var b = 2
}
}
// TRAILING_COMMA: false
+3 -1
View File
@@ -2,4 +2,6 @@ class Foo(val x: Int,
var y: Int) {
val a = 1
var b = 2
}
}
// TRAILING_COMMA: false
+1 -1
View File
@@ -1,6 +1,6 @@
abstract class RustNavigationContributorBase<T> protected constructor(
private val indexKey: StubIndexKey<String, T>,
private val clazz: Class<T>
private val clazz: Class<T>,
) : ChooseByNameContributor, GotoClassContributor
where T : NavigationItem,
T : RustNamedElement {
@@ -1,12 +1,12 @@
fun foo(
x: Int =
42
42,
) {
}
class C(
val x: Int =
42
42,
)
// SET_TRUE: CONTINUATION_INDENT_FOR_EXPRESSION_BODIES
@@ -1,12 +1,12 @@
fun foo(
x: Int =
42
42,
) {
}
class C(
val x: Int =
42
42,
)
// SET_TRUE: CONTINUATION_INDENT_FOR_EXPRESSION_BODIES
@@ -15,7 +15,8 @@ fun test(foo: Int, bar: Int)
fun testtesttesttest(foofoo: Int,
barbar: Int,
foobar: Int,
barfoo: Int)
barfoo: Int,
)
fun test() {
for (foo: Int in bar) {
@@ -41,20 +42,24 @@ class SN
class A(longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
constructor(
longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
}
}
class B(a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
constructor(a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
}
}
@@ -66,7 +71,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
constructor(sn1: ShorName,
sn2: ShorName,
sn3: ShorName,
@@ -75,7 +81,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
}
}
@@ -92,7 +99,8 @@ class D(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
constructor(sn1: SN,
sn2: SN,
sn3: SN,
@@ -106,13 +114,15 @@ class D(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
}
}
class E(longLongLongLongNameCLass1: LongLongLongLongNameCLass) {
constructor(
longLongLongLongNameCLass1: LongLongLongLongNameCLass) {
longLongLongLongNameCLass1: LongLongLongLongNameCLass,
) {
}
}
@@ -11,22 +11,26 @@ fun testtest(foofoo: Int)
fun testtesttesttest(foofoo: Int)
fun test(foo: Int,
bar: Int)
bar: Int,
)
fun testtesttesttest(foofoo: Int,
barbar: Int,
foobar: Int,
barfoo: Int)
barfoo: Int,
)
fun testtesttesttest(foofoo: Int,
@Some barbar: Int,
foobar: Int,
barfoo: Int)
barfoo: Int,
)
fun testtesttesttest(@Some foofoo: Int,
@Some barbar: Int,
@Some foobar: Int,
barfoo: Int)
barfoo: Int,
)
fun test() {
for (foo: Int in bar) {
@@ -52,19 +56,23 @@ class SN
class A(longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
constructor(longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
}
}
class B(a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
constructor(a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
}
}
@@ -76,7 +84,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
constructor(sn1: ShorName,
sn2: ShorName,
sn3: ShorName,
@@ -85,7 +94,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
}
}
@@ -102,7 +112,8 @@ class D(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
constructor(sn1: SN,
sn2: SN,
sn3: SN,
@@ -116,7 +127,8 @@ class D(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
}
}
@@ -136,7 +148,8 @@ class G(sn1: ShorName) {
}
class H(sn1: SN,
sn2: SN) {
sn2: SN,
) {
constructor(sn1: SN) {
}
}
@@ -154,7 +167,8 @@ class I(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
constructor(sn1: SN,
sn2: SN,
sn3: SN,
@@ -168,6 +182,7 @@ class I(sn1: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
}
}
@@ -9,7 +9,8 @@ fun testtesttesttesttesttesttesttesttest()
fun testtest(foofoo: Int)
fun testtesttesttest(
foofoo: Int)
foofoo: Int,
)
fun test(foo: Int, bar: Int)
@@ -17,7 +18,8 @@ fun testtesttesttest(
foofoo: Int,
barbar: Int,
foobar: Int,
barfoo: Int)
barfoo: Int,
)
fun test() {
for (foo: Int in bar) {
@@ -43,21 +45,25 @@ class SN
class A(longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
constructor(
longLongLongLongNameCLass1: LongLongLongLongNameCLass,
longLongLongLongNameCLass2: LongLongLongLongNameCLass,
longLongLongLongNameCLass3: LongLongLongLongNameCLass) {
longLongLongLongNameCLass3: LongLongLongLongNameCLass,
) {
}
}
class B(a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
constructor(
a: LongLongLongLongNameCLass,
b: LongLongLongLongNameCLass,
c: LongLongLongLongNameCLass) {
c: LongLongLongLongNameCLass,
) {
}
}
@@ -69,7 +75,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
constructor(sn1: ShorName,
sn2: ShorName,
sn3: ShorName,
@@ -78,7 +85,8 @@ class C(sn1: ShorName,
sn6: ShorName,
sn6: ShorName,
sn8: ShorName,
sn9: ShorName) {
sn9: ShorName,
) {
}
}
@@ -88,7 +96,8 @@ class D(sn1: SN, sn2: SN,
sn6: SN, sn8: SN,
sn9: SN, sn10: SN,
sn11: SN, sn12: SN,
sn13: SN, sn14: SN) {
sn13: SN, sn14: SN,
) {
constructor(sn1: SN,
sn2: SN,
sn3: SN,
@@ -102,25 +111,29 @@ class D(sn1: SN, sn2: SN,
sn11: SN,
sn12: SN,
sn13: SN,
sn14: SN) {
sn14: SN,
) {
}
}
class E(longLongLongLongNameCLass1: LongLongLongLongNameCLass) {
constructor(
longLongLongLongNameCLass1: LongLongLongLongNameCLass) {
longLongLongLongNameCLass1: LongLongLongLongNameCLass,
) {
}
}
class F(a: LongLongLongLongNameCLass) {
constructor(
a: LongLongLongLongNameCLass) {
a: LongLongLongLongNameCLass,
) {
}
}
class G(sn1: ShorName) {
constructor(
sn1: ShorName) {
sn1: ShorName,
) {
}
}
@@ -0,0 +1,85 @@
fun a1(
x: String,
y: String,
) = Unit
fun b1(
x: String,
y: String
) = Unit
fun c1(
x: String,
y: String,
) = Unit
fun d1(
x: String,
y: String,
) = Unit
fun a2(
x: String,
y: String,
z: String,
) = Unit
fun b2(
x: String,
y: String,
z: String
) = Unit
fun c2(
x: String,
y: String,
z: String,
) = Unit
fun d2(
x: String,
y: String,
z: String,
) = Unit
fun a3(
x: String,
) = Unit
fun b3(
x: String
) = Unit
fun c3(
x: String,
) = Unit
fun d3(
x: String,
) = Unit
fun a4(
x: String,
y: String,
z: String,
) = Unit
fun b4(
x: String,
y: String,
z: String
) = Unit
fun c4(
x: String,
y: String,
z: String,
) = Unit
fun d4(
x: String,
y: String,
z: String,
) = Unit
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,85 @@
fun a1(
x: String,
y: String,
) = Unit
fun b1(
x: String,
y: String,
) = Unit
fun c1(
x: String,
y: String,
) = Unit
fun d1(
x: String,
y: String,
) = Unit
fun a2(
x: String,
y: String,
z: String,
) = Unit
fun b2(
x: String,
y: String,
z: String,
) = Unit
fun c2(
x: String,
y: String,
z: String,
) = Unit
fun d2(
x: String,
y: String,
z: String,
) = Unit
fun a3(
x: String,
) = Unit
fun b3(
x: String,
) = Unit
fun c3(
x: String,
) = Unit
fun d3(
x: String,
) = Unit
fun a4(
x: String,
y: String,
z: String,
) = Unit
fun b4(
x: String,
y: String,
z: String,
) = Unit
fun c4(
x: String,
y: String,
z: String,
) = Unit
fun d4(
x: String,
y: String,
z: String,
) = Unit
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,81 @@
fun a1(
x: String,
y: String,
) = Unit
fun b1(
x: String,
y: String
) = Unit
fun c1(
x: String,
y: String,) = Unit
fun d1(
x: String,
y: String
,) = Unit
fun a2(
x: String,
y: String,
z: String,
) = Unit
fun b2(
x: String,
y: String,
z: String
) = Unit
fun c2(
x: String,
y: String,
z: String,) = Unit
fun d2(
x: String,
y: String,
z: String
,) = Unit
fun a3(
x: String,
) = Unit
fun b3(
x: String
) = Unit
fun c3(
x: String,) = Unit
fun d3(
x: String
,) = Unit
fun a4(
x: String
,
y: String,
z: String ,
) = Unit
fun b4(
x: String,
y: String,
z: String
) = Unit
fun c4(x: String,
y: String,
z: String ,) = Unit
fun d4(
x: String,
y: String,
z: String
, ) = Unit
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,75 @@
val foo1: (Int, Int) -> Int = fun(
x,
y,
): Int = 42
val foo2: (Int, Int) -> Int = fun(
x,
y
): Int {
return x + y
}
val foo3: (Int, Int) -> Int = fun(
x, y,
): Int {
return x + y
}
val foo4: (Int) -> Int = fun(
x,
): Int = 42
val foo5: (Int) -> Int = fun(
x
): Int = 42
val foo6: (Int) -> Int = fun(
x,
): Int = 42
val foo7: (Int) -> Int = fun(x): Int = 42
val foo8: (Int, Int, Int) -> Int = fun(
x, y: Int, z,
): Int {
return x + y
}
val foo9: (Int, Int, Int) -> Int = fun(
x,
y: Int,
z,
): Int = 42
val foo10: (Int, Int, Int) -> Int = fun(
x,
y: Int,
z: Int
): Int = 43
val foo10 = fun(
x: Int,
y: Int,
z: Int
): Int = 43
val foo11 = fun(
x: Int,
y: Int,
z: Int,
): Int = 43
val foo12 = fun(
x: Int, y: Int, z: Int,
): Int = 43
val foo13 = fun(
x: Int, y: Int, z: Int,
): Int = 43
val foo14 = fun(
x: Int, y: Int, z: Int,
): Int = 43
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,75 @@
val foo1: (Int, Int) -> Int = fun(
x,
y,
): Int = 42
val foo2: (Int, Int) -> Int = fun(
x,
y,
): Int {
return x + y
}
val foo3: (Int, Int) -> Int = fun(
x, y,
): Int {
return x + y
}
val foo4: (Int) -> Int = fun(
x,
): Int = 42
val foo5: (Int) -> Int = fun(
x,
): Int = 42
val foo6: (Int) -> Int = fun(
x,
): Int = 42
val foo7: (Int) -> Int = fun(x): Int = 42
val foo8: (Int, Int, Int) -> Int = fun(
x, y: Int, z,
): Int {
return x + y
}
val foo9: (Int, Int, Int) -> Int = fun(
x,
y: Int,
z,
): Int = 42
val foo10: (Int, Int, Int) -> Int = fun(
x,
y: Int,
z: Int,
): Int = 43
val foo10 = fun(
x: Int,
y: Int,
z: Int,
): Int = 43
val foo11 = fun(
x: Int,
y: Int,
z: Int,
): Int = 43
val foo12 = fun(
x: Int, y: Int, z: Int,
): Int = 43
val foo13 = fun(
x: Int, y: Int, z: Int,
): Int = 43
val foo14 = fun(
x: Int, y: Int, z: Int,
): Int = 43
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,69 @@
val foo1: (Int, Int) -> Int = fun(
x,
y,
): Int = 42
val foo2: (Int, Int) -> Int = fun(
x,
y
): Int {
return x + y
}
val foo3: (Int, Int) -> Int = fun(
x, y,
): Int {
return x + y
}
val foo4: (Int) -> Int = fun(
x,
): Int = 42
val foo5: (Int) -> Int = fun(
x
): Int = 42
val foo6: (Int) -> Int = fun(x,): Int = 42
val foo7: (Int) -> Int = fun(x): Int = 42
val foo8: (Int, Int, Int) -> Int = fun (x, y: Int, z,): Int {
return x + y
}
val foo9: (Int, Int, Int) -> Int = fun (
x,
y: Int,
z,
): Int = 42
val foo10: (Int, Int, Int) -> Int = fun (
x,
y: Int,
z: Int
): Int = 43
val foo10 = fun (
x: Int,
y: Int,
z: Int
): Int = 43
val foo11 = fun (
x: Int,
y: Int,
z: Int,
): Int = 43
val foo12 = fun (
x: Int, y: Int, z: Int,
): Int = 43
val foo13 = fun (x: Int, y: Int, z: Int,
): Int = 43
val foo14 = fun (x: Int, y: Int, z: Int
,): Int = 43
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,94 @@
class A1(
val x: String,
y: String,
)
class B1(
val x: String,
val y: String
)
class C1(
val x: String,
val y: String,
)
class D1(
val x: String,
val y: String,
)
class A2(
val x: String,
val y: String,
val z: String,
)
class B2(
val x: String,
val y: String,
val z: String
)
class C2(
val x: String,
val y: String,
val z: String,
)
class D2(
val x: String,
val y: String,
val z: String,
)
class A3(
val x: String,
)
class B3(
val x: String
)
class C3(
val x: String,
)
class D3(
val x: String,
)
class A4(
val x: String,
val y: String,
val z: String,
)
class B4(
val x: String,
val y: String,
val z: String
)
class C4(
val x: String,
val y: String,
val z: String,
)
class D4(
val x: String,
val y: String,
val z: String,
)
class E1(
val x: String, val y: String,
val z: String,
)
class E2(
val x: String, val y: String, val z: String
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,94 @@
class A1(
val x: String,
y: String,
)
class B1(
val x: String,
val y: String,
)
class C1(
val x: String,
val y: String,
)
class D1(
val x: String,
val y: String,
)
class A2(
val x: String,
val y: String,
val z: String,
)
class B2(
val x: String,
val y: String,
val z: String,
)
class C2(
val x: String,
val y: String,
val z: String,
)
class D2(
val x: String,
val y: String,
val z: String,
)
class A3(
val x: String,
)
class B3(
val x: String,
)
class C3(
val x: String,
)
class D3(
val x: String,
)
class A4(
val x: String,
val y: String,
val z: String,
)
class B4(
val x: String,
val y: String,
val z: String,
)
class C4(
val x: String,
val y: String,
val z: String,
)
class D4(
val x: String,
val y: String,
val z: String,
)
class E1(
val x: String, val y: String,
val z: String,
)
class E2(
val x: String, val y: String, val z: String,
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,90 @@
class A1(
val x: String,
y: String,
)
class B1(
val x: String,
val y: String
)
class C1(
val x: String,
val y: String,)
class D1(
val x: String,
val y: String
,)
class A2(
val x: String,
val y: String,
val z: String,
)
class B2(
val x: String,
val y: String,
val z: String
)
class C2(
val x: String,
val y: String,
val z: String,)
class D2(
val x: String,
val y: String,
val z: String
,)
class A3(
val x: String,
)
class B3(
val x: String
)
class C3(
val x: String,)
class D3(
val x: String
,)
class A4(
val x: String ,
val y: String,
val z: String ,
)
class B4(
val x: String,
val y: String,
val z: String
)
class C4(
val x: String,
val y: String,
val z: String ,)
class D4(
val x: String,
val y: String,
val z: String
, )
class E1(
val x: String, val y: String,
val z: String
, )
class E2(
val x: String, val y: String, val z: String
)
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,184 @@
class A1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class B1 {
val x: String
val y: String
constructor(
x: String,
y: String
) {
this.x = x
this.y = y
}
}
class C1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class D1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class A2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class B2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String
) {
this.x = x
this.y = y
this.z = z
}
}
class C2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class D2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class A3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class B3 {
val x: String
constructor(x: String) {
this.x = x
}
}
class C3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class D3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class E1 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class E2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String) {
this.x = x
this.y = y
this.z = z
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,185 @@
class A1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class B1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class C1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class D1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class A2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class B2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class C2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class D2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class A3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class B3 {
val x: String
constructor(x: String) {
this.x = x
}
}
class C3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class D3 {
val x: String
constructor(
x: String,
) {
this.x = x
}
}
class E1 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class E2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,178 @@
class A1 {
val x: String
val y: String
constructor(
x: String,
y: String,
) {
this.x = x
this.y = y
}
}
class B1 {
val x: String
val y: String
constructor(
x: String,
y: String
) {
this.x = x
this.y = y
}
}
class C1 {
val x: String
val y: String
constructor(
x: String,
y: String,) {
this.x = x
this.y = y
}
}
class D1 {
val x: String
val y: String
constructor(
x: String,
y: String
,) {
this.x = x
this.y = y
}
}
class A2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,
) {
this.x = x
this.y = y
this.z = z
}
}
class B2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String
) {
this.x = x
this.y = y
this.z = z
}
}
class C2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String,) {
this.x = x
this.y = y
this.z = z
}
}
class D2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String,
z: String
,) {
this.x = x
this.y = y
this.z = z
}
}
class A3 {
val x: String
constructor(x: String,) {
this.x = x
}
}
class B3 {
val x: String
constructor(x: String) {
this.x = x
}
}
class C3 {
val x: String
constructor(
x: String,) {
this.x = x
}
}
class D3 {
val x: String
constructor(
x: String
,) {
this.x = x
}
}
class E1 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String,) {
this.x = x
this.y = y
this.z = z
}
}
class E2 {
val x: String
val y: String
val z: String
constructor(
x: String,
y: String, z: String) {
this.x = x
this.y = y
this.z = z
}
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings;
import org.jetbrains.kotlin.idea.test.KotlinLightIdeaTestCase;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
@@ -139,6 +140,11 @@ public abstract class AbstractFormatterTest extends KotlinLightIdeaTestCase {
codeStyleSettings.setRightMargin(KotlinLanguage.INSTANCE, rightMargin);
}
Boolean trailingComma = InTextDirectivesUtils.getPrefixedBoolean(originalFileText, "// TRAILING_COMMA: ");
if (trailingComma != null) {
codeStyleSettings.getCustomSettings(KotlinCodeStyleSettings.class).ALLOW_TRAILING_COMMA = trailingComma;
}
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(originalFileText, codeStyleSettings);
if (!inverted) {
configurator.configureSettings();
@@ -1151,6 +1151,52 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
runTest("idea/testData/formatter/parameterList/ParameterListWrapAsNeeded.after.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TrailingComma extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInTrailingComma() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("idea/testData/formatter/trailingComma/valueParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ValueParameters extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInValueParameters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/valueParameters"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("Function.after.kt")
public void testFunction() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/Function.after.kt");
}
@TestMetadata("OptionalTypes.after.kt")
public void testOptionalTypes() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/OptionalTypes.after.kt");
}
@TestMetadata("PrimaryConstructor.after.kt")
public void testPrimaryConstructor() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/PrimaryConstructor.after.kt");
}
@TestMetadata("SecondConstructor.after.kt")
public void testSecondConstructor() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/SecondConstructor.after.kt");
}
}
}
}
@TestMetadata("idea/testData/formatter")
@@ -1516,5 +1562,51 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
runTest("idea/testData/formatter/parameterList/DefaultParameterValues.after.inv.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TrailingComma extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestInverted, this, testDataFilePath);
}
public void testAllFilesPresentInTrailingComma() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true);
}
@TestMetadata("idea/testData/formatter/trailingComma/valueParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ValueParameters extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestInverted, this, testDataFilePath);
}
public void testAllFilesPresentInValueParameters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/valueParameters"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true);
}
@TestMetadata("Function.after.inv.kt")
public void testFunction() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/Function.after.inv.kt");
}
@TestMetadata("OptionalTypes.after.inv.kt")
public void testOptionalTypes() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/OptionalTypes.after.inv.kt");
}
@TestMetadata("PrimaryConstructor.after.inv.kt")
public void testPrimaryConstructor() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/PrimaryConstructor.after.inv.kt");
}
@TestMetadata("SecondConstructor.after.inv.kt")
public void testSecondConstructor() throws Exception {
runTest("idea/testData/formatter/trailingComma/valueParameters/SecondConstructor.after.inv.kt");
}
}
}
}
}