diff --git a/CHANGES.md b/CHANGES.md index c7f75ca609..49bf5b684d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed +- `VersionCatalogStep` preserves standalone comments at section boundaries and the end of the file. ([#3048](https://github.com/diffplug/spotless/issues/3048)) - `VersionCatalogStep` preserves entries when comments contain unmatched brackets, preserves commas inside quoted strings, and keeps significant line boundaries in multiline entries. ([#3042](https://github.com/diffplug/spotless/pull/3042)) - `VersionCatalogStep` now reports unfinished entries as lints at their starting line. These fail formatting by default, so upgrading may expose catalog errors that previously caused silent data loss. ([#3042](https://github.com/diffplug/spotless/pull/3042)) diff --git a/lib/src/main/java/com/diffplug/spotless/toml/VersionCatalogStep.java b/lib/src/main/java/com/diffplug/spotless/toml/VersionCatalogStep.java index ac89fbdade..3a4e2002a2 100644 --- a/lib/src/main/java/com/diffplug/spotless/toml/VersionCatalogStep.java +++ b/lib/src/main/java/com/diffplug/spotless/toml/VersionCatalogStep.java @@ -59,7 +59,7 @@ static String format(String raw, boolean stripQuotedKeys) { return raw; } - Map> sections = parseSections(raw); + Map sections = parseSections(raw); List preambleLines = extractPreamble(raw); StringBuilder result = new StringBuilder(); @@ -83,7 +83,8 @@ static String format(String raw, boolean stripQuotedKeys) { } for (String header : orderedKeys) { - List entries = sections.get(header); + Section section = sections.get(header); + List entries = section.entries; if (!first) { result.append('\n'); } @@ -101,6 +102,9 @@ static String format(String raw, boolean stripQuotedKeys) { } result.append(entry.formatted).append('\n'); } + for (String commentLine : section.trailingComments) { + result.append(commentLine).append('\n'); + } } return result.toString(); @@ -120,10 +124,10 @@ private static List extractPreamble(String raw) { return preamble; } - private static Map> parseSections(String raw) { - Map> sections = new LinkedHashMap<>(); + private static Map parseSections(String raw) { + Map sections = new LinkedHashMap<>(); String currentHeader = null; - List currentEntries = null; + Section currentSection = null; List pendingComments = new ArrayList<>(); StringBuilder multiLineAccumulator = null; int lineNumber = 0; @@ -137,7 +141,7 @@ private static Map> parseSections(String raw) { multiLineAccumulator.append('\n').append(line); if (isBalanced(multiLineAccumulator.toString())) { Entry entry = new Entry(multiLineAccumulator.toString(), new ArrayList<>(pendingComments)); - currentEntries.add(entry); + currentSection.entries.add(entry); pendingComments.clear(); multiLineAccumulator = null; } @@ -149,11 +153,11 @@ private static Map> parseSections(String raw) { } Matcher headerMatcher = TABLE_HEADER.matcher(trimmed); if (headerMatcher.matches()) { + moveTrailingComments(currentSection, pendingComments); currentHeader = "[" + headerMatcher.group(1) + "]"; - currentEntries = new ArrayList<>(); - sections.put(currentHeader, currentEntries); - pendingComments.clear(); - } else if (currentEntries != null) { + currentSection = new Section(); + sections.put(currentHeader, currentSection); + } else if (currentSection != null) { if (trimmed.isEmpty() || trimmed.startsWith("#")) { pendingComments.add(trimmed); } else if (!isBalanced(trimmed)) { @@ -161,7 +165,7 @@ private static Map> parseSections(String raw) { entryStartLine = lineNumber; } else { Entry entry = new Entry(trimmed, new ArrayList<>(pendingComments)); - currentEntries.add(entry); + currentSection.entries.add(entry); pendingComments.clear(); } } @@ -171,9 +175,22 @@ private static Map> parseSections(String raw) { // Report the incomplete entry instead of silently returning a partially parsed catalog. throw Lint.atLine(entryStartLine, "unterminatedEntry", "Unterminated version catalog entry in " + currentHeader).shortcut(); } + moveTrailingComments(currentSection, pendingComments); return sections; } + private static void moveTrailingComments(Section section, List pendingComments) { + if (section == null) { + return; + } + int lastNonBlank = pendingComments.size(); + while (lastNonBlank > 0 && pendingComments.get(lastNonBlank - 1).isEmpty()) { + lastNonBlank--; + } + section.trailingComments.addAll(pendingComments.subList(0, lastNonBlank)); + pendingComments.clear(); + } + private static boolean isBalanced(String text) { int depth = 0; @@ -441,4 +458,9 @@ String sortKey() { return extractKey(formatted != null ? formatted : content); } } + + private static final class Section { + final List entries = new ArrayList<>(); + final List trailingComments = new ArrayList<>(); + } } diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7bc475f509..45d704b852 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed +- `versionCatalog()` preserves standalone comments at section boundaries and the end of the file. ([#3048](https://github.com/diffplug/spotless/issues/3048)) - `versionCatalog()` preserves entries when comments contain unmatched brackets, preserves commas inside quoted strings, and keeps significant line boundaries in multiline entries. ([#3042](https://github.com/diffplug/spotless/pull/3042)) - `versionCatalog()` now reports unfinished entries as lints at their starting line. These fail formatting by default, so upgrading may expose catalog errors that previously caused silent data loss. ([#3042](https://github.com/diffplug/spotless/pull/3042)) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 528c37f491..c14051d828 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed +- `` preserves standalone comments at section boundaries and the end of the file. ([#3048](https://github.com/diffplug/spotless/issues/3048)) - `` preserves entries when comments contain unmatched brackets, preserves commas inside quoted strings, and keeps significant line boundaries in multiline entries. ([#3042](https://github.com/diffplug/spotless/pull/3042)) - `` now reports unfinished entries as lints at their starting line. These fail formatting by default, so upgrading may expose catalog errors that previously caused silent data loss. ([#3042](https://github.com/diffplug/spotless/pull/3042)) diff --git a/testlib/src/test/java/com/diffplug/spotless/toml/VersionCatalogStepTest.java b/testlib/src/test/java/com/diffplug/spotless/toml/VersionCatalogStepTest.java index cc94fc4c1b..13570ea66d 100644 --- a/testlib/src/test/java/com/diffplug/spotless/toml/VersionCatalogStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/toml/VersionCatalogStepTest.java @@ -91,6 +91,19 @@ void commentsPreserved() throws Exception { "[versions]\n# A library\nalpha = \"2.0\"\n# Z library\nzoo = \"1.0\"\n"); } + @Test + void standaloneCommentsAtSectionBoundariesAndEofArePreserved() throws Exception { + StepHarness.forStep(VersionCatalogStep.create()).testUnaffected( + "[versions]\nzoo = \"1.0\"\n# keep this trailing comment\n\n[libraries]\nfoo = { module = \"g:a\", version.ref = \"zoo\" }\n# keep this final comment\n"); + } + + @Test + void trailingCommentsFollowTheirSectionWhenTablesAreSorted() throws Exception { + StepHarness.forStep(VersionCatalogStep.create()).test( + "[plugins]\nzoo = \"g:z:1\"\n# plugin note\n[versions]\nalpha = \"1.0\"\n# version note\n", + "[versions]\nalpha = \"1.0\"\n# version note\n\n[plugins]\nzoo = \"g:z:1\"\n# plugin note\n"); + } + @Test void inlineCommentsPreserved() throws Exception { StepHarness harness = StepHarness.forStep(VersionCatalogStep.create());