Skip to content

Commit

Permalink
Introduce compose helper for building document; expose reformat h…
Browse files Browse the repository at this point in the history
…elper method; tests
  • Loading branch information
hsz committed Oct 19, 2022
1 parent e85a7a6 commit 3ead4cb
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 171 deletions.
174 changes: 82 additions & 92 deletions src/main/kotlin/org/jetbrains/changelog/Changelog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,104 +29,94 @@ data class Changelog(
private val flavour = ChangelogFlavourDescriptor()
private val parser = MarkdownParser(flavour)

val content
get() = file.run {
if (!exists()) {
throw MissingFileException(canonicalPath)
}
readText()
val content = file.run {
if (!exists()) {
throw MissingFileException(canonicalPath)
}
private val tree
get() = parser.buildMarkdownTreeFromString(content)

private val preTitleNodes
get() = tree.children
.takeWhile { it.type != MarkdownElementTypes.ATX_1 }
val preTitleValue
get() = preTitle ?: preTitleNodes
.joinToString(NEW_LINE) { it.text() }
.trim()

private val titleNodes
get() = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_1 }
.takeWhile { it.type == MarkdownElementTypes.ATX_1 }
val titleValue
get() = title ?: titleNodes
.joinToString(NEW_LINE) { it.text() }
.trim()

private val introductionNodes
get() = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_1 }
.dropWhile { it.type == MarkdownElementTypes.ATX_1 }
.takeWhile { it.type != MarkdownElementTypes.ATX_2 }
val introductionValue
get() = introduction ?: introductionNodes
.joinToString(NEW_LINE) { it.text() }
.reformat()

private val itemsNodes
get() = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_2 }
private val items
get() = itemsNodes
.groupByType(MarkdownElementTypes.ATX_2) {
it.children.last().text().trim().run {
when (this) {
unreleasedTerm -> this
else -> split("""[^-+.0-9a-zA-Z]+""".toRegex()).firstOrNull(
headerParserRegex::matches
) ?: throw HeaderParseException(this, unreleasedTerm)
}
readText()
}
private val tree = parser.buildMarkdownTreeFromString(content)

private val preTitleNodes = tree.children
.takeWhile { it.type != MarkdownElementTypes.ATX_1 }
val preTitleValue = preTitle ?: preTitleNodes
.joinToString(NEW_LINE) { it.text() }
.trim()

private val titleNodes = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_1 }
.takeWhile { it.type == MarkdownElementTypes.ATX_1 }
val titleValue = title ?: titleNodes
.joinToString(NEW_LINE) { it.text() }
.trim()

private val introductionNodes = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_1 }
.dropWhile { it.type == MarkdownElementTypes.ATX_1 }
.takeWhile { it.type != MarkdownElementTypes.ATX_2 }
val introductionValue = introduction ?: introductionNodes
.joinToString(NEW_LINE) { it.text() }
.reformat()

