Skip to content

Commit 96820c7

Browse files
committed
Handle additional text inside changelog
1 parent 1efc7e7 commit 96820c7

File tree

2 files changed

+123
-3
lines changed

2 files changed

+123
-3
lines changed

src/main/kotlin/org/jetbrains/changelog/Changelog.kt

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,31 @@ data class Changelog(
137137

138138
val (summary, items) = value.extractItemData()
139139

140-
Item(key, header, summary, isUnreleased, items)
140+
// Capture any extra text (paragraphs, tables, ordered lists, etc.) that falls
141+
// outside structured release notes by reading raw source after the last node
142+
// that extractItemData actually processes.
143+
val nodesWithoutHeader = value.filter { it.type != ATX_2 }
144+
val sectionNodes = nodesWithoutHeader
145+
.filter { it.type == ATX_3 || it.type == UNORDERED_LIST || it.type == ORDERED_LIST }
146+
val lastSectionNode = sectionNodes.lastOrNull { it.type == ATX_3 }
147+
// The last processed node is either the first list right after the last ATX_3, or the last list before any ATX_3
148+
val lastProcessedNode = if (lastSectionNode != null) {
149+
sectionNodes.firstOrNull { it.startOffset > lastSectionNode.endOffset } ?: lastSectionNode
150+
} else {
151+
sectionNodes.lastOrNull()
152+
}
153+
// Fall back to summary paragraphs
154+
val endOfProcessed = lastProcessedNode?.endOffset
155+
?: nodesWithoutHeader.takeWhile { it.type == PARAGRAPH }.lastOrNull()?.endOffset
156+
157+
val blockEnd = value.last().endOffset
158+
val suffix = if (endOfProcessed != null && endOfProcessed < blockEnd) {
159+
content.substring(endOfProcessed, blockEnd).trim()
160+
} else {
161+
""
162+
}
163+
164+
Item(key, header, summary, isUnreleased, items, suffix)
141165
.withEmptySections(isUnreleased)
142166
}
143167
}
@@ -250,6 +274,11 @@ data class Changelog(
250274
}
251275
}
252276

277+
if (suffix.isNotEmpty()) {
278+
add(lineSeparator)
279+
add(suffix)
280+
}
281+
253282
if (withLinks) {
254283
links
255284
.filterKeys { id ->
@@ -300,6 +329,7 @@ data class Changelog(
300329
val summary: String = "",
301330
val isUnreleased: Boolean = false,
302331
private val items: Map<String, Set<String>> = mutableMapOf(),
332+
internal val suffix: String = "",
303333
) {
304334

305335
internal var withHeader = true
@@ -333,13 +363,14 @@ data class Changelog(
333363
summary: String = this.summary,
334364
isUnreleased: Boolean = this.isUnreleased,
335365
items: Map<String, Set<String>> = this.items,
366+
suffix: String = this.suffix,
336367
withHeader: Boolean = this.withHeader,
337368
withLinkedHeader: Boolean = this.withLinkedHeader,
338369
withSummary: Boolean = this.withSummary,
339370
withLinks: Boolean = this.withLinks,
340371
withEmptySections: Boolean = this.withEmptySections,
341372
filterCallback: ((String) -> Boolean)? = this.filterCallback,
342-
) = Item(version, header, summary, isUnreleased, items).also {
373+
) = Item(version, header, summary, isUnreleased, items, suffix).also {
343374
it.withHeader = withHeader
344375
it.withLinkedHeader = withLinkedHeader
345376
it.withSummary = withSummary
@@ -356,6 +387,7 @@ data class Changelog(
356387
return copy(
357388
summary = summary.ifEmpty { item.summary },
358389
items = items + item.items,
390+
suffix = listOf(suffix, item.suffix).filter { it.isNotEmpty() }.joinToString("\n\n"),
359391
filterCallback = filterCallback,
360392
)
361393
}

src/test/kotlin/org/jetbrains/changelog/tasks/PatchChangelogTaskTest.kt

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import org.jetbrains.changelog.exceptions.MissingVersionException
88
import org.jetbrains.changelog.normalizeLineSeparator
99
import java.text.SimpleDateFormat
1010
import java.util.*
11-
import kotlin.test.*
11+
import kotlin.test.BeforeTest
12+
import kotlin.test.Test
13+
import kotlin.test.assertEquals
14+
import kotlin.test.assertFailsWith
15+
import kotlin.test.assertFalse
16+
import kotlin.test.assertTrue
1217

1318
class PatchChangelogTaskTest : BaseTest() {
1419

@@ -1715,4 +1720,87 @@ class PatchChangelogTaskTest : BaseTest() {
17151720

17161721
assertMarkdown(expectedContent, changelog)
17171722
}
1723+
1724+
@Test
1725+
fun `additional text inside release notes`() {
1726+
changelog =
1727+
"""
1728+
# Gradle plugin changelog
1729+
1730+
## Unreleased
1731+
1732+
## 0.4.0 - 2026-02-25
1733+
1734+
### Added
1735+
1736+
- Some release notes
1737+
1738+
Additional note for customers.
1739+
1740+
Structured info:
1741+
1742+
1. **L1** - text
1743+
2. **L2** - text
1744+
3. **L3** - text
1745+
1746+
## 0.3.0 - 2026-02-24
1747+
1748+
### Changed
1749+
1750+
- Introduce breaking changes
1751+
1752+
| Before | After |
1753+
|--------|-------------|
1754+
| `test` | `test test` |
1755+
1756+
""".trimIndent()
1757+
1758+
buildFile =
1759+
"""
1760+
plugins {
1761+
id 'org.jetbrains.changelog'
1762+
}
1763+
changelog {
1764+
version = "0.4.0"
1765+
groups.empty()
1766+
}
1767+
""".trimIndent()
1768+
project.evaluate()
1769+
runTask(PATCH_CHANGELOG_TASK_NAME)
1770+
1771+
val expectedContent =
1772+
"""
1773+
# Gradle plugin changelog
1774+
1775+
## Unreleased
1776+
1777+
## 0.4.0 - 2026-02-25
1778+
1779+
### Added
1780+
1781+
- Some release notes
1782+
1783+
Additional note for customers.
1784+
1785+
Structured info:
1786+
1787+
1. **L1** - text
1788+
2. **L2** - text
1789+
3. **L3** - text
1790+
1791+
## 0.3.0 - 2026-02-24
1792+
1793+
### Changed
1794+
1795+
- Introduce breaking changes
1796+
1797+
| Before | After |
1798+
|--------|-------------|
1799+
| `test` | `test test` |
1800+
1801+
""".trimIndent()
1802+
1803+
1804+
assertMarkdown(expectedContent, changelog)
1805+
}
17181806
}

0 commit comments

Comments
 (0)