BlockHandler abstraction in core module

This commit is contained in:
Dimitry Ivanov 2020-03-11 12:43:20 +03:00
parent 69c2d1255c
commit 0b813e43f7
14 changed files with 207 additions and 56 deletions

View File

@ -14,6 +14,7 @@ dependency (must be explicitly added to `Markwon` whilst configuring)
* non-empty bounds for `AsyncDrawable` when no dimensions are not yet available ([#189]) * non-empty bounds for `AsyncDrawable` when no dimensions are not yet available ([#189])
* `linkify` - option to use `LinkifyCompat` in `LinkifyPlugin` ([#201]) * `linkify` - option to use `LinkifyCompat` in `LinkifyPlugin` ([#201])
<br>Thanks to [@drakeet] <br>Thanks to [@drakeet]
* `MarkwonVisitor.BlockHandler` and `BlockHandlerDef` implementation to control how blocks insert new lines after them
```java ```java

View File

@ -0,0 +1,23 @@
package io.noties.markwon;
import androidx.annotation.NonNull;
import org.commonmark.node.Node;
/**
* @since $nap;
*/
public class BlockHandlerDef implements MarkwonVisitor.BlockHandler {
@Override
public void blockStart(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
visitor.ensureNewLine();
}
@Override
public void blockEnd(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
if (visitor.hasNext(node)) {
visitor.ensureNewLine();
visitor.forceNewLine();
}
}
}

View File

@ -23,6 +23,19 @@ public interface MarkwonVisitor extends Visitor {
void visit(@NonNull MarkwonVisitor visitor, @NonNull N n); void visit(@NonNull MarkwonVisitor visitor, @NonNull N n);
} }
/**
* Primary purpose is to control the spacing applied before/after certain blocks, which
* visitors are created elsewhere
*
* @since $nap;
*/
interface BlockHandler {
void blockStart(@NonNull MarkwonVisitor visitor, @NonNull Node node);
void blockEnd(@NonNull MarkwonVisitor visitor, @NonNull Node node);
}
interface Builder { interface Builder {
/** /**
@ -33,6 +46,16 @@ public interface MarkwonVisitor extends Visitor {
@NonNull @NonNull
<N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor); <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor);
/**
* @param blockHandler to handle block start/end
* @see BlockHandler
* @see BlockHandlerDef
* @since $nap;
*/
@SuppressWarnings("UnusedReturnValue")
@NonNull
Builder blockHandler(@NonNull BlockHandler blockHandler);
@NonNull @NonNull
MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps); MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps);
} }
@ -133,4 +156,14 @@ public interface MarkwonVisitor extends Visitor {
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
<N extends Node> void setSpansForNodeOptional(@NonNull Class<N> node, int start); <N extends Node> void setSpansForNodeOptional(@NonNull Class<N> node, int start);
/**
* @since $nap;
*/
void blockStart(@NonNull Node node);
/**
* @since $nap;
*/
void blockEnd(@NonNull Node node);
} }

View File

@ -45,15 +45,20 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes; private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes;
// @since $nap;
private final BlockHandler blockHandler;
MarkwonVisitorImpl( MarkwonVisitorImpl(
@NonNull MarkwonConfiguration configuration, @NonNull MarkwonConfiguration configuration,
@NonNull RenderProps renderProps, @NonNull RenderProps renderProps,
@NonNull SpannableBuilder builder, @NonNull SpannableBuilder builder,
@NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes,
@NonNull BlockHandler blockHandler) {
this.configuration = configuration; this.configuration = configuration;
this.renderProps = renderProps; this.renderProps = renderProps;
this.builder = builder; this.builder = builder;
this.nodes = nodes; this.nodes = nodes;
this.blockHandler = blockHandler;
} }
@Override @Override
@ -268,9 +273,20 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
} }
} }
@Override
public void blockStart(@NonNull Node node) {
blockHandler.blockStart(this, node);
}
@Override
public void blockEnd(@NonNull Node node) {
blockHandler.blockEnd(this, node);
}
static class BuilderImpl implements Builder { static class BuilderImpl implements Builder {
private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes = new HashMap<>(); private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes = new HashMap<>();
private BlockHandler blockHandler;
@NonNull @NonNull
@Override @Override
@ -290,14 +306,28 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
return this; return this;
} }
@NonNull
@Override
public Builder blockHandler(@NonNull BlockHandler blockHandler) {
this.blockHandler = blockHandler;
return this;
}
@NonNull @NonNull
@Override @Override
public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) { public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) {
// @since $nap;
BlockHandler blockHandler = this.blockHandler;
if (blockHandler == null) {
blockHandler = new BlockHandlerDef();
}
return new MarkwonVisitorImpl( return new MarkwonVisitorImpl(
configuration, configuration,
renderProps, renderProps,
new SpannableBuilder(), new SpannableBuilder(),
Collections.unmodifiableMap(nodes)); Collections.unmodifiableMap(nodes),
blockHandler);
} }
} }
} }

