Markwon/docs/docs/v3/core/visitor.md
2019-08-06 19:27:20 +03:00

3.7 KiB

Visitor

Starting with 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 SpannableBuilder position 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 RenderProps and SpannableBuilder, this is done by Markwon automatically after each render call

And some utility functions to control the spans:

  • setSpans(int start, Object spans) - will apply supplied spans on SpannableBuilder starting at start position and ending at SpannableBuilder#length. spans can 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 SpanFactory for that node and uses it to apply spans)
  • setSpansForNode(N node, int start) - almost the same as setSpansForNodeOptional but instead of silently ignoring call if none SpanFactory is 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();
            }
        }
    });
}