diff --git a/CHANGELOG.md b/CHANGELOG.md index 5662167..b689a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.9.2] - 2024-08-30 + +- Fix blank line at the end of file [#299](https://github.com/nbbrd/heylogs/issues/299) + ## [0.9.1] - 2024-08-28 ### Fixed @@ -173,7 +177,8 @@ This release improves extension points and also aligns features of Maven plugin - Initial release -[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.9.1...HEAD +[Unreleased]: https://github.com/nbbrd/heylogs/compare/v0.9.2...HEAD +[0.9.2]: https://github.com/nbbrd/heylogs/compare/v0.9.1...v0.9.2 [0.9.1]: https://github.com/nbbrd/heylogs/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/nbbrd/heylogs/compare/v0.8.1...v0.9.0 [0.8.1]: https://github.com/nbbrd/heylogs/compare/v0.8.0...v0.8.1 diff --git a/heylogs-api/pom.xml b/heylogs-api/pom.xml index 342596a..bec0eee 100644 --- a/heylogs-api/pom.xml +++ b/heylogs-api/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-api diff --git a/heylogs-api/src/main/java/internal/heylogs/FlexmarkIO.java b/heylogs-api/src/main/java/internal/heylogs/FlexmarkIO.java new file mode 100644 index 0000000..dc05a9e --- /dev/null +++ b/heylogs-api/src/main/java/internal/heylogs/FlexmarkIO.java @@ -0,0 +1,32 @@ +package internal.heylogs; + +import com.vladsch.flexmark.formatter.Formatter; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Document; +import nbbrd.io.text.TextFormatter; +import nbbrd.io.text.TextParser; + +public final class FlexmarkIO { + + private FlexmarkIO() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static Parser newParser() { + return Parser.builder().build(); + } + + public static TextParser newTextParser() { + return TextParser.onParsingReader(newParser()::parseReader); + } + + public static Formatter newFormatter() { + Formatter.Builder result = Formatter.builder(); + result.set(Formatter.MAX_TRAILING_BLANK_LINES, 0); + return result.build(); + } + + public static TextFormatter newTextFormatter() { + return TextFormatter.onFormattingWriter(newFormatter()::render); + } +} diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java index 95f1565..d4c0f41 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/FilterTest.java @@ -6,7 +6,6 @@ import java.time.LocalDate; import java.util.function.Function; -import static nbbrd.heylogs.Filter.builder; import static nbbrd.heylogs.Filter.parseLocalDate; import static nbbrd.heylogs.Version.HYPHEN; import static org.assertj.core.api.Assertions.*; @@ -15,43 +14,43 @@ public class FilterTest { @Test public void testRef() { - assertThat(builder().build()) + assertThat(Filter.builder().build()) .describedAs("Empty reference") .is(containing(unreleased)) .is(containing(v1_1_0)) .is(containing(v1_0_0)); - assertThat(builder().ref("Unreleased").build()) + assertThat(Filter.builder().ref("Unreleased").build()) .describedAs("Full reference") .is(containing(unreleased)) .isNot(containing(v1_1_0)) .isNot(containing(v1_0_0)); - assertThat(builder().ref("1.1.0").build()) + assertThat(Filter.builder().ref("1.1.0").build()) .describedAs("Full reference") .isNot(containing(unreleased)) .is(containing(v1_1_0)) .isNot(containing(v1_0_0)); - assertThat(builder().ref("rel").build()) + assertThat(Filter.builder().ref("rel").build()) .describedAs("Partial reference") .is(containing(unreleased)) .isNot(containing(v1_1_0)) .isNot(containing(v1_0_0)); - assertThat(builder().ref("1.").build()) + assertThat(Filter.builder().ref("1.").build()) .describedAs("Partial reference") .isNot(containing(unreleased)) .is(containing(v1_1_0)) .is(containing(v1_0_0)); - assertThat(builder().ref("other").build()) + assertThat(Filter.builder().ref("other").build()) .describedAs("Unknown reference") .isNot(containing(unreleased)) .isNot(containing(v1_1_0)) .isNot(containing(v1_0_0)); - assertThat(builder().ref("other-SNAPSHOT").build()) + assertThat(Filter.builder().ref("other-SNAPSHOT").build()) .describedAs("Matching unreleased pattern reference") .is(containing(unreleased)) .isNot(containing(v1_1_0)) @@ -60,7 +59,7 @@ public void testRef() { @Test public void testTimeRange() { - Function onTimeRange = o -> builder().timeRange(o).build(); + Function onTimeRange = o -> Filter.builder().timeRange(o).build(); assertThat(TimeRange.ALL) .extracting(onTimeRange) diff --git a/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java b/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java index 95840f4..8da3fb0 100644 --- a/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java +++ b/heylogs-api/src/test/java/nbbrd/heylogs/HeylogsTest.java @@ -2,6 +2,7 @@ import com.vladsch.flexmark.util.ast.Document; import com.vladsch.flexmark.util.ast.Node; +import internal.heylogs.FlexmarkIO; import internal.heylogs.StylishFormat; import lombok.NonNull; import nbbrd.design.MightBePromoted; @@ -11,7 +12,6 @@ import nbbrd.heylogs.spi.RuleSeverity; import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; -import tests.heylogs.api.Sample; import java.io.IOException; import java.net.URL; @@ -21,9 +21,9 @@ import static internal.heylogs.URLExtractor.urlOf; import static java.util.Collections.singletonList; -import static nbbrd.heylogs.Filter.builder; import static nbbrd.heylogs.Heylogs.FIRST_FORMAT_AVAILABLE; import static nbbrd.heylogs.spi.RuleSeverity.ERROR; +import static nbbrd.io.function.IOFunction.unchecked; import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.InstanceOfAssertFactories.list; import static tests.heylogs.api.Sample.using; @@ -62,13 +62,9 @@ public void testCheckFormat() { public void testExtractVersions() { Heylogs x = Heylogs.ofServiceLoader(); - Function usingMain = extractor -> { - Document doc = using("/Main.md"); - x.extractVersions(doc, extractor); - return Sample.FORMATTER.render(doc); - }; + Function usingMain = extractor -> extractVersionsToString(x, using("/Main.md"), extractor); - assertThat(builder().ref("1.1.0").build()) + assertThat(Filter.builder().ref("1.1.0").build()) .extracting(usingMain, STRING) .isEqualTo( "## [1.1.0] - 2019-02-15\n" + @@ -84,18 +80,16 @@ public void testExtractVersions() { "- Fixed typos in Italian translation from [@lorenzo-arena](https://github.com/lorenzo-arena).\n" + "- Fixed typos in Indonesian translation from [@ekojs](https://github.com/ekojs).\n" + "\n" + - "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n" + - "\n"); + "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n"); - assertThat(builder().ref("1.1.0").ignoreContent(true).build()) + assertThat(Filter.builder().ref("1.1.0").ignoreContent(true).build()) .extracting(usingMain, STRING) .isEqualTo( "## [1.1.0] - 2019-02-15\n" + "\n" + - "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n" + - "\n"); + "[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n"); - assertThat(builder().ref("zzz").build()) + assertThat(Filter.builder().ref("zzz").build()) .extracting(usingMain, STRING) .isEmpty(); } @@ -127,7 +121,8 @@ public void testReleaseChanges() { "## [1.2.3] - 2010-01-01", "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...v1.2.3") - .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD"); + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD") + .endsWith("[0.0.1]: https://github.com/olivierlacan/keep-a-changelog/releases/tag/v0.0.1\n"); assertThat(releaseChangesToString(x, using("/UnreleasedChanges.md"), v123)) .contains( @@ -135,7 +130,8 @@ public void testReleaseChanges() { "## [1.2.3] - 2010-01-01", "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...v1.2.3") - .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD"); + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...HEAD") + .endsWith("[1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0\n"); assertThat(releaseChangesToString(x, using("/FirstRelease.md"), v123)) .contains( @@ -143,7 +139,8 @@ public void testReleaseChanges() { "## [1.2.3] - 2010-01-01", "[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...HEAD", "[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...v1.2.3") - .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/HEAD...HEAD"); + .doesNotContain("[unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/HEAD...HEAD") + .endsWith("[1.2.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.2.3...v1.2.3\n"); } @Test @@ -259,9 +256,14 @@ public void testFormatStatus() throws IOException { ); } + private static String extractVersionsToString(Heylogs heylogs, Document doc, Filter extractor) { + heylogs.extractVersions(doc, extractor); + return unchecked(FlexmarkIO.newTextFormatter()::formatToString).apply(doc); + } + private static String releaseChangesToString(Heylogs heylogs, Document doc, Version version) { heylogs.releaseChanges(doc, version, "v"); - return Sample.FORMATTER.render(doc); + return unchecked(FlexmarkIO.newTextFormatter()::formatToString).apply(doc); } private static final class MockedRule implements Rule { diff --git a/heylogs-api/src/test/java/tests/heylogs/api/Sample.java b/heylogs-api/src/test/java/tests/heylogs/api/Sample.java index e623310..33678ae 100644 --- a/heylogs-api/src/test/java/tests/heylogs/api/Sample.java +++ b/heylogs-api/src/test/java/tests/heylogs/api/Sample.java @@ -1,48 +1,40 @@ package tests.heylogs.api; import com.vladsch.flexmark.ast.Heading; -import com.vladsch.flexmark.formatter.Formatter; -import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; import com.vladsch.flexmark.util.sequence.BasedSequence; +import internal.heylogs.FlexmarkIO; import nbbrd.heylogs.*; import nbbrd.heylogs.spi.RuleIssue; import nbbrd.io.function.IOConsumer; import org.assertj.core.util.URLs; -import java.io.*; -import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.Objects; +import static java.nio.charset.StandardCharsets.UTF_8; import static nbbrd.heylogs.spi.RuleSeverity.ERROR; +import static nbbrd.io.function.IOFunction.unchecked; public class Sample { - public static final Parser PARSER = Parser.builder().build(); - public static final Formatter FORMATTER = Formatter.builder().build(); - public static Document using(String name) { - try (InputStream stream = Sample.class.getResourceAsStream(name)) { - if (stream == null) { - throw new IllegalArgumentException("Missing resource '" + name + "'"); - } - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - return PARSER.parseReader(reader); - } - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } + return unchecked((String x) -> FlexmarkIO.newTextParser().parseResource(Sample.class, x, UTF_8)) + .apply(name); } public static Heading asHeading(String text) { - return (Heading) PARSER.parse(text).getChildOfType(Heading.class); + return unchecked(FlexmarkIO.newTextParser()::parseChars) + .andThen(doc -> (Heading) doc.getChildOfType(Heading.class)) + .apply(text); } public static String asText(Heading heading) { Document doc = new Document(null, BasedSequence.NULL); doc.appendChild(heading); - return FORMATTER.render(doc).trim(); + return unchecked(FlexmarkIO.newTextFormatter()::formatToString) + .andThen(String::trim) + .apply(doc); } public static final Problem PROBLEM1 = Problem.builder().id("rule1").severity(ERROR).issue(RuleIssue.builder().message("boom").line(5).column(18).build()).build(); @@ -76,6 +68,6 @@ public static String writing(IOConsumer content) { } public static String contentOf(Class anchor, String resourceName) { - return URLs.contentOf(Objects.requireNonNull(anchor.getResource(resourceName)), StandardCharsets.UTF_8); + return URLs.contentOf(Objects.requireNonNull(anchor.getResource(resourceName)), UTF_8); } } diff --git a/heylogs-bom/pom.xml b/heylogs-bom/pom.xml index 43be18a..b0915ba 100644 --- a/heylogs-bom/pom.xml +++ b/heylogs-bom/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-bom diff --git a/heylogs-cli/pom.xml b/heylogs-cli/pom.xml index b7cbca8..edc3fc3 100644 --- a/heylogs-cli/pom.xml +++ b/heylogs-cli/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-cli diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownInputSupport.java b/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownInputSupport.java index 4260a1f..108b695 100644 --- a/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownInputSupport.java +++ b/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownInputSupport.java @@ -1,7 +1,7 @@ package internal.heylogs.cli; -import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; +import internal.heylogs.FlexmarkIO; import lombok.AccessLevel; import lombok.NonNull; import nbbrd.console.picocli.CommandSupporter; @@ -9,7 +9,6 @@ import nbbrd.design.StaticFactoryMethod; import java.io.IOException; -import java.io.Reader; import java.nio.file.DirectoryStream; import java.nio.file.Path; import java.util.Locale; @@ -25,12 +24,8 @@ public class MarkdownInputSupport extends TextInputSupport implements DirectoryS return CommandSupporter.create(MarkdownInputSupport::new, supporters); } - private @NonNull Parser parser = Parser.builder().build(); - public Document readDocument(Path file) throws IOException { - try (Reader reader = newBufferedReader(file)) { - return parser.parseReader(reader); - } + return FlexmarkIO.newTextParser().parseReader(() -> newBufferedReader(file)); } public String getName(Path file) { diff --git a/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownOutputSupport.java b/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownOutputSupport.java index a27de3b..e92fe1a 100644 --- a/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownOutputSupport.java +++ b/heylogs-cli/src/main/java/internal/heylogs/cli/MarkdownOutputSupport.java @@ -1,7 +1,7 @@ package internal.heylogs.cli; -import com.vladsch.flexmark.formatter.Formatter; import com.vladsch.flexmark.util.ast.Document; +import internal.heylogs.FlexmarkIO; import lombok.AccessLevel; import lombok.NonNull; import nbbrd.console.picocli.CommandSupporter; @@ -9,7 +9,6 @@ import nbbrd.design.StaticFactoryMethod; import java.io.IOException; -import java.io.Writer; import java.nio.file.Path; @lombok.Getter @@ -23,11 +22,7 @@ public class MarkdownOutputSupport extends TextOutputSupport { return CommandSupporter.create(MarkdownOutputSupport::new, supporters); } - private @NonNull Formatter formatter = Formatter.builder().build(); - public void writeDocument(Path file, Document document) throws IOException { - try (Writer writer = newBufferedWriter(file)) { - formatter.render(document, writer); - } + FlexmarkIO.newTextFormatter().formatWriter(document, () -> newBufferedWriter(file)); } } diff --git a/heylogs-ext-github/pom.xml b/heylogs-ext-github/pom.xml index 2dc1542..e655b12 100644 --- a/heylogs-ext-github/pom.xml +++ b/heylogs-ext-github/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-ext-github diff --git a/heylogs-ext-json/pom.xml b/heylogs-ext-json/pom.xml index 5232266..2c2b6ec 100644 --- a/heylogs-ext-json/pom.xml +++ b/heylogs-ext-json/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-ext-json diff --git a/heylogs-ext-semver/pom.xml b/heylogs-ext-semver/pom.xml index 14ff53e..4f2886d 100644 --- a/heylogs-ext-semver/pom.xml +++ b/heylogs-ext-semver/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-ext-semver diff --git a/heylogs-maven-plugin/pom.xml b/heylogs-maven-plugin/pom.xml index 85c8391..2f05ec4 100644 --- a/heylogs-maven-plugin/pom.xml +++ b/heylogs-maven-plugin/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 heylogs-maven-plugin diff --git a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java index 1ea666e..597eb6e 100644 --- a/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java +++ b/heylogs-maven-plugin/src/main/java/nbbrd/heylogs/maven/plugin/HeylogsMojo.java @@ -1,8 +1,7 @@ package nbbrd.heylogs.maven.plugin; -import com.vladsch.flexmark.formatter.Formatter; -import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.ast.Document; +import internal.heylogs.FlexmarkIO; import nbbrd.design.MightBePromoted; import nbbrd.heylogs.Heylogs; import nbbrd.heylogs.ext.semver.SemVerRule; @@ -15,6 +14,7 @@ import java.util.function.Consumer; import static internal.heylogs.maven.plugin.HeylogsParameters.isMojoLogFile; +import static java.nio.charset.StandardCharsets.UTF_8; import static nbbrd.console.picocli.text.TextOutputSupport.newTextOutputSupport; abstract class HeylogsMojo extends AbstractMojo { @@ -43,9 +43,8 @@ protected void notifyMissingChangelog() { protected Document readChangelog(File inputFile) throws MojoExecutionException { getLog().info("Reading changelog " + inputFile); - Parser parser = Parser.builder().build(); - try (Reader reader = Files.newBufferedReader(inputFile.toPath())) { - return parser.parseReader(reader); + try { + return FlexmarkIO.newTextParser().parseFile(inputFile, UTF_8); } catch (IOException ex) { throw new MojoExecutionException("Failed to read changelog", ex); } @@ -53,12 +52,9 @@ protected Document readChangelog(File inputFile) throws MojoExecutionException { protected void writeChangelog(Document document, File outputFile) throws MojoExecutionException { getLog().info("Writing changelog " + outputFile); - Formatter formatter = Formatter.builder().build(); try { Files.createDirectories(outputFile.getParentFile().toPath()); - try (Writer writer = Files.newBufferedWriter(outputFile.toPath())) { - formatter.render(document, writer); - } + FlexmarkIO.newTextFormatter().formatFile(document, outputFile, UTF_8); } catch (IOException ex) { throw new MojoExecutionException("Failed to write changelog", ex); } diff --git a/pom.xml b/pom.xml index ef383f0..0dbb5a3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.nbbrd.heylogs heylogs-parent - 0.9.1 + 0.9.2 pom heylogs @@ -39,7 +39,7 @@ UTF-8 - 2024-08-28T15:15:37Z + 2024-08-30T09:54:30Z 1.8 1.8