Visitor
Starting with 3.0.0 visiting of parsed markdown
nodes does not require creating own instance of commonmark-java Visitor,
instead a composable/configurable MarkwonVisitor is used.
Visitor.Builder
There is no need to create own instance of MarkwonVisitor.Builder as
it is done by Markwon itself. One still can configure it as one wishes:
final Markwon markwon = Markwon.builder(contex)
        .usePlugin(new AbstractMarkwonPlugin() {
            @Override
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
                builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() {
                    @Override
                    public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
                        visitor.forceNewLine();
                    }
                });
            }
        });
MarkwonVisitor encapsulates most of the functionality of rendering parsed markdown.
It holds rendering configuration:
- MarkwonVisitor#configuration- getter for current MarkwonConfiguration
- MarkwonVisitor#renderProps- getter for current RenderProps
- MarkwonVisitor#builder- getter for current- SpannableBuilder
It contains also a number of utility functions:
- visitChildren(Node)- will visit all children of supplied Node
- hasNext(Node)- utility function to check if supplied Node has a Node after it (useful for white-space management, so there should be no blank new line after last BlockNode)
- ensureNewLine- will insert a new line at current- SpannableBuilderposition only if current (last) character is not a new-line
- forceNewLine- will insert a new line character without any condition checking
- length- helper function to call- visitor.builder().length(), returns current length of- SpannableBuilder
- clear- will clear state for- RenderPropsand- SpannableBuilder, this is done by- Markwonautomatically after each render call
And some utility functions to control the spans:
- setSpans(int start, Object spans)- will apply supplied- spanson- SpannableBuilderstarting at- startposition and ending at- SpannableBuilder#length.- spanscan be- null(no spans will be applied) or an array of spans (each span of this array will be applied)
- setSpansForNodeOptional(N node, int start)- helper method to set spans for specified- node(internally obtains- SpanFactoryfor that node and uses it to apply spans)
- setSpansForNode(N node, int start)- almost the same as- setSpansForNodeOptionalbut instead of silently ignoring call if none- SpanFactoryis registered, this method will throw an exception.
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
    builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
        @Override
        public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
            // or just `visitor.length()`
            final int start = visitor.builder().length();
            visitor.visitChildren(heading);
            // or just `visitor.setSpansForNodeOptional(heading, start)`
            final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass());
            if (factory != null) {
                visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps()));
            }
            
            if (visitor.hasNext(heading)) {
                visitor.ensureNewLine();
                visitor.forceNewLine();
            }
        }
    });
}
BlockHandler 4.3.0
Since 4.3.0 there is class to control insertions of new lines after markdown blocks
BlockHandler (MarkwonVisitor.BlockHandler) and its default implementation BlockHandlerDef. For example,
to disable an empty new line after Heading:
final Markwon markwon = Markwon.builder(this)
        .usePlugin(new AbstractMarkwonPlugin() {
            @Override
            public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
                builder.blockHandler(new BlockHandlerDef() {
                    @Override
                    public void blockEnd(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
                        if (node instanceof Heading) {
                            if (visitor.hasNext(node)) {
                                visitor.ensureNewLine();
                                // ensure new line but do not force insert one
                            }
                        } else {
                            super.blockEnd(visitor, node);
                        }
                    }
                });
            }
        })
        .build();