View File

@ -19,7 +19,7 @@ public class SoftBreakAddsNewLinePlugin extends AbstractMarkwonPlugin {
builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
visitor.forceNewLine(); visitor.ensureNewLine();
} }
}); });
} }

View File

@ -210,17 +210,14 @@ public class CorePlugin extends AbstractMarkwonPlugin {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull BlockQuote blockQuote) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull BlockQuote blockQuote) {
visitor.ensureNewLine(); visitor.blockStart(blockQuote);
final int length = visitor.length(); final int length = visitor.length();
visitor.visitChildren(blockQuote); visitor.visitChildren(blockQuote);
visitor.setSpansForNodeOptional(blockQuote, length); visitor.setSpansForNodeOptional(blockQuote, length);
if (visitor.hasNext(blockQuote)) { visitor.blockEnd(blockQuote);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
}); });
} }
@ -316,7 +313,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
@NonNull String code, @NonNull String code,
@NonNull Node node) { @NonNull Node node) {
visitor.ensureNewLine(); visitor.blockStart(node);
final int length = visitor.length(); final int length = visitor.length();
@ -333,10 +330,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
visitor.setSpansForNodeOptional(node, length); visitor.setSpansForNodeOptional(node, length);
if (visitor.hasNext(node)) { visitor.blockEnd(node);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
private static void bulletList(@NonNull MarkwonVisitor.Builder builder) { private static void bulletList(@NonNull MarkwonVisitor.Builder builder) {
@ -402,7 +396,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull ThematicBreak thematicBreak) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull ThematicBreak thematicBreak) {
visitor.ensureNewLine(); visitor.blockStart(thematicBreak);
final int length = visitor.length(); final int length = visitor.length();
@ -411,10 +405,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
visitor.setSpansForNodeOptional(thematicBreak, length); visitor.setSpansForNodeOptional(thematicBreak, length);
if (visitor.hasNext(thematicBreak)) { visitor.blockEnd(thematicBreak);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
}); });
} }
@ -424,7 +415,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
visitor.ensureNewLine(); visitor.blockStart(heading);
final int length = visitor.length(); final int length = visitor.length();
visitor.visitChildren(heading); visitor.visitChildren(heading);
@ -433,10 +424,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
visitor.setSpansForNodeOptional(heading, length); visitor.setSpansForNodeOptional(heading, length);
if (visitor.hasNext(heading)) { visitor.blockEnd(heading);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
}); });
} }
@ -467,7 +455,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
final boolean inTightList = isInTightList(paragraph); final boolean inTightList = isInTightList(paragraph);
if (!inTightList) { if (!inTightList) {
visitor.ensureNewLine(); visitor.blockStart(paragraph);
} }
final int length = visitor.length(); final int length = visitor.length();
@ -478,9 +466,8 @@ public class CorePlugin extends AbstractMarkwonPlugin {
// @since 1.1.1 apply paragraph span // @since 1.1.1 apply paragraph span
visitor.setSpansForNodeOptional(paragraph, length); visitor.setSpansForNodeOptional(paragraph, length);
if (!inTightList && visitor.hasNext(paragraph)) { if (!inTightList) {
visitor.ensureNewLine(); visitor.blockEnd(paragraph);
visitor.forceNewLine();
} }
} }
}); });

View File

