Skip to content

Commit

Permalink
Added the changelog.lineSeparator property to allow for customizing…
Browse files Browse the repository at this point in the history
… the line separator used in the changelog. #104
  • Loading branch information
hsz committed Oct 19, 2022
1 parent 3ead4cb commit f732ac8
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 81 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Introduce changelog `summary` and changelog property [#127](../../issues/127)
- Introduce changelog `preTitle` and `title` changelog properties
- Ensure patched changelog ends with a newline [#126](../../issues/126)
- Added the `changelog.lineSeparator` property to allow for customizing the line separator used in the changelog. [#104](../../issues/104)
- Added the `--version=...` CLI parameter for the `getChangelog` task [#83](../../issues/83)
- Throw an exception when `initializeChangelog` task works on non-empty file [#82](../../issues/82)

Expand Down
31 changes: 16 additions & 15 deletions src/main/kotlin/org/jetbrains/changelog/Changelog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.parser.MarkdownParser
import org.jetbrains.changelog.ChangelogPluginConstants.ATX_3
import org.jetbrains.changelog.ChangelogPluginConstants.NEW_LINE
import org.jetbrains.changelog.exceptions.HeaderParseException
import org.jetbrains.changelog.exceptions.MissingFileException
import org.jetbrains.changelog.exceptions.MissingVersionException
Expand All @@ -24,6 +23,7 @@ data class Changelog(
val unreleasedTerm: String,
val headerParserRegex: Regex,
val itemPrefix: String,
val lineSeparator: String,
) {

private val flavour = ChangelogFlavourDescriptor()
Expand All @@ -40,23 +40,23 @@ data class Changelog(
private val preTitleNodes = tree.children
.takeWhile { it.type != MarkdownElementTypes.ATX_1 }
val preTitleValue = preTitle ?: preTitleNodes
.joinToString(NEW_LINE) { it.text() }
.joinToString(lineSeparator) { 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() }
.joinToString(lineSeparator) { 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()
.joinToString(lineSeparator) { it.text() }
.reformat(lineSeparator)

private val itemsNodes = tree.children
.dropWhile { it.type != MarkdownElementTypes.ATX_2 }
Expand Down Expand Up @@ -93,8 +93,8 @@ data class Changelog(
node.type != MarkdownElementTypes.ATX_3 && !node.text().startsWith(itemPrefix)
}
val summary = summaryNodes
.joinToString(NEW_LINE) { it.text() }
.reformat()
.joinToString(lineSeparator) { it.text() }
.reformat(lineSeparator)

val items = nodes
.drop(summaryNodes.size)
Expand All @@ -105,8 +105,8 @@ data class Changelog(
section.value
.map { it.text().trim() }
.filterNot { it.startsWith(ATX_3) || it.isEmpty() }
.joinToString(NEW_LINE)
.split("""(^|$NEW_LINE)${Regex.escape(itemPrefix)}\s*""".toRegex())
.joinToString(lineSeparator)
.split("""(^|$lineSeparator)${Regex.escape(itemPrefix)}\s*""".toRegex())
.mapNotNull {
"$itemPrefix $it".takeIf { _ ->
it.isNotEmpty()
Expand All @@ -115,7 +115,7 @@ data class Changelog(

}

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

fun has(version: String) = items.containsKey(version)
Expand All @@ -132,6 +132,7 @@ data class Changelog(
val summary: String,
private val items: Map<String, List<String>>,
private val isUnreleased: Boolean = false,
private val lineSeparator: String,
) {

private var withHeader = true
Expand Down Expand Up @@ -161,7 +162,7 @@ data class Changelog(

if (withSummary && summary.isNotEmpty()) {
yield(summary)
yield(NEW_LINE)
yield(lineSeparator)
}

getSections()
Expand All @@ -183,17 +184,17 @@ data class Changelog(
}

if (hasNext()) {
yield(NEW_LINE)
yield(lineSeparator)
}
}
}
}
.joinToString(NEW_LINE)
.reformat()
.joinToString(lineSeparator)
.reformat(lineSeparator)

fun toHTML() = markdownToHTML(toText())

fun toPlainText() = markdownToPlainText(toText())
fun toPlainText() = markdownToPlainText(toText(), lineSeparator)

override fun toString() = toText()
}
Expand Down
24 changes: 22 additions & 2 deletions src/main/kotlin/org/jetbrains/changelog/ChangelogPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import org.jetbrains.changelog.tasks.GetChangelogTask
import org.jetbrains.changelog.tasks.InitializeChangelogTask
import org.jetbrains.changelog.tasks.PatchChangelogTask
import java.io.File
import java.nio.file.Files
import java.nio.file.Path

class ChangelogPlugin : Plugin<Project> {

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

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

Expand All @@ -46,6 +46,23 @@ class ChangelogPlugin : Plugin<Project> {
}
)
title.convention(ChangelogPluginConstants.DEFAULT_TITLE)
lineSeparator.convention(path.map { path ->
val content = Path.of(path)
.takeIf { Files.exists(it) }
?.let { Files.readString(it) }
?: return@map "\n"
val rnless = content.replace("\r\n", "")

val rn = (content.length - rnless.length) / 2
val r = rnless.count { it == '\r' }
val n = rnless.count { it == '\n' }

when {
rn > r && rn > n -> "\r\n"
r > n -> "\r"
else -> "\n"
}
})
}

val pathProvider = project.layout.file(extension.path.map { File(it) })
Expand All @@ -58,6 +75,7 @@ class ChangelogPlugin : Plugin<Project> {
itemPrefix.convention(extension.itemPrefix)
unreleasedTerm.set(extension.unreleasedTerm)
version.set(extension.version)
lineSeparator.convention(extension.lineSeparator)

outputs.upToDateWhen { false }
}
Expand All @@ -78,6 +96,7 @@ class ChangelogPlugin : Plugin<Project> {
patchEmpty.convention(extension.patchEmpty)
unreleasedTerm.convention(extension.unreleasedTerm)
version.convention(extension.version)
lineSeparator.convention(extension.lineSeparator)
}

project.tasks.register<InitializeChangelogTask>(INITIALIZE_CHANGELOG_TASK_NAME) {
Expand All @@ -91,6 +110,7 @@ class ChangelogPlugin : Plugin<Project> {
outputFile.convention(pathProvider)
itemPrefix.set(extension.itemPrefix)
unreleasedTerm.set(extension.unreleasedTerm)
lineSeparator.convention(extension.lineSeparator)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ object ChangelogPluginConstants {
const val ITEM_PREFIX = "-"
const val DEFAULT_TITLE = "Changelog"
const val UNRELEASED_TERM = "[Unreleased]"
const val NEW_LINE = "\n"
const val ATX_1 = "#"
const val ATX_2 = "##"
const val ATX_3 = "###"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,19 @@ abstract class ChangelogPluginExtension {
@get:Optional
abstract val version: Property<String>

@get:Optional
abstract val lineSeparator: Property<String>

val instance
get() = Changelog(
File(path.get()),
preTitle.orNull,
title.orNull,
introduction.orNull,
unreleasedTerm.get(),
getHeaderParserRegex.get(),
itemPrefix.get(),
file = File(path.get()),
preTitle = preTitle.orNull,
title = title.orNull,
introduction = introduction.orNull,
unreleasedTerm = unreleasedTerm.get(),
headerParserRegex = getHeaderParserRegex.get(),
itemPrefix = itemPrefix.get(),
lineSeparator = lineSeparator.get(),
)

fun get(version: String) = instance.get(version)
Expand Down
27 changes: 13 additions & 14 deletions src/main/kotlin/org/jetbrains/changelog/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ 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
import java.text.SimpleDateFormat
Expand All @@ -20,25 +19,24 @@ fun markdownToHTML(input: String) = ChangelogFlavourDescriptor().run {
.generateHtml()
}

fun markdownToPlainText(input: String) = PlainTextFlavourDescriptor().run {
fun markdownToPlainText(input: String, lineSeparator: String) = PlainTextFlavourDescriptor(lineSeparator).run {
HtmlGenerator(input, MarkdownParser(this).buildMarkdownTreeFromString(input), this, false)
.generateHtml(PlainTextTagRenderer())
}

fun String.reformat(): String {
fun String.reformat(lineSeparator: String): 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",
"""(?:^|$lineSeparator)+(#+ [^$lineSeparator]*)(?:$lineSeparator)*""".toRegex() to "$lineSeparator$lineSeparator$1$lineSeparator",
"""((?:^|$lineSeparator)#+ .*?)$lineSeparator(#+ )""".toRegex() to "$1$lineSeparator$lineSeparator$2",
"""($lineSeparator){3,}""".toRegex() to "$lineSeparator$lineSeparator",
).fold(this) { acc, (pattern, replacement) ->
acc.replace(pattern, replacement)
}.trim() + NEW_LINE
}.trim() + lineSeparator

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

}

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

if (!unreleasedTerm.isNullOrBlank()) {
Expand All @@ -72,5 +71,5 @@ internal fun compose(

function()
}
.joinToString(NEW_LINE)
.reformat()
.joinToString(lineSeparator)
.reformat(lineSeparator)
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.html.OpenCloseGeneratingProvider
import org.intellij.markdown.parser.LinkMap
import org.jetbrains.changelog.ChangelogPluginConstants.NEW_LINE
import java.net.URI

class PlainTextFlavourDescriptor : GFMFlavourDescriptor() {
class PlainTextFlavourDescriptor(private val lineSeparator: String) : GFMFlavourDescriptor() {

override fun createHtmlGeneratingProviders(linkMap: LinkMap, baseURI: URI?) =
super.createHtmlGeneratingProviders(linkMap, baseURI) + hashMapOf(
MarkdownElementTypes.LIST_ITEM to CustomProvider("- "),
MarkdownTokenTypes.EOL to CustomProvider("", NEW_LINE)
MarkdownTokenTypes.EOL to CustomProvider("", lineSeparator)
)

private class CustomProvider(private val openTagName: String = "", private val closeTagName: String = "") :
Expand Down
23 changes: 12 additions & 11 deletions src/main/kotlin/org/jetbrains/changelog/tasks/GetChangelogTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ package org.jetbrains.changelog.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.*
import org.gradle.api.tasks.options.Option
import org.jetbrains.changelog.Changelog

Expand Down Expand Up @@ -51,16 +48,20 @@ abstract class GetChangelogTask : DefaultTask() {
@get:Optional
abstract val version: Property<String>

@get:Internal
abstract val lineSeparator: Property<String>

@TaskAction
fun run() = logger.quiet(
Changelog(
inputFile.map { it.asFile }.get(),
null,
null,
null,
unreleasedTerm.get(),
headerParserRegex.get(),
itemPrefix.get(),
file = inputFile.map { it.asFile }.get(),
preTitle = null,
title = null,
introduction = null,
unreleasedTerm = unreleasedTerm.get(),
headerParserRegex = headerParserRegex.get(),
itemPrefix = itemPrefix.get(),
lineSeparator = lineSeparator.get(),
).let {
val version = cliVersion ?: when (unreleased) {
true -> unreleasedTerm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import org.gradle.api.GradleException
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
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.gradle.api.tasks.*
import org.jetbrains.changelog.compose

abstract class InitializeChangelogTask : DefaultTask() {
Expand Down Expand Up @@ -43,6 +40,9 @@ abstract class InitializeChangelogTask : DefaultTask() {
@get:Optional
abstract val unreleasedTerm: Property<String>

@get:Internal
abstract val lineSeparator: Property<String>

@TaskAction
fun run() {
val file = outputFile.get()
Expand All @@ -58,11 +58,12 @@ abstract class InitializeChangelogTask : DefaultTask() {
}

val content = compose(
preTitle.orNull,
title.orNull,
introduction.orNull,
unreleasedTerm.get(),
groups.get(),
preTitle = preTitle.orNull,
title = title.orNull,
introduction = introduction.orNull,
unreleasedTerm = unreleasedTerm.get(),
groups = groups.get(),
lineSeparator = lineSeparator.get(),
)
file.writeText(content)
}
Expand Down
Loading

0 comments on commit f732ac8

Please sign in to comment.