Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Commit

Permalink
Finish support for proper formatting of file-level annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
hovinen committed Apr 5, 2021
1 parent b2903a3 commit 0c916f3
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.kotlin.formatter.scanning

import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.psi.psiUtil.children
import org.kotlin.formatter.ForceSynchronizedBreaksInBlockToken
import org.kotlin.formatter.ForcedBreakToken
import org.kotlin.formatter.Token
import org.kotlin.formatter.WhitespaceToken
import org.kotlin.formatter.scanning.nodepattern.NodePatternBuilder
import org.kotlin.formatter.scanning.nodepattern.nodePattern

/** A [NodeScanner] for the list of annotations at the top of a Kotlin file. */
internal class FileAnnotationListScanner(private val kotlinScanner: KotlinScanner) : NodeScanner {
private val nodePattern =
nodePattern {
zeroOrMore {
either {
simpleAnnotation() thenMapToTokens { nodes ->
kotlinScanner.scanNodes(nodes, ScannerState.STATEMENT)
}
} or {
nodeOfOneOfTypes(
KtNodeTypes.ANNOTATION,
KtNodeTypes.ANNOTATION_ENTRY
) thenMapToTokens { nodes ->
kotlinScanner.scanNodes(nodes, ScannerState.STATEMENT)
.plus(ForceSynchronizedBreaksInBlockToken)
}
}
possibleWhitespace() thenMapToTokens { nodes ->
if (nodes.firstOrNull()?.textContains('\n') == true) {
listOf(ForcedBreakToken(count = 1))
} else {
listOf(WhitespaceToken(" "))
}
}
zeroOrOne {
comment() thenMapTokens { tokens -> tokens.plus(ForcedBreakToken(count = 1)) }
}
possibleWhitespace()
}
end()
}

private fun NodePatternBuilder.simpleAnnotation(): NodePatternBuilder =
nodeMatching {
it.elementType == KtNodeTypes.ANNOTATION_ENTRY &&
it.lastChildNode.elementType == KtNodeTypes.CONSTRUCTOR_CALLEE
}

override fun scan(node: ASTNode, scannerState: ScannerState): List<Token> =
nodePattern.matchSequence(node.children().asIterable())
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ internal class NodeScannerProvider(
SimpleBlockScanner(kotlinScanner, ScannerState.PACKAGE_IMPORT, State.PACKAGE_IMPORT)
}
private val modifierListScanner = lazy { ModifierListScanner(kotlinScanner) }
private val fileAnnotationListScanner = lazy { FileAnnotationListScanner(kotlinScanner) }
private val returnScanner = lazy { ReturnScanner(kotlinScanner) }
private val throwScanner = lazy { ThrowScanner(kotlinScanner) }
private val tryScanner = lazy { TryScanner(kotlinScanner) }
Expand Down Expand Up @@ -121,8 +122,9 @@ internal class NodeScannerProvider(
KtNodeTypes.LABELED_EXPRESSION -> labeledExpressionScanner.value
KtNodeTypes.ANNOTATED_EXPRESSION -> annotatedExpressionScanner.value
KtFileElementType.INSTANCE -> kotlinFileScanner.value
KtFileElementType.INSTANCE, is KtScriptElementType,
KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY -> simpleScannerForBlock.value
is KtScriptElementType, KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY ->
simpleScannerForBlock.value
KtNodeTypes.FILE_ANNOTATION_LIST -> fileAnnotationListScanner.value
KtNodeTypes.TYPE_REFERENCE -> typeReferenceScanner.value
KtNodeTypes.WHEN_ENTRY, KtNodeTypes.ANNOTATION_ENTRY, KtNodeTypes.PREFIX_EXPRESSION,
KtNodeTypes.VALUE_PARAMETER, KtNodeTypes.SUPER_TYPE_ENTRY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3772,7 +3772,7 @@ class KotlinFormatterTest {
.isEqualTo(
"""
@file:AnAnnotation
package apackage
import anotherpackage.AClass
Expand All @@ -3787,7 +3787,62 @@ class KotlinFormatterTest {
.format(
"""
@file:AnAnnotation // A comment
package apackage
import anotherpackage.AClass
""".trimIndent()
)

assertThat(result)
.isEqualTo(
"""
@file:AnAnnotation // A comment
package apackage
import anotherpackage.AClass
""".trimIndent()
)
}

@Test
fun `adds newline between file annotations with attributes`() {
val result =
KotlinFormatter(importPolicySupplier = { { _, _ -> true } })
.format(
"""
@file:AnAnnotation("A value")
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
""".trimIndent()
)

assertThat(result)
.isEqualTo(
"""
@file:AnAnnotation("A value")
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
""".trimIndent()
)
}

@Test
fun `preserves comments between file annotations`() {
val result =
KotlinFormatter(importPolicySupplier = { { _, _ -> true } })
.format(
"""
@file:AnAnnotation // A comment
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
Expand All @@ -3798,7 +3853,38 @@ class KotlinFormatterTest {
.isEqualTo(
"""
@file:AnAnnotation // A comment
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
""".trimIndent()
)
}

@Test
fun `preserves block between file annotations`() {
val result =
KotlinFormatter(importPolicySupplier = { { _, _ -> true } })
.format(
"""
@file:AnAnnotation
/* A comment */
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
""".trimIndent()
)

assertThat(result)
.isEqualTo(
"""
@file:AnAnnotation
/* A comment */
@file:AnotherAnnotation
package apackage
import anotherpackage.AClass
Expand All @@ -3823,7 +3909,7 @@ class KotlinFormatterTest {
.isEqualTo(
"""
@file:[AnAnnotation AnotherAnnotation]
package apackage
import anotherpackage.AClass
Expand Down

0 comments on commit 0c916f3

Please sign in to comment.