@ -17,19 +17,16 @@ public class SimpleBlockNodeVisitor implements MarkwonVisitor.NodeVisitor<Node>
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Node node) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
visitor.blockStart(node);
// @since 3.0.1 we keep track of start in order to apply spans (optionally) // @since 3.0.1 we keep track of start in order to apply spans (optionally)
final int length = visitor.length(); final int length = visitor.length();
visitor.ensureNewLine();
visitor.visitChildren(node); visitor.visitChildren(node);
// @since 3.0.1 we apply optional spans // @since 3.0.1 we apply optional spans
visitor.setSpansForNodeOptional(node, length); visitor.setSpansForNodeOptional(node, length);
if (visitor.hasNext(node)) { visitor.blockEnd(node);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
} }

View File

@ -12,7 +12,8 @@ public class AbstractMarkwonVisitorImpl extends MarkwonVisitorImpl {
@NonNull MarkwonConfiguration configuration, @NonNull MarkwonConfiguration configuration,
@NonNull RenderProps renderProps, @NonNull RenderProps renderProps,
@NonNull SpannableBuilder spannableBuilder, @NonNull SpannableBuilder spannableBuilder,
@NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes,
super(configuration, renderProps, spannableBuilder, nodes); @NonNull BlockHandler blockHandler) {
super(configuration, renderProps, spannableBuilder, nodes, blockHandler);
} }
} }

View File

@ -43,7 +43,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
renderProps, renderProps,
spannableBuilder, spannableBuilder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
impl.clear(); impl.clear();
@ -61,7 +62,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
mock(RenderProps.class), mock(RenderProps.class),
builder, builder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
// at the start - won't add anything // at the start - won't add anything
impl.ensureNewLine(); impl.ensureNewLine();
@ -92,7 +94,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
mock(RenderProps.class), mock(RenderProps.class),
builder, builder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
assertEquals(0, builder.length()); assertEquals(0, builder.length());
@ -144,7 +147,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
mock(RenderProps.class), mock(RenderProps.class),
mock(SpannableBuilder.class), mock(SpannableBuilder.class),
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
final BlockQuote node = mock(BlockQuote.class); final BlockQuote node = mock(BlockQuote.class);
final Node child = mock(Node.class); final Node child = mock(Node.class);
@ -163,7 +167,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
mock(RenderProps.class), mock(RenderProps.class),
mock(SpannableBuilder.class), mock(SpannableBuilder.class),
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
final Node noNext = mock(Node.class); final Node noNext = mock(Node.class);
assertFalse(impl.hasNext(noNext)); assertFalse(impl.hasNext(noNext));
@ -195,7 +200,8 @@ public class MarkwonVisitorImplTest {
mock(MarkwonConfiguration.class), mock(MarkwonConfiguration.class),
mock(RenderProps.class), mock(RenderProps.class),
builder, builder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
for (int i = 0; i < 13; i++) { for (int i = 0; i < 13; i++) {
builder.setLength(i); builder.setLength(i);
@ -221,7 +227,8 @@ public class MarkwonVisitorImplTest {
configuration, configuration,
mock(RenderProps.class), mock(RenderProps.class),
mock(SpannableBuilder.class), mock(SpannableBuilder.class),
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
impl.setSpansForNode(Node.class, 0); impl.setSpansForNode(Node.class, 0);
@ -252,7 +259,8 @@ public class MarkwonVisitorImplTest {
configuration, configuration,
mock(RenderProps.class), mock(RenderProps.class),
builder, builder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap()); Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap(),
mock(MarkwonVisitor.BlockHandler.class));
// append something // append something
builder.append("no-spans-test"); builder.append("no-spans-test");

View File

@ -107,6 +107,12 @@ public class CorePluginTest {
return this; return this;
} }
@NonNull
@Override
public MarkwonVisitor.Builder blockHandler(@NonNull MarkwonVisitor.BlockHandler blockHandler) {
throw new RuntimeException();
}
@NonNull @NonNull
@Override @Override
public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) { public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) {

View File

@ -91,7 +91,8 @@ public class SyntaxHighlightTest {
configuration, configuration,
mock(RenderProps.class), mock(RenderProps.class),
new SpannableBuilder(), new SpannableBuilder(),
visitorMap); visitorMap,
mock(MarkwonVisitor.BlockHandler.class));
final SpannableBuilder builder = visitor.builder(); final SpannableBuilder builder = visitor.builder();

View File

@ -191,7 +191,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathBlock jLatexMathBlock) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathBlock jLatexMathBlock) {
visitor.ensureNewLine(); visitor.blockStart(jLatexMathBlock);
final String latex = jLatexMathBlock.latex(); final String latex = jLatexMathBlock.latex();
@ -217,10 +217,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
visitor.setSpans(length, span); visitor.setSpans(length, span);
if (visitor.hasNext(jLatexMathBlock)) { visitor.blockEnd(jLatexMathBlock);
visitor.ensureNewLine();
visitor.forceNewLine();
}
} }
}); });
} }

