Skip to content

Commit

Permalink
feat: Change log DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
jmongard committed Sep 24, 2023
1 parent 97b2d0b commit 62b15e0
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 351 deletions.
13 changes: 0 additions & 13 deletions src/main/kotlin/git/semver/plugin/changelog/ChangeLogContext.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,84 @@ package git.semver.plugin.changelog

import java.util.*

open class MarkDownDocumentBuilder {
open class ChangeLogDocumentBuilder(
private val context: ChangeLogFormatter,
private val commitInfos: List<ChangeLogFormatter.CommitInfo>
) {
val out = StringBuilder()

fun build(): String {
return out.toString()
}

fun heading1(text: String?) {
out.append("# ").appendLine(text)
fun append(t: String?): ChangeLogDocumentBuilder {
out.append(t)
return this
}

fun heading2(text: String?) {
out.append("## ").appendLine(text)
fun appendLine(t: String? = ""): ChangeLogDocumentBuilder {
out.appendLine(t)
return this
}

fun heading3(text: String?) {
out.append("### ").appendLine(text)
fun format(block: ChangeLogTextFormatter.() -> String) {
for (commitInfo in commitInfos()) {
context.flagCommit(commitInfo)
val builder = ChangeLogTextFormatter(commitInfo)
out.appendLine(builder.block())
}
}

fun heading4(text: String?) {
out.append("#### ").appendLine(text)
fun skip() {
for (commitInfo in commitInfos()) {
context.flagCommit(commitInfo)
}
}

fun append(t: String?): MarkDownDocumentBuilder {
out.append(t)
return this
//<editor-fold desc="filter">
fun withBreakingChanges(block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.isBreaking }, block)
}

fun appendLine(t: String? = ""): MarkDownDocumentBuilder {
out.appendLine(t)
return this
fun withScope(vararg scopes: String, block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.scope in scopes }, block)
}
}

open class ChangeLogDocumentBuilder(
private val commitInfos: List<ChangeLogFormatter.CommitInfo>,
private val context: ChangeLogContext
) : MarkDownDocumentBuilder() {
fun breakingChanges(block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { it.isBreaking }
fun withScope(block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.scope != null }, block)
}

fun withScopes(vararg scopes: String, block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { it.scope in scopes }
fun withoutScope(block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.scope == null }, block)
}

fun withoutScope(block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { it.scope == null }
fun withType(vararg types: String, block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.type in types }, block)
}

fun withTypes(vararg types: String, block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { it.type in types }
fun withType(block: ChangeLogDocumentBuilder.() -> Unit) {
with({ it.type != null }, block)
}

fun withoutType(block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { it.type == null }
with({ it.type == null }, block)
}

fun otherwise(block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block) { true }
with({ true }, block)
}

fun with(filter: (ChangeLogFormatter.CommitInfo) -> Boolean, block: ChangeLogDocumentBuilder.() -> Unit) {
filter(block, filter)
}

private fun filter(block: ChangeLogDocumentBuilder.() -> Unit, filter: (ChangeLogFormatter.CommitInfo) -> Boolean) {
val filteredCommits = commitInfos().filter(filter)
if (filteredCommits.isNotEmpty()) {
val builder = ChangeLogDocumentBuilder(filteredCommits, context)
val builder = ChangeLogDocumentBuilder(context, filteredCommits)
builder.block()
out.appendLine(builder.build())
out.append(builder.build())
}
}
//</editor-fold>

//<editor-fold desc="grouping">
fun groupByScope(block: ChangeLogGroupBuilder.() -> Unit) {
groupBy({ it.scope ?: "" }, block)
}
Expand All @@ -85,52 +88,31 @@ open class ChangeLogDocumentBuilder(
groupBy({ it.type ?: "" }, block)
}

fun groupBy(group: (ChangeLogFormatter.CommitInfo) -> String, block: ChangeLogGroupBuilder.() -> Unit) {
processGroups(commitInfos().groupBy(group), block)
fun groupBy(keySelector: (ChangeLogFormatter.CommitInfo) -> String, block: ChangeLogGroupBuilder.() -> Unit) {
processGroups(commitInfos().groupBy(keySelector), block)
}

fun groupBySorted(group: (ChangeLogFormatter.CommitInfo) -> String, block: ChangeLogGroupBuilder.() -> Unit) {
processGroups(commitInfos().groupByTo(TreeMap(), group), block)
fun groupBySorted(keySelector: (ChangeLogFormatter.CommitInfo) -> String, block: ChangeLogGroupBuilder.() -> Unit) {
processGroups(commitInfos().groupByTo(TreeMap(), keySelector), block)
}

private fun processGroups(
groupedByScope: Map<String, List<ChangeLogFormatter.CommitInfo>>,
block: ChangeLogGroupBuilder.() -> Unit
) {
for ((key, scopeCommits) in groupedByScope.filterKeys { it.isNotEmpty() }) {
processGroup(scopeCommits, key, block)
}
}

private fun processGroup(
scopeCommits: List<ChangeLogFormatter.CommitInfo>,
group: String,
block: ChangeLogGroupBuilder.() -> Unit
) {
val builder = ChangeLogGroupBuilder(scopeCommits, context, group)
block(builder)
out.appendLine(builder.build())
}

fun formatEach(block: ChangeLogTextFormatter.() -> String) {
for (commitInfo in commitInfos()) {
context.flagCommit(commitInfo)
val builder = ChangeLogTextFormatter(commitInfo)
out.appendLine(builder.block())
}
}

fun skip() {
for (commitInfo in commitInfos()) {
context.flagCommit(commitInfo)
val builder = ChangeLogGroupBuilder(context, scopeCommits, key)
block(builder)
out.append(builder.build())
}
}
//</editor-fold>

private fun commitInfos() = commitInfos.filter { !context.isCommitFlagged(it) }
}

class ChangeLogGroupBuilder(
context: ChangeLogFormatter,
commitInfos: List<ChangeLogFormatter.CommitInfo>,
flagManager: ChangeLogContext,
val group: String
) : ChangeLogDocumentBuilder(commitInfos, flagManager)
) : ChangeLogDocumentBuilder(context, commitInfos)
113 changes: 25 additions & 88 deletions src/main/kotlin/git/semver/plugin/changelog/ChangeLogFormatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package git.semver.plugin.changelog

import git.semver.plugin.scm.Commit
import git.semver.plugin.semver.SemverSettings
import java.util.TreeMap

private const val SCOPE = "Scope"
private const val TYPE = "Type"
Expand All @@ -12,104 +11,42 @@ class ChangeLogFormatter(private val settings: SemverSettings, private val forma

private val changeLogRegex = format.changeLogPattern.toRegex(SemverSettings.REGEX_OPTIONS)

internal fun formatLog(changeLog: List<Commit>): String {
val infos = commitInfos(changeLog)
private val flaggedCommits = mutableSetOf<CommitInfo>()

return formatLog(infos)
fun flagCommit(commit: CommitInfo) {
flaggedCommits.add(commit)
}

private fun formatLog(infos: List<CommitInfo>): String {
val groupedByHeading = TreeMap<String, MutableSet<String>>()

infos.sortedBy { it.text }.forEach { addCommit(it, groupedByHeading) }

val builder = StringBuilder()
addStaticText(builder, format.header)
groupedByHeading.forEach { (heading, items) ->
addStaticText(builder, format.groupStart)
builder.appendLine().appendLine(heading)
items.forEach { item ->
builder
.append(format.changePrefix)
.append(format.changeLineSeparator?.let { item.lineSequence().joinToString(it) }
?: item.lineSequence().first())
.appendLine(format.changePostfix)
}
addStaticText(builder, format.groupEnd)
}

addStaticText(builder, format.footer)
return builder.trim().toString()
fun isCommitFlagged(commit: CommitInfo): Boolean {
return commit in flaggedCommits
}

fun commitInfos(changeLog: List<Commit>): List<CommitInfo> {
return changeLog.groupBy { it.text }.map { commitInfo(it.key, it.value) }
fun formatLog(changeLog: List<Commit>): String {
return formatLog(changeLog, format.builder)
}

private fun commitInfo(commitText: String, commits: List<Commit>): CommitInfo {
val isBreaking = settings.majorRegex.containsMatchIn(commitText)
val match = changeLogRegex.find(commitText)
return if (match != null) {
CommitInfo(
commits,
commitText,
isBreaking,
match.groupValue(TYPE),
match.groupValue(SCOPE),
match.groupValue(MESSAGE)
)
} else {
CommitInfo(commits, commitText, isBreaking)
}
}

private fun addCommit(
commit: CommitInfo,
resultMap: MutableMap<String, MutableSet<String>>
) {
fun addChangeLogText(category: String?, message: String, scope: String? = null) {
if (!category.isNullOrEmpty()) {
resultMap.computeIfAbsent(category) { mutableSetOf() }.add(formatLine(scope, message, commit).trim())
fun formatLog(changeLog: List<Commit>, block: ChangeLogDocumentBuilder.() -> Unit): String {
val infos = changeLog.groupBy { it.text }.map {
val isBreaking = settings.majorRegex.containsMatchIn(it.key)
val match = changeLogRegex.find(it.key)
if (match != null) {
CommitInfo(
it.value,
it.key,
isBreaking,
match.groupValue(TYPE),
match.groupValue(SCOPE),
match.groupValue(MESSAGE)
)
} else {
CommitInfo(it.value, it.key, isBreaking)
}
}

val text = commit.text
if (format.breakingChangeHeader.isNotEmpty() && commit.isBreaking) {
addChangeLogText(format.breakingChangeHeader, text)
return
}

if (commit.type == null) {
addChangeLogText(format.missingTypeHeader, text)
return
}

val scope = commit.scope
val scopeHeading = scope?.let { format.headerTexts[it] }
if (scopeHeading != null) {
addChangeLogText(scopeHeading, text)
return
}

val typeHeading = format.headerTexts[commit.type]
if (typeHeading != null) {
addChangeLogText(typeHeading, commit.message!!, scope)
return
}

addChangeLogText(format.otherChangeHeader, text)
val changeLogDocumentBuilder = ChangeLogDocumentBuilder(this, infos)
changeLogDocumentBuilder.block()
return changeLogDocumentBuilder.build()
}

private fun formatLine(scope: String?, message: String, commitInfo: CommitInfo) = commitInfo.commits
.map { it.sha.take(format.changeShaLength) }
.filter { it.isNotEmpty() }
.joinToString(" ", "", " ") +
scope?.let { "$it: " }.orEmpty() +
message

private fun addStaticText(builder: StringBuilder, text: String?) =
text?.let(builder::appendLine)

private fun MatchResult.groupValue(groupId: String) = this.groups[groupId]?.value

class CommitInfo(
Expand Down
Loading

0 comments on commit 62b15e0

Please sign in to comment.