Skip to content

Commit

Permalink
feat: Add util class to remove comments from blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
wsutina committed Nov 26, 2024
1 parent 877ea9a commit b8d6e82
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 6 deletions.
12 changes: 6 additions & 6 deletions core/src/main/kotlin/cash/grammar/kotlindsl/utils/Comments.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ public class Comments(
}

public fun getCommentsInBlock(ctx: KotlinParser.NamedBlockContext): List<Token> {
val comments = ArrayDeque<Token>()
var index = ctx.stop.tokenIndex
val comments = mutableListOf<Token>()
var index = ctx.start.tokenIndex

while (index > ctx.start.tokenIndex) {
var token = tokens.get(index)
while (index <= ctx.stop.tokenIndex) {
val token = tokens.get(index)

if (token.isComment()) {
comments.addFirst(token)
comments.add(token)
}
--index
++index
}

return comments
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package cash.grammar.kotlindsl.utils

import cash.grammar.kotlindsl.parse.KotlinParseException
import cash.grammar.kotlindsl.parse.Parser
import cash.grammar.kotlindsl.parse.Rewriter
import cash.grammar.kotlindsl.utils.Context.leafRule
import cash.grammar.kotlindsl.utils.Whitespace.trimGently
import cash.grammar.utils.ifNotEmpty
import com.squareup.cash.grammar.KotlinParser
import com.squareup.cash.grammar.KotlinParserBaseListener
import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.Token
import java.io.InputStream
import java.nio.file.Path

/**
* Removes comments from a specified block in a build script.
*
* Example:
* ```
* dependencies {
* /* This is a block comment
* that spans multiple lines */
* implementation("org.jetbrains.kotlin:kotlin-stdlib") // This is an inline comment
* // This is a single-line comment
* testImplementation("org.junit.jupiter:junit-jupiter")
* }
* ```
*
* The above script would be rewritten to:
* ```
* dependencies {
* implementation("org.jetbrains.kotlin:kotlin-stdlib")
* testImplementation("org.junit.jupiter:junit-jupiter")
* }
* ```
*/
public class CommentsInBlockRemover private constructor(
private val input: CharStream,
private val tokens: CommonTokenStream,
private val errorListener: CollectingErrorListener,
private val blockName: String,
) : KotlinParserBaseListener() {
private var terminalNewlines = 0
private val rewriter = Rewriter(tokens)
private val indent = Whitespace.computeIndent(tokens, input)
private val comments = Comments(tokens, indent)

@Throws(KotlinParseException::class)
public fun rewritten(): String {
errorListener.getErrorMessages().ifNotEmpty {
throw KotlinParseException.withErrors(it)
}

return rewriter.text.trimGently(terminalNewlines)
}

override fun exitNamedBlock(ctx: KotlinParser.NamedBlockContext) {
if (ctx.name().text == blockName) {
// Delete inline comments (a comment after a statement)
val allInlineComments = mutableListOf<Token>()
ctx.statements().statement().forEach {
val leafRule = it.leafRule()
val inlineComments = rewriter.deleteCommentsAndBlankSpaceToRight(leafRule.stop).orEmpty()
allInlineComments += inlineComments
}

val nonInlineComments = comments.getCommentsInBlock(ctx).subtract(allInlineComments)
nonInlineComments.forEach { token ->
rewriter.deleteWhitespaceToLeft(token)
rewriter.deleteNewlineToRight(token)
rewriter.delete(token)
}
}
}

public companion object {
public fun of(
buildScript: Path,
blockName: String,
): CommentsInBlockRemover {
return of(Parser.readOnlyInputStream(buildScript), blockName)
}

public fun of(
buildScript: String,
blockName: String,
): CommentsInBlockRemover {
return of(buildScript.byteInputStream(), blockName)
}

private fun of(
buildScript: InputStream,
blockName: String,
): CommentsInBlockRemover {
val errorListener = CollectingErrorListener()

return Parser(
file = buildScript,
errorListener = errorListener,
listenerFactory = { input, tokens, _ ->
CommentsInBlockRemover(
input = input,
tokens = tokens,
errorListener = errorListener,
blockName = blockName,
)
},
).listener()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cash.grammar.kotlindsl.utils

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class CommentsInBlockRemoverTest {
@Test
fun `remove all comments in the given block`() {
// Given
val buildScript = """
|dependencies {
| /* This is a block comment
| that spans multiple lines */
| implementation("org.jetbrains.kotlin:kotlin-stdlib") // This is an line comment
| // This is a single-line comment
| testImplementation("org.junit.jupiter:junit-jupiter")
| // This is another single-line comment
|}
|
|// This is project bar
|project.name = bar
|
|otherBlock {
| // More comments
|}
""".trimMargin()

// When
val rewrittenBuildScript = CommentsInBlockRemover.of(buildScript, "dependencies").rewritten()

// Then
assertThat(rewrittenBuildScript).isEqualTo("""
|dependencies {
| implementation("org.jetbrains.kotlin:kotlin-stdlib")
| testImplementation("org.junit.jupiter:junit-jupiter")
|}
|
|// This is project bar
|project.name = bar
|
|otherBlock {
| // More comments
|}
""".trimMargin())
}
}

0 comments on commit b8d6e82

Please sign in to comment.