View File

@ -121,12 +121,15 @@ public class TablePlugin extends AbstractMarkwonPlugin {
@Override @Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBlock tableBlock) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBlock tableBlock) {
visitor.blockStart(tableBlock);
visitor.visitChildren(tableBlock); visitor.visitChildren(tableBlock);
if (visitor.hasNext(tableBlock)) { // if (visitor.hasNext(tableBlock)) {
visitor.ensureNewLine(); // visitor.ensureNewLine();
visitor.forceNewLine(); // visitor.forceNewLine();
} // }
visitor.blockEnd(tableBlock);
} }
}) })
.on(TableBody.class, new MarkwonVisitor.NodeVisitor<TableBody>() { .on(TableBody.class, new MarkwonVisitor.NodeVisitor<TableBody>() {

View File

@ -11,19 +11,19 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.commonmark.node.Heading; import org.commonmark.node.Heading;
import org.commonmark.node.Node;
import org.commonmark.node.Paragraph; import org.commonmark.node.Paragraph;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.BlockHandlerDef;
import io.noties.markwon.Markwon; import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonConfiguration; import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.MarkwonSpansFactory; import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.MarkwonVisitor; import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.RenderProps;
import io.noties.markwon.SoftBreakAddsNewLinePlugin; import io.noties.markwon.SoftBreakAddsNewLinePlugin;
import io.noties.markwon.SpanFactory;
import io.noties.markwon.core.CoreProps; import io.noties.markwon.core.CoreProps;
import io.noties.markwon.core.MarkwonTheme; import io.noties.markwon.core.MarkwonTheme;
import io.noties.markwon.core.spans.LastLineSpacingSpan; import io.noties.markwon.core.spans.LastLineSpacingSpan;
@ -52,7 +52,9 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions {
.add("softBreakAddsSpace", this::softBreakAddsSpace) .add("softBreakAddsSpace", this::softBreakAddsSpace)
.add("softBreakAddsNewLine", this::softBreakAddsNewLine) .add("softBreakAddsNewLine", this::softBreakAddsNewLine)
.add("additionalSpacing", this::additionalSpacing) .add("additionalSpacing", this::additionalSpacing)
.add("headingNoSpace", this::headingNoSpace); .add("headingNoSpace", this::headingNoSpace)
.add("headingNoSpaceBlockHandler", this::headingNoSpaceBlockHandler)
.add("allBlocksNoForcedLine", this::allBlocksNoForcedLine);
} }
@Override @Override
@ -311,6 +313,68 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions {
markwon.setMarkdown(textView, md); markwon.setMarkdown(textView, md);
} }
private void headingNoSpaceBlockHandler() {
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();
final String md = "" +
"# Title title title title title title title title title title \n\ntext text text text";
markwon.setMarkdown(textView, md);
}
private void allBlocksNoForcedLine() {
final MarkwonVisitor.BlockHandler blockHandler = new BlockHandlerDef() {
@Override
public void blockEnd(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
if (visitor.hasNext(node)) {
visitor.ensureNewLine();
}
}
};
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.blockHandler(blockHandler);
}
})
.build();
final String md = "" +
"# Hello there!\n\n" +
"* a first\n" +
"* second\n" +
"- third\n" +
"* * nested one\n\n" +
"> block quote\n\n" +
"> > and nested one\n\n" +
"```java\n" +
"final int i = 0;\n" +
"```\n\n";
markwon.setMarkdown(textView, md);
}
// public void step_6() { // public void step_6() {
// //
// final Markwon markwon = Markwon.builder(this) // final Markwon markwon = Markwon.builder(this)