Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix indentation of delegated super type call entry #1305

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- Do not remove import which is used as markdown link in KDoc only (`no-unused-imports`) ([#1282](https://github.com/pinterest/ktlint/issues/1282))
- Fix indentation of secondary constructor (`indent`) ([#1222](https://github.com/pinterest/ktlint/issues/1222))
- Fix alignment of arrow when trailing comma is missing in when entry (`trailing-comma`) ([#1312](https://github.com/pinterest/ktlint/issues/1312))
- Fix indent of delegated super type entry (`indent`) ([#1210](https://github.com/pinterest/ktlint/issues/1210))

### Changed
- Update Kotlin version to `1.6.0` release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,12 +518,16 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast {
val pairedLeft = n.pairedLeft()
if (prevBlockLine != blockLine && !pairedLeft.isAfterLambdaArgumentOnSameLine()) {
expectedIndent--
debug { "--on(${n.elementType}) -> $expectedIndent" }

val byKeywordOnSameLine = pairedLeft?.prevLeafOnSameLine(BY_KEYWORD)
if (byKeywordOnSameLine != null &&
byKeywordOnSameLine.prevLeaf()?.isWhiteSpaceWithNewline() == true &&
n.leavesOnSameLine(forward = true).all { it.isWhiteSpace() || it.isPartOfComment() }
) expectedIndent--
debug { "--${n.text} -> $expectedIndent" }
) {
expectedIndent--
debug { "--on same line as by keyword ${n.text} -> $expectedIndent" }
}
}
}
LT ->
Expand Down Expand Up @@ -641,6 +645,7 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast {
visitWhiteSpace(n, autoCorrect, emit, editorConfig)
if (ctx.localAdj != 0) {
expectedIndent += ctx.localAdj
debug { "++${ctx.localAdj} on whitespace containing new line (${n.elementType}) -> $expectedIndent" }
ctx.localAdj = 0
}
} else if (n.isPartOf(KDOC)) {
Expand Down Expand Up @@ -734,8 +739,15 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast {
}

private fun adjustExpectedIndentAfterSuperTypeList(n: ASTNode) {
expectedIndent--
debug { "--after(${n.elementType}) -> $expectedIndent" }
val byKeywordLeaf = n
.findChildByType(DELEGATED_SUPER_TYPE_ENTRY)
?.findChildByType(BY_KEYWORD)
if (n.prevLeaf()?.textContains('\n') == true && byKeywordLeaf?.prevLeaf().isWhiteSpaceWithNewline()) {
Unit
} else {
expectedIndent--
debug { "--after(${n.elementType}) -> $expectedIndent" }
}
}

private fun adjustExpectedIndentInsideSuperTypeCall(n: ASTNode, ctx: IndentContext) {
Expand Down Expand Up @@ -997,13 +1009,17 @@ class IndentationRule : Rule("indent"), Rule.Modifier.RestrictToRootLast {
// instead of expected
// val i: Int
// by lazy { 1 }
nextLeafElementType == BY_KEYWORD ->
if (node.isPartOf(DELEGATED_SUPER_TYPE_ENTRY)) {
nextLeafElementType == BY_KEYWORD -> {
if (node.isPartOf(DELEGATED_SUPER_TYPE_ENTRY) &&
node.treeParent.prevLeaf()?.textContains('\n') == true
) {
0
} else {
expectedIndent++
debug { "++whitespace followed by BY keyword -> $expectedIndent" }
1
}
}
// IDEA quirk:
// var value: DataClass =
// DataClass("too long line")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -952,23 +952,42 @@ internal class IndentationRuleTest {
}

@Test
fun `lint delegation 2`() {
assertThat(
IndentationRule().lint(
"""
interface Foo
fun `lint and format delegation 2`() {
val code =
"""
interface Foo

class Bar(a: Int, b: Int, c: Int) : Foo
class Bar(a: Int, b: Int, c: Int) : Foo

class Test2 : Foo
by Bar(
a = 1,
b = 2,
c = 3
)
""".trimIndent()
val formattedCode =
"""
interface Foo

class Test2 : Foo
class Bar(a: Int, b: Int, c: Int) : Foo

class Test2 : Foo
by Bar(
a = 1,
b = 2,
c = 3
)
""".trimIndent()
)
).isEmpty()
""".trimIndent()

assertThat(IndentationRule().lint(code)).containsExactly(
LintError(6, 1, "indent", "Unexpected indentation (0) (should be 4)"),
LintError(7, 1, "indent", "Unexpected indentation (4) (should be 8)"),
LintError(8, 1, "indent", "Unexpected indentation (4) (should be 8)"),
LintError(9, 1, "indent", "Unexpected indentation (4) (should be 8)"),
LintError(10, 1, "indent", "Unexpected indentation (0) (should be 4)"),
)
assertThat(IndentationRule().format(code)).isEqualTo(formattedCode)
}

@Test
Expand Down Expand Up @@ -1556,6 +1575,67 @@ internal class IndentationRuleTest {
)
}

@Test
fun `Issue 1210 - format supertype delegate`() {
val code =
"""
object ApplicationComponentFactory : ApplicationComponent.Factory
by DaggerApplicationComponent.factory()
""".trimIndent()
val formattedCode =
"""
object ApplicationComponentFactory : ApplicationComponent.Factory
by DaggerApplicationComponent.factory()
""".trimIndent()
assertThat(IndentationRule().lint(code)).containsExactly(
LintError(2, 1, "indent", "Unexpected indentation (0) (should be 4)")
)
assertThat(IndentationRule().format(code)).isEqualTo(formattedCode)
}

@Test
fun `Issue 1210 - format of statements after supertype delegated entry 1`() {
val code =
"""
object Issue1210 : ApplicationComponent.Factory
by DaggerApplicationComponent.factory()

// The next line ensures that the fix regarding the expectedIndex due to alignment of "by" keyword in
// class above, is still in place. Without this fix, the expectedIndex would hold a negative value,
// resulting in the formatting to crash on the next line.
val bar = 1
""".trimIndent()

assertThat(IndentationRule().lint(code)).isEmpty()
assertThat(IndentationRule().format(code)).isEqualTo(code)
}

@Test
fun `Issue 1210 - format of statements after supertype delegated entry 2`() {
val code =
"""
interface Foo

class Bar(a: Int, b: Int, c: Int) : Foo

class Test4 :
Foo
by Bar(
a = 1,
b = 2,
c = 3
)

// The next line ensures that the fix regarding the expectedIndex due to alignment of "by" keyword in
// class above, is still in place. Without this fix, the expectedIndex would hold a negative value,
// resulting in the formatting to crash on the next line.
val bar = 1
""".trimIndent()

assertThat(IndentationRule().lint(code)).isEmpty()
assertThat(IndentationRule().format(code)).isEqualTo(code)
}

private companion object {
const val MULTILINE_STRING_QUOTE = "${'"'}${'"'}${'"'}"
const val TAB = "${'\t'}"
Expand Down