private val itemsNodes = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_2 }
private val items = itemsNodes
.groupByType(MarkdownElementTypes.ATX_2) {
it.children.last().text().trim().run {
when (this) {
unreleasedTerm -> this
else -> split("""[^-+.0-9a-zA-Z]+""".toRegex()).firstOrNull(
headerParserRegex::matches
) ?: throw HeaderParseException(this, unreleasedTerm)
}
}
.filterKeys(String::isNotEmpty)
.mapKeys {
headerParserRegex.matchEntire(it.key)?.run {
groupValues.drop(1).firstOrNull()
} ?: it.key
}
.mapValues { (key, value) ->
val header = value
.firstOrNull { it.type == MarkdownElementTypes.ATX_2 }?.text()
.orEmpty()
.trim()

val nodes = value
.drop(1)
.dropWhile { node -> node.type == MarkdownTokenTypes.EOL }

val isUnreleased = key == unreleasedTerm
val summaryNodes = nodes
.takeWhile { node ->
node.type != MarkdownElementTypes.ATX_3 && !node.text().startsWith(itemPrefix)
}
val summary = summaryNodes
.joinToString(NEW_LINE) { it.text() }
.reformat()

val items = nodes
.drop(summaryNodes.size)
.groupByType(MarkdownElementTypes.ATX_3) {
it.text().trimStart('#').trim()
}
.mapValues { section ->
section.value
.map { it.text().trim() }
.filterNot { it.startsWith(ATX_3) || it.isEmpty() }
.joinToString(NEW_LINE)
.split("""(^|$NEW_LINE)${Regex.escape(itemPrefix)}\s*""".toRegex())
.mapNotNull {
"$itemPrefix $it".takeIf { _ ->
it.isNotEmpty()
}
}
.filterKeys(String::isNotEmpty)
.mapKeys {
headerParserRegex.matchEntire(it.key)?.run {
groupValues.drop(1).firstOrNull()
} ?: it.key
}
.mapValues { (key, value) ->
val header = value
.firstOrNull { it.type == MarkdownElementTypes.ATX_2 }?.text()
.orEmpty()
.trim()

val nodes = value
.drop(1)
.dropWhile { node -> node.type == MarkdownTokenTypes.EOL }

val isUnreleased = key == unreleasedTerm
val summaryNodes = nodes
.takeWhile { node ->
node.type != MarkdownElementTypes.ATX_3 && !node.text().startsWith(itemPrefix)
}
val summary = summaryNodes
.joinToString(NEW_LINE) { it.text() }
.reformat()

val items = nodes
.drop(summaryNodes.size)
.groupByType(MarkdownElementTypes.ATX_3) {
it.text().trimStart('#').trim()
}
.mapValues { section ->
section.value
.map { it.text().trim() }
.filterNot { it.startsWith(ATX_3) || it.isEmpty() }
.joinToString(NEW_LINE)
.split("""(^|$NEW_LINE)${Regex.escape(itemPrefix)}\s*""".toRegex())
.mapNotNull {
"$itemPrefix $it".takeIf { _ ->
it.isNotEmpty()
}
}

}
}

Item(key, header, summary, items, isUnreleased)
}
Item(key, header, summary, items, isUnreleased)
}

fun has(version: String) = items.containsKey(version)

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/org/jetbrains/changelog/ChangelogPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import java.io.File

class ChangelogPlugin : Plugin<Project> {

private val field: Long? = null //End-Of-Line Comments

override fun apply(project: Project) {
checkGradleVersion(project)

Expand Down
57 changes: 51 additions & 6 deletions src/main/kotlin/org/jetbrains/changelog/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package org.jetbrains.changelog
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_1
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_2
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_3
import org.jetbrains.changelog.ChangelogPluginConstants.NEW_LINE
import org.jetbrains.changelog.flavours.ChangelogFlavourDescriptor
import org.jetbrains.changelog.flavours.PlainTextFlavourDescriptor
Expand All @@ -23,9 +25,52 @@ fun markdownToPlainText(input: String) = PlainTextFlavourDescriptor().run {
.generateHtml(PlainTextTagRenderer())
}

internal fun String.reformat() = this
.replace("""$NEW_LINE{3,}""".toRegex(), NEW_LINE + NEW_LINE)
.replace("""($NEW_LINE$ATX_1.*?)$NEW_LINE+""".toRegex(), "$1$NEW_LINE")
.replace("""($NEW_LINE$ATX_1.*?)$NEW_LINE+""".toRegex(), "$1$NEW_LINE")
.replace("""($ATX_1+ .*?$NEW_LINE)$ATX_1""".toRegex(), "$1$NEW_LINE$ATX_1")
.trim() + NEW_LINE
fun String.reformat(): String {
val result = listOf(
"""(?:^|\n)+(#+ [^\n]*)\n*""".toRegex() to "\n\n$1\n",
"""((?:^|\n)#+ .*?)\n(#+ )""".toRegex() to "$1\n\n$2",
"""\n{3,}""".toRegex() to "\n\n",
).fold(this) { acc, (pattern, replacement) ->
acc.replace(pattern, replacement)
}.trim() + NEW_LINE

return when (result) {
this -> result
else -> result.reformat()
}

}

internal fun compose(
preTitle: String?,
title: String?,
introduction: String?,
unreleasedTerm: String?,
groups: List<String>,
function: suspend SequenceScope<String>.() -> Unit = {},
) = sequence {
if (!preTitle.isNullOrBlank()) {
yield(preTitle)
yield(NEW_LINE)
}
if (!title.isNullOrBlank()) {
yield("$ATX_1 ${title.trim()}")
yield(NEW_LINE)
}
if (!introduction.isNullOrBlank()) {
yield(introduction)
yield(NEW_LINE)
}

if (!unreleasedTerm.isNullOrBlank()) {
yield("$ATX_2 $unreleasedTerm")

groups
.map { "$ATX_3 $it" }
.let { yieldAll(it) }
}

function()
}
.joinToString(NEW_LINE)
.reformat()
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ abstract class GetChangelogTask : DefaultTask() {
@TaskAction
fun run() = logger.quiet(
Changelog(
inputFile.get().asFile,
inputFile.map { it.asFile }.get(),
null,
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_1
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_2
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_3
import org.jetbrains.changelog.ChangelogPluginConstants.NEW_LINE
import org.jetbrains.changelog.reformat
import org.jetbrains.changelog.compose

abstract class InitializeChangelogTask : DefaultTask() {

Expand Down Expand Up @@ -61,30 +57,13 @@ abstract class InitializeChangelogTask : DefaultTask() {
throw GradleException("Changelog file is not empty: ${file.absolutePath}")
}

sequence {
if (preTitle.isPresent) {
yield(preTitle.get())
yield(NEW_LINE)
}
if (title.isPresent) {
yield("$ATX_1 ${title.get()}")
yield(NEW_LINE)
}
if (introduction.isPresent) {
yield(introduction.get())
yield(NEW_LINE)
}

yield("$ATX_2 ${unreleasedTerm.get()}")

groups.get()
.map { "$ATX_3 $it" }
.let { yieldAll(it) }
}
.joinToString(NEW_LINE)
.reformat()
.let {
file.writeText(it)
}
val content = compose(
preTitle.orNull,
title.orNull,
introduction.orNull,
unreleasedTerm.get(),
groups.get(),
)
file.writeText(content)
}
}
Loading

0 comments on commit 3ead4cb

Please sign in to comment.