Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static String format(String raw, boolean stripQuotedKeys) {
return raw;
}

Map<String, List<Entry>> sections = parseSections(raw);
Map<String, Section> sections = parseSections(raw);
List<String> preambleLines = extractPreamble(raw);

StringBuilder result = new StringBuilder();
Expand All @@ -83,7 +83,8 @@ static String format(String raw, boolean stripQuotedKeys) {
}

for (String header : orderedKeys) {
List<Entry> entries = sections.get(header);
Section section = sections.get(header);
List<Entry> entries = section.entries;
if (!first) {
result.append('\n');
}
Expand All @@ -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();
Expand All @@ -120,10 +124,10 @@ private static List<String> extractPreamble(String raw) {
return preamble;
}

private static Map<String, List<Entry>> parseSections(String raw) {
Map<String, List<Entry>> sections = new LinkedHashMap<>();
private static Map<String, Section> parseSections(String raw) {
Map<String, Section> sections = new LinkedHashMap<>();
String currentHeader = null;
List<Entry> currentEntries = null;
Section currentSection = null;
List<String> pendingComments = new ArrayList<>();
StringBuilder multiLineAccumulator = null;
int lineNumber = 0;
Expand All @@ -137,7 +141,7 @@ private static Map<String, List<Entry>> 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;
}
Expand All @@ -149,19 +153,19 @@ private static Map<String, List<Entry>> 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)) {
multiLineAccumulator = new StringBuilder(line.stripLeading());
entryStartLine = lineNumber;
} else {
Entry entry = new Entry(trimmed, new ArrayList<>(pendingComments));
currentEntries.add(entry);
currentSection.entries.add(entry);
pendingComments.clear();
}
}
Expand All @@ -171,9 +175,22 @@ private static Map<String, List<Entry>> 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<String> 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;

Expand Down Expand Up @@ -441,4 +458,9 @@ String sortKey() {
return extractKey(formatted != null ? formatted : content);
}
}

private static final class Section {
final List<Entry> entries = new ArrayList<>();
final List<String> trailingComments = new ArrayList<>();
}
}
1 change: 1 addition & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
1 change: 1 addition & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading