KotlinLikeLangLineIndentProvider: improve indent for braces

Part of #KT-22211
Part of #KT-39353
This commit is contained in:
Dmitry Gridin
2020-06-13 22:40:48 +07:00
parent 9d6ea3c073
commit 01707800c1
33 changed files with 372 additions and 23 deletions
@@ -43,12 +43,25 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
val before = currentPosition.beforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
val after = currentPosition.afterOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
when {
before.isAt(BlockOpeningBrace) && after.isAt(BlockClosingBrace) && !currentPosition.hasLineBreaksAfter(offset) ->
return factory.createIndentCalculator(Indent.getNoneIndent(), before.startOffset)
before.isAt(ArrayOpeningBracket) && after.isAt(ArrayClosingBracket) && !currentPosition.hasLineBreaksAfter(offset) ->
return factory.createIndentCalculator(Indent.getNoneIndent(), before.startOffset)
when {
after.isAt(BlockClosingBrace) && !currentPosition.hasLineBreaksAfter(offset) ->
return factory.createIndentCalculatorForBrace(before, after, BlockOpeningBrace, BlockClosingBrace)
before.isAt(BlockOpeningBrace) && after.isAt(BlockClosingBrace) ->
return factory.createIndentCalculator(Indent.getNormalIndent(), before.startOffset)
after.isAt(ArrayClosingBracket) && !currentPosition.hasLineBreaksAfter(offset) ->
return factory.createIndentCalculatorForBrace(before, after, ArrayOpeningBracket, ArrayClosingBracket)
before.isAt(ArrayOpeningBracket) && after.isAt(ArrayClosingBracket) -> {
val indent = if (isSimilarToFunctionInvocation(before))
Indent.getContinuationIndent()
else
Indent.getNormalIndent()
return factory.createIndentCalculator(indent, before.startOffset)
}
before.isAt(BlockOpeningBrace) && before.beforeIgnoringWhiteSpaceOrComment().isFunctionDeclaration() ->
return factory.createIndentCalculator(Indent.getNormalIndent(), before.startOffset)
@@ -115,6 +128,24 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
}
}
private fun IndentCalculatorFactory.createIndentCalculatorForBrace(
before: SemanticEditorPosition,
after: SemanticEditorPosition,
leftBraceType: SemanticEditorPosition.SyntaxElement,
rightBraceType: SemanticEditorPosition.SyntaxElement
): IndentCalculator? {
val leftBrace = before.copyAnd {
it.moveToLeftParenthesisBackwardsSkippingNested(leftBraceType, rightBraceType)
}
val indent = if (after.after().afterOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET).isAt(Comma))
createAlignMultilineIndent(leftBrace)
else
Indent.getNoneIndent()
return createIndentCalculator(indent, leftBrace.startOffset)
}
private fun IndentCalculatorFactory.createIndentCalculatorForParenthesis(
leftParenthesis: SemanticEditorPosition,
currentPosition: SemanticEditorPosition,
@@ -272,20 +303,21 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
}
}
/**
* @param leftParenthesis is `(` or `[`
*/
private fun isSimilarToFunctionInvocation(leftParenthesis: SemanticEditorPosition): Boolean = with(leftParenthesis.copy()) {
assert(isAt(LeftParenthesis))
moveBeforeIgnoringWhiteSpaceOrComment()
moveBefore()
// `a()()()`
// ^
while (moveBeforeParenthesesIfPossible()) {
// loop
}
if (!moveBeforeWhileThisIsWhiteSpaceOnSameLineOrBlockComment() || isAtEnd) return false
// calls with types e.g. `test<Int>()`
moveBeforeTypeParametersIfPossible()
if (isAt(CloseTypeBrace)) {
moveBeforeParentheses(OpenTypeBrace, CloseTypeBrace)
return moveBeforeWhileThisIsWhiteSpaceOnSameLineOrBlockComment() && isIdentifier()
}
return isAt(Identifier) || isAt(KtTokens.THIS_KEYWORD)
return isIdentifier() || isAtAnyOf(RightParenthesis, ArrayClosingBracket)
}
private fun isDestructuringDeclaration(
@@ -448,6 +480,8 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
}
}
private fun SemanticEditorPosition.isIdentifier(): Boolean = isAt(Identifier) || isAt(KtTokens.THIS_KEYWORD)
private fun SemanticEditorPosition.isVarOrVal(): Boolean = isAtAnyOf(Var, Val)
private fun SemanticEditorPosition.moveBeforeBlockIfPossible(): Boolean = moveBeforeParenthesesIfPossible(
@@ -478,6 +512,16 @@ abstract class KotlinLikeLangLineIndentProvider : JavaLikeLangLineIndentProvider
private fun SemanticEditorPosition.moveBeforeWhileThisIsWhiteSpaceOrComment() = moveBeforeOptionalMix(*WHITE_SPACE_OR_COMMENT_BIT_SET)
private fun SemanticEditorPosition.moveBeforeWhileThisIsWhiteSpaceOnSameLineOrBlockComment(): Boolean {
while (!isAtEnd) {
if (isAt(Whitespace) && isAtMultiline) return false
if (!isAt(BlockComment)) break
moveBefore()
}
return true
}
private fun SemanticEditorPosition.moveBeforeIgnoringWhiteSpaceOrComment() {
moveBefore()
moveBeforeWhileThisIsWhiteSpaceOrComment()
@@ -183,11 +183,36 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
runTest("idea/testData/indentationOnNewline/KT20783.kt");
}
@TestMetadata("LambdaInArguments.kt")
public void testLambdaInArguments() throws Exception {
runTest("idea/testData/indentationOnNewline/LambdaInArguments.kt");
}
@TestMetadata("LambdaInArguments2.kt")
public void testLambdaInArguments2() throws Exception {
runTest("idea/testData/indentationOnNewline/LambdaInArguments2.kt");
}
@TestMetadata("LargeFile.kt")
public void testLargeFile() throws Exception {
runTest("idea/testData/indentationOnNewline/LargeFile.kt");
}
@TestMetadata("LiteralExpression.kt")
public void testLiteralExpression() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression.kt");
}
@TestMetadata("LiteralExpression2.kt")
public void testLiteralExpression2() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression2.kt");
}
@TestMetadata("LiteralExpression3.kt")
public void testLiteralExpression3() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression3.kt");
}
@TestMetadata("ModifierListInUnfinishedDeclaration.kt")
public void testModifierListInUnfinishedDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/ModifierListInUnfinishedDeclaration.kt");
@@ -544,6 +569,26 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline/emptyParameters"), Pattern.compile("^([^.]+)\\.(kt|kts)$"), null, true);
}
@TestMetadata("EmptyArgumentInCallByArrayAccess.kt")
public void testEmptyArgumentInCallByArrayAccess() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByArrayAccess.kt");
}
@TestMetadata("EmptyArgumentInCallByArrayAccess2.kt")
public void testEmptyArgumentInCallByArrayAccess2() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByArrayAccess2.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration.kt")
public void testEmptyArgumentInCallByDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByDeclaration.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration2.kt")
public void testEmptyArgumentInCallByDeclaration2() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByDeclaration2.kt");
}
@TestMetadata("EmptyArgumentInCallByReference.kt")
public void testEmptyArgumentInCallByReference() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByReference.kt");
@@ -832,6 +877,16 @@ public class PerformanceTypingIndentationTestGenerated extends AbstractPerforman
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/BinaryWithTypeExpressions.kt");
}
@TestMetadata("EmptyArgumentInCallByArrayAccess.kt")
public void testEmptyArgumentInCallByArrayAccess() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByArrayAccess.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration.kt")
public void testEmptyArgumentInCallByDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByDeclaration.kt");
}
@TestMetadata("InBinaryExpressionInMiddle.kt")
public void testInBinaryExpressionInMiddle() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/InBinaryExpressionInMiddle.kt");
@@ -1,5 +1,3 @@
val a: (String) -> String = { some ->
<caret>
}
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
@@ -1,3 +1 @@
val a: (String) -> String = { some -><caret>}
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
@@ -1,5 +1,3 @@
val a: (String) -> String = { some ->
<caret>
}
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
@@ -1,3 +1 @@
val a: (String) -> String = { some -> <caret> }
// WITHOUT_CUSTOM_LINE_INDENT_PROVIDER
@@ -0,0 +1,9 @@
fun a() {
b({1
<caret>},
{},
{},
)
}
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
fun a() {
b({1<caret>},
{},
{},
)
}
// IGNORE_FORMATTER
@@ -0,0 +1,10 @@
fun a() {
b({
1
<caret>},
{},
{},
)
}
// IGNORE_FORMATTER
@@ -0,0 +1,9 @@
fun a() {
b({
1<caret>},
{},
{},
)
}
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
@Anno([
1
<caret>],
[2],
)
class A
// IGNORE_FORMATTER
@@ -0,0 +1,7 @@
@Anno([
1<caret>],
[2],
)
class A
// IGNORE_FORMATTER
@@ -0,0 +1,7 @@
@Anno([1
<caret>],
[2],
)
class A
// IGNORE_FORMATTER
@@ -0,0 +1,6 @@
@Anno([1<caret>],
[2],
)
class A
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
@Anno([
<caret>
],
[2],
)
class A
// IGNORE_FORMATTER
@@ -0,0 +1,6 @@
@Anno([<caret>],
[2],
)
class A
// IGNORE_FORMATTER
@@ -2,7 +2,9 @@ fun a() {
val a = listOf(1, 2)
println(
a[
<caret>
<caret>
]
)
}
// IGNORE_FORMATTER
@@ -4,3 +4,5 @@ fun a() {
a[<caret>]
)
}
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
@@ -0,0 +1,6 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](<caret>)
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
@@ -0,0 +1,9 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](
<caret>
)
}
// SET_TRUE: ALIGN_MULTILINE_METHOD_BRACKETS
// IGNORE_FORMATTER
@@ -0,0 +1,7 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](<caret>)
}
// SET_TRUE: ALIGN_MULTILINE_METHOD_BRACKETS
// IGNORE_FORMATTER
@@ -0,0 +1,9 @@
fun a() {
(fun(i: Int) {
})(
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
@@ -0,0 +1,7 @@
fun a() {
(fun(i: Int) {
})(<caret>)
}
// SET_FALSE: ALIGN_MULTILINE_METHOD_BRACKETS
@@ -0,0 +1,10 @@
fun a() {
(fun(i: Int) {
})(
<caret>
)
}
// SET_TRUE: ALIGN_MULTILINE_METHOD_BRACKETS
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
fun a() {
(fun(i: Int) {
})(<caret>)
}
// SET_TRUE: ALIGN_MULTILINE_METHOD_BRACKETS
// IGNORE_FORMATTER
@@ -0,0 +1,8 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -0,0 +1,8 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -0,0 +1,6 @@
fun a() {
val b = listOf(fun(c: Int) {})
b[0](<caret>)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -0,0 +1,9 @@
fun a() {
(fun(i: Int) {
})(
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -0,0 +1,9 @@
fun a() {
(fun(i: Int) {
})(
<caret>
)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -0,0 +1,7 @@
fun a() {
(fun(i: Int) {
})(<caret>)
}
// SET_FALSE: ALIGN_MULTILINE_BINARY_OPERATION
@@ -185,11 +185,36 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
runTest("idea/testData/indentationOnNewline/KT20783.after.kt");
}
@TestMetadata("LambdaInArguments.after.kt")
public void testLambdaInArguments() throws Exception {
runTest("idea/testData/indentationOnNewline/LambdaInArguments.after.kt");
}
@TestMetadata("LambdaInArguments2.after.kt")
public void testLambdaInArguments2() throws Exception {
runTest("idea/testData/indentationOnNewline/LambdaInArguments2.after.kt");
}
@TestMetadata("LargeFile.after.kt")
public void testLargeFile() throws Exception {
runTest("idea/testData/indentationOnNewline/LargeFile.after.kt");
}
@TestMetadata("LiteralExpression.after.kt")
public void testLiteralExpression() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression.after.kt");
}
@TestMetadata("LiteralExpression2.after.kt")
public void testLiteralExpression2() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression2.after.kt");
}
@TestMetadata("LiteralExpression3.after.kt")
public void testLiteralExpression3() throws Exception {
runTest("idea/testData/indentationOnNewline/LiteralExpression3.after.kt");
}
@TestMetadata("ModifierListInUnfinishedDeclaration.after.kt")
public void testModifierListInUnfinishedDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/ModifierListInUnfinishedDeclaration.after.kt");
@@ -546,6 +571,26 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/indentationOnNewline/emptyParameters"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("EmptyArgumentInCallByArrayAccess.after.kt")
public void testEmptyArgumentInCallByArrayAccess() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByArrayAccess.after.kt");
}
@TestMetadata("EmptyArgumentInCallByArrayAccess2.after.kt")
public void testEmptyArgumentInCallByArrayAccess2() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByArrayAccess2.after.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration.after.kt")
public void testEmptyArgumentInCallByDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByDeclaration.after.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration2.after.kt")
public void testEmptyArgumentInCallByDeclaration2() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByDeclaration2.after.kt");
}
@TestMetadata("EmptyArgumentInCallByReference.after.kt")
public void testEmptyArgumentInCallByReference() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParameters/EmptyArgumentInCallByReference.after.kt");
@@ -829,6 +874,16 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/BinaryWithTypeExpressions.after.kt");
}
@TestMetadata("EmptyArgumentInCallByArrayAccess.after.kt")
public void testEmptyArgumentInCallByArrayAccess() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByArrayAccess.after.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration.after.kt")
public void testEmptyArgumentInCallByDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByDeclaration.after.kt");
}
@TestMetadata("InBinaryExpressionInMiddle.after.kt")
public void testInBinaryExpressionInMiddle() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/InBinaryExpressionInMiddle.after.kt");
@@ -1351,6 +1406,16 @@ public class TypingIndentationTestBaseGenerated extends AbstractTypingIndentatio
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/BinaryWithTypeExpressions.after.inv.kt");
}
@TestMetadata("EmptyArgumentInCallByArrayAccess.after.inv.kt")
public void testEmptyArgumentInCallByArrayAccess() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByArrayAccess.after.inv.kt");
}
@TestMetadata("EmptyArgumentInCallByDeclaration.after.inv.kt")
public void testEmptyArgumentInCallByDeclaration() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/EmptyArgumentInCallByDeclaration.after.inv.kt");
}
@TestMetadata("InBinaryExpressionInMiddle.after.inv.kt")
public void testInBinaryExpressionInMiddle() throws Exception {
runTest("idea/testData/indentationOnNewline/emptyParenthesisInBinaryExpression/InBinaryExpressionInMiddle.after.inv.kt");