Skip to content
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
with the exception that 0.x versions can break between minor versions.

## [Unreleased]
### Added
- New option `lineSeparator` for `MarkdownRenderer.Builder` to change the default line
separator from `\n` (e.g. to `\r\n`) (#442)

## [0.29.0] - 2026-06-20
### Added
- Support rendering GFM task list items to Markdown (#433)
Expand Down Expand Up @@ -560,6 +565,7 @@ API breaking changes (caused by changes in spec):
Initial release of commonmark-java, a port of commonmark.js with extensions
for autolinking URLs, GitHub flavored strikethrough and tables.

[Unreleased]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.29.0...HEAD
[0.29.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.28.0...commonmark-parent-0.29.0
[0.28.0]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0
[0.27.1]: https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.0...commonmark-parent-0.27.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
*/
public class MarkdownRenderer implements Renderer {

private final String lineSeparator;
private final List<MarkdownNodeRendererFactory> nodeRendererFactories;

private MarkdownRenderer(Builder builder) {
this.lineSeparator = builder.lineSeparator;
this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1);
this.nodeRendererFactories.addAll(builder.nodeRendererFactories);
// Add as last. This means clients can override the rendering of core nodes if they want.
Expand Down Expand Up @@ -58,7 +60,7 @@ public static Builder builder() {

@Override
public void render(Node node, Appendable output) {
RendererContext context = new RendererContext(new MarkdownWriter(output));
var context = new RendererContext(new MarkdownWriter(output, lineSeparator));
context.render(node);
}

Expand All @@ -74,6 +76,7 @@ public String render(Node node) {
*/
public static class Builder {

private String lineSeparator = "\n";
private final List<MarkdownNodeRendererFactory> nodeRendererFactories = new ArrayList<>();

/**
Expand All @@ -83,6 +86,18 @@ public MarkdownRenderer build() {
return new MarkdownRenderer(this);
}

/**
* Set the line separator (line terminator) that the writer should use. The default is a
* newline ({@code \n}) on all systems.
*
* @param lineSeparator
* @return {@code this}
*/
public Builder lineSeparator(String lineSeparator) {
this.lineSeparator = lineSeparator;
return this;
}

/**
* Add a factory for instantiating a node renderer (done when rendering). This allows to
* override the rendering of node types or define rendering for custom node types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
public class MarkdownWriter {

private final Appendable buffer;
private final String lineSeparator;

private int blockSeparator = 0;
private char lastChar;
Expand All @@ -21,7 +22,12 @@ public class MarkdownWriter {
private final LinkedList<CharMatcher> rawEscapes = new LinkedList<>();

public MarkdownWriter(Appendable out) {
buffer = out;
this(out, "\n");
}

public MarkdownWriter(Appendable out, String lineSeparator) {
this.buffer = out;
this.lineSeparator = lineSeparator;
}

/** Write the supplied string (raw/unescaped except if {@link #pushRawEscape} was used). */
Expand Down Expand Up @@ -53,9 +59,9 @@ public void text(String s, CharMatcher escape) {
atLineStart = false;
}

/** Write a newline (line terminator). */
/** Write a line separator (line terminator). */
public void line() {
write('\n');
write(lineSeparator);
writePrefixes();
atLineStart = true;
}
Expand Down Expand Up @@ -167,6 +173,17 @@ private void write(String s, CharMatcher escape) {
atLineStart = false;
}

private void write(String s) {
try {
buffer.append(s);
} catch (IOException e) {
throw new RuntimeException(e);
}

lastChar = s.charAt(s.length() - 1);
atLineStart = false;
}

private void write(char c) {
try {
append(c, null);
Expand All @@ -192,10 +209,10 @@ private void writePrefixes() {
*/
private void flushBlockSeparator() {
if (blockSeparator != 0) {
write('\n');
write(lineSeparator);
writePrefixes();
if (blockSeparator > 1) {
write('\n');
write(lineSeparator);
writePrefixes();
}
blockSeparator = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,13 @@ public void testSoftLineBreaks() {
assertRoundTrip("foo\nbar\n");
}

@Test
public void testCustomLineSeparator() {
var renderer = MarkdownRenderer.builder().lineSeparator("\r\n").build();
assertThat(renderer.render(parse("# Heading\n\nBody\n")))
.isEqualTo("# Heading\r\n\r\nBody\r\n");
}

@Test
public void overrideNodeRender() {
var nodeRendererFactory =
Expand Down
Loading