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 super Appendable> 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