Specific node visitors for core nodes
This commit is contained in:
parent
42aab2280e
commit
19cd94febb
@ -17,7 +17,11 @@ public interface MarkwonVisitor extends Visitor {
|
|||||||
interface Builder {
|
interface Builder {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
<N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<N> nodeVisitor);
|
<N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor);
|
||||||
|
|
||||||
|
// to obtain currently registered one
|
||||||
|
@Nullable
|
||||||
|
<N extends Node> NodeVisitor<N> registeredVisitor(@NonNull Class<N> node);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
MarkwonVisitor build(@NonNull MarkwonConfiguration configuration);
|
MarkwonVisitor build(@NonNull MarkwonConfiguration configuration);
|
||||||
|
@ -281,7 +281,7 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<N> nodeVisitor) {
|
public <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> nodeVisitor) {
|
||||||
// we should allow `null` to exclude node from being visited (for example to disable
|
// we should allow `null` to exclude node from being visited (for example to disable
|
||||||
// some functionality)
|
// some functionality)
|
||||||
if (nodeVisitor == null) {
|
if (nodeVisitor == null) {
|
||||||
@ -292,6 +292,13 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <N extends Node> NodeVisitor<N> registeredVisitor(@NonNull Class<N> node) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (NodeVisitor<N>) nodes.get(node);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration) {
|
public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.noties.markwon.core;
|
package ru.noties.markwon.core;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.commonmark.node.BlockQuote;
|
import org.commonmark.node.BlockQuote;
|
||||||
@ -13,9 +12,7 @@ import org.commonmark.node.HardLineBreak;
|
|||||||
import org.commonmark.node.Heading;
|
import org.commonmark.node.Heading;
|
||||||
import org.commonmark.node.IndentedCodeBlock;
|
import org.commonmark.node.IndentedCodeBlock;
|
||||||
import org.commonmark.node.Link;
|
import org.commonmark.node.Link;
|
||||||
import org.commonmark.node.ListBlock;
|
|
||||||
import org.commonmark.node.ListItem;
|
import org.commonmark.node.ListItem;
|
||||||
import org.commonmark.node.Node;
|
|
||||||
import org.commonmark.node.OrderedList;
|
import org.commonmark.node.OrderedList;
|
||||||
import org.commonmark.node.Paragraph;
|
import org.commonmark.node.Paragraph;
|
||||||
import org.commonmark.node.SoftLineBreak;
|
import org.commonmark.node.SoftLineBreak;
|
||||||
@ -24,21 +21,25 @@ import org.commonmark.node.Text;
|
|||||||
import org.commonmark.node.ThematicBreak;
|
import org.commonmark.node.ThematicBreak;
|
||||||
|
|
||||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
|
||||||
import ru.noties.markwon.MarkwonVisitor;
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.BlockQuoteNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.CodeBlockNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.CodeNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.EmphasisNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.HardLineBreakNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.HeadingNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.LinkNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.ListBlockNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.ListItemNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.ParagraphNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.SoftLineBreakNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.StrongEmphasisNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.TextNodeVisitor;
|
||||||
|
import ru.noties.markwon.core.visitor.ThematicBreakNodeVisitor;
|
||||||
import ru.noties.markwon.spans.OrderedListItemSpan;
|
import ru.noties.markwon.spans.OrderedListItemSpan;
|
||||||
|
|
||||||
public class CorePlugin extends AbstractMarkwonPlugin {
|
public class CorePlugin extends AbstractMarkwonPlugin {
|
||||||
|
|
||||||
// todo: factory. Logically it must be here only, but in order to make spans
|
|
||||||
// uniform in HTML (for example) we should expose it... Anyway, this factory _must_
|
|
||||||
// include only _core_ spans
|
|
||||||
|
|
||||||
// todo: softBreak adds new line should be here (or maybe removed even?)
|
|
||||||
|
|
||||||
// todo: add a simple HTML handler
|
|
||||||
// todo: configure primitive images (without okhttp -> just HttpUrlConnection and simple types (static, data)
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static CorePlugin create() {
|
public static CorePlugin create() {
|
||||||
return create(false);
|
return create(false);
|
||||||
@ -72,7 +73,6 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
softLineBreak(builder);
|
softLineBreak(builder);
|
||||||
hardLineBreak(builder);
|
hardLineBreak(builder);
|
||||||
paragraph(builder);
|
paragraph(builder);
|
||||||
// image(builder);
|
|
||||||
link(builder);
|
link(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,303 +88,66 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void text(@NonNull MarkwonVisitor.Builder builder) {
|
protected void text(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Text.class, new MarkwonVisitor.NodeVisitor<Text>() {
|
builder.on(Text.class, new TextNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
|
||||||
visitor.builder().append(text.getLiteral());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void strongEmphasis(@NonNull MarkwonVisitor.Builder builder) {
|
protected void strongEmphasis(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(StrongEmphasis.class, new MarkwonVisitor.NodeVisitor<StrongEmphasis>() {
|
builder.on(StrongEmphasis.class, new StrongEmphasisNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) {
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.visitChildren(strongEmphasis);
|
|
||||||
visitor.setSpans(length, visitor.factory().strongEmphasis());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void emphasis(@NonNull MarkwonVisitor.Builder builder) {
|
protected void emphasis(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Emphasis.class, new MarkwonVisitor.NodeVisitor<Emphasis>() {
|
builder.on(Emphasis.class, new EmphasisNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) {
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.visitChildren(emphasis);
|
|
||||||
visitor.setSpans(length, visitor.factory().emphasis());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void blockQuote(@NonNull MarkwonVisitor.Builder builder) {
|
protected void blockQuote(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(BlockQuote.class, new MarkwonVisitor.NodeVisitor<BlockQuote>() {
|
builder.on(BlockQuote.class, new BlockQuoteNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull BlockQuote blockQuote) {
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.incrementBlockIndent();
|
|
||||||
visitor.visitChildren(blockQuote);
|
|
||||||
visitor.setSpans(length, visitor.factory().blockQuote(visitor.theme()));
|
|
||||||
visitor.decrementBlockIndent();
|
|
||||||
|
|
||||||
if (visitor.hasNext(blockQuote)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void code(@NonNull MarkwonVisitor.Builder builder) {
|
protected void code(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Code.class, new MarkwonVisitor.NodeVisitor<Code>() {
|
builder.on(Code.class, new CodeNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Code code) {
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
|
|
||||||
// NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces
|
|
||||||
// unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted
|
|
||||||
visitor.builder()
|
|
||||||
.append('\u00a0')
|
|
||||||
.append(code.getLiteral())
|
|
||||||
.append('\u00a0');
|
|
||||||
|
|
||||||
visitor.setSpans(length, visitor.factory().code(visitor.theme(), false));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void fencedCodeBlock(@NonNull MarkwonVisitor.Builder builder) {
|
protected void fencedCodeBlock(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(FencedCodeBlock.class, new MarkwonVisitor.NodeVisitor<FencedCodeBlock>() {
|
builder.on(FencedCodeBlock.class, new CodeBlockNodeVisitor.Fenced());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull FencedCodeBlock fencedCodeBlock) {
|
|
||||||
visitCodeBlock(visitor, fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral(), fencedCodeBlock);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void indentedCodeBlock(@NonNull MarkwonVisitor.Builder builder) {
|
protected void indentedCodeBlock(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(IndentedCodeBlock.class, new MarkwonVisitor.NodeVisitor<IndentedCodeBlock>() {
|
builder.on(IndentedCodeBlock.class, new CodeBlockNodeVisitor.Indented());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull IndentedCodeBlock indentedCodeBlock) {
|
|
||||||
visitCodeBlock(visitor, null, indentedCodeBlock.getLiteral(), indentedCodeBlock);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void visitCodeBlock(
|
|
||||||
@NonNull MarkwonVisitor visitor,
|
|
||||||
@Nullable String info,
|
|
||||||
@NonNull String code,
|
|
||||||
@NonNull Node node) {
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
|
|
||||||
visitor.builder()
|
|
||||||
.append('\u00a0').append('\n')
|
|
||||||
.append(visitor.configuration().syntaxHighlight().highlight(info, code));
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
visitor.builder().append('\u00a0');
|
|
||||||
|
|
||||||
visitor.setSpans(length, visitor.factory().code(visitor.theme(), true));
|
|
||||||
|
|
||||||
if (visitor.hasNext(node)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void bulletList(@NonNull MarkwonVisitor.Builder builder) {
|
protected void bulletList(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(BulletList.class, new MarkwonVisitor.NodeVisitor<BulletList>() {
|
builder.on(BulletList.class, new ListBlockNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull BulletList bulletList) {
|
|
||||||
visitList(visitor, bulletList);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void orderedList(@NonNull MarkwonVisitor.Builder builder) {
|
protected void orderedList(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(OrderedList.class, new MarkwonVisitor.NodeVisitor<OrderedList>() {
|
builder.on(OrderedList.class, new ListBlockNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull OrderedList orderedList) {
|
|
||||||
visitList(visitor, orderedList);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void visitList(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
visitor.visitChildren(node);
|
|
||||||
|
|
||||||
if (visitor.hasNext(node)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
if (visitor.listLevel() == 0
|
|
||||||
&& visitor.blockIndent() == 0) {
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void listItem(@NonNull MarkwonVisitor.Builder builder) {
|
protected void listItem(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(ListItem.class, new MarkwonVisitor.NodeVisitor<ListItem>() {
|
builder.on(ListItem.class, new ListItemNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull ListItem listItem) {
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
|
|
||||||
visitor.incrementBlockIndent();
|
|
||||||
visitor.incrementListLevel();
|
|
||||||
|
|
||||||
final Node parent = listItem.getParent();
|
|
||||||
if (parent instanceof OrderedList) {
|
|
||||||
|
|
||||||
final int start = ((OrderedList) parent).getStartNumber();
|
|
||||||
|
|
||||||
visitor.visitChildren(listItem);
|
|
||||||
visitor.setSpans(length, visitor.factory().orderedListItem(visitor.theme(), start));
|
|
||||||
|
|
||||||
|
|
||||||
// after we have visited the children increment start number
|
|
||||||
final OrderedList orderedList = (OrderedList) parent;
|
|
||||||
orderedList.setStartNumber(orderedList.getStartNumber() + 1);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
visitor.visitChildren(listItem);
|
|
||||||
visitor.setSpans(length, visitor.factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
visitor.decrementBlockIndent();
|
|
||||||
visitor.decrementListLevel();
|
|
||||||
|
|
||||||
if (visitor.hasNext(listItem)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void thematicBreak(@NonNull MarkwonVisitor.Builder builder) {
|
protected void thematicBreak(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(ThematicBreak.class, new MarkwonVisitor.NodeVisitor<ThematicBreak>() {
|
builder.on(ThematicBreak.class, new ThematicBreakNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull ThematicBreak thematicBreak) {
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
|
|
||||||
// without space it won't render
|
|
||||||
visitor.builder().append('\u00a0');
|
|
||||||
|
|
||||||
visitor.setSpans(length, visitor.factory().thematicBreak(visitor.theme()));
|
|
||||||
|
|
||||||
if (visitor.hasNext(thematicBreak)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void heading(@NonNull MarkwonVisitor.Builder builder) {
|
protected void heading(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
|
builder.on(Heading.class, new HeadingNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
|
|
||||||
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.visitChildren(heading);
|
|
||||||
visitor.setSpans(length, visitor.factory().heading(visitor.theme(), heading.getLevel()));
|
|
||||||
|
|
||||||
if (visitor.hasNext(heading)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void softLineBreak(@NonNull MarkwonVisitor.Builder builder) {
|
protected void softLineBreak(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() {
|
builder.on(SoftLineBreak.class, new SoftLineBreakNodeVisitor(softBreakAddsNewLine));
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
|
|
||||||
if (softBreakAddsNewLine) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
} else {
|
|
||||||
visitor.builder().append(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void hardLineBreak(@NonNull MarkwonVisitor.Builder builder) {
|
protected void hardLineBreak(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(HardLineBreak.class, new MarkwonVisitor.NodeVisitor<HardLineBreak>() {
|
builder.on(HardLineBreak.class, new HardLineBreakNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HardLineBreak hardLineBreak) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paragraph(@NonNull MarkwonVisitor.Builder builder) {
|
protected void paragraph(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Paragraph.class, new MarkwonVisitor.NodeVisitor<Paragraph>() {
|
builder.on(Paragraph.class, new ParagraphNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Paragraph paragraph) {
|
|
||||||
|
|
||||||
final boolean inTightList = isInTightList(paragraph);
|
|
||||||
|
|
||||||
if (!inTightList) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.visitChildren(paragraph);
|
|
||||||
|
|
||||||
// @since 1.1.1 apply paragraph span
|
|
||||||
visitor.setSpans(length, visitor.factory().paragraph(inTightList));
|
|
||||||
|
|
||||||
if (!inTightList && visitor.hasNext(paragraph)) {
|
|
||||||
visitor.ensureNewLine();
|
|
||||||
visitor.forceNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void link(@NonNull MarkwonVisitor.Builder builder) {
|
protected void link(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Link.class, new MarkwonVisitor.NodeVisitor<Link>() {
|
builder.on(Link.class, new LinkNodeVisitor());
|
||||||
@Override
|
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Link link) {
|
|
||||||
final int length = visitor.length();
|
|
||||||
visitor.visitChildren(link);
|
|
||||||
final MarkwonConfiguration configuration = visitor.configuration();
|
|
||||||
final String destination = configuration.urlProcessor().process(link.getDestination());
|
|
||||||
visitor.setSpans(length, visitor.factory().link(visitor.theme(), destination, configuration.linkResolver()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isInTightList(@NonNull Paragraph paragraph) {
|
|
||||||
final Node parent = paragraph.getParent();
|
|
||||||
if (parent != null) {
|
|
||||||
final Node gramps = parent.getParent();
|
|
||||||
if (gramps instanceof ListBlock) {
|
|
||||||
ListBlock list = (ListBlock) gramps;
|
|
||||||
return list.isTight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,26 @@
|
|||||||
package ru.noties.markwon.core.visitor;
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
public class BlockQuoteNodeVisitor {
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.BlockQuote;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class BlockQuoteNodeVisitor implements MarkwonVisitor.NodeVisitor<BlockQuote> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull BlockQuote blockQuote) {
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.incrementBlockIndent();
|
||||||
|
visitor.visitChildren(blockQuote);
|
||||||
|
visitor.setSpans(length, visitor.factory().blockQuote(visitor.theme()));
|
||||||
|
visitor.decrementBlockIndent();
|
||||||
|
|
||||||
|
if (visitor.hasNext(blockQuote)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.commonmark.node.FencedCodeBlock;
|
||||||
|
import org.commonmark.node.IndentedCodeBlock;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public abstract class CodeBlockNodeVisitor {
|
||||||
|
|
||||||
|
public static class Fenced implements MarkwonVisitor.NodeVisitor<FencedCodeBlock> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull FencedCodeBlock fencedCodeBlock) {
|
||||||
|
visitCodeBlock(visitor, fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral(), fencedCodeBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Indented implements MarkwonVisitor.NodeVisitor<IndentedCodeBlock> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull IndentedCodeBlock indentedCodeBlock) {
|
||||||
|
visitCodeBlock(visitor, null, indentedCodeBlock.getLiteral(), indentedCodeBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void visitCodeBlock(
|
||||||
|
@NonNull MarkwonVisitor visitor,
|
||||||
|
@Nullable String info,
|
||||||
|
@NonNull String code,
|
||||||
|
@NonNull Node node) {
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
|
visitor.builder()
|
||||||
|
.append('\u00a0').append('\n')
|
||||||
|
.append(visitor.configuration().syntaxHighlight().highlight(info, code));
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
visitor.builder().append('\u00a0');
|
||||||
|
|
||||||
|
visitor.setSpans(length, visitor.factory().code(visitor.theme(), true));
|
||||||
|
|
||||||
|
if (visitor.hasNext(node)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Code;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class CodeNodeVisitor implements MarkwonVisitor.NodeVisitor<Code> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Code code) {
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
|
// NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces
|
||||||
|
// unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted
|
||||||
|
visitor.builder()
|
||||||
|
.append('\u00a0')
|
||||||
|
.append(code.getLiteral())
|
||||||
|
.append('\u00a0');
|
||||||
|
|
||||||
|
visitor.setSpans(length, visitor.factory().code(visitor.theme(), false));
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,16 @@
|
|||||||
package ru.noties.markwon.core.visitor;
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
public class EmphasisNodeVisitor {
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Emphasis;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class EmphasisNodeVisitor implements MarkwonVisitor.NodeVisitor<Emphasis> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) {
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.visitChildren(emphasis);
|
||||||
|
visitor.setSpans(length, visitor.factory().emphasis());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.HardLineBreak;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class HardLineBreakNodeVisitor implements MarkwonVisitor.NodeVisitor<HardLineBreak> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HardLineBreak hardLineBreak) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Heading;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class HeadingNodeVisitor implements MarkwonVisitor.NodeVisitor<Heading> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.visitChildren(heading);
|
||||||
|
visitor.setSpans(length, visitor.factory().heading(visitor.theme(), heading.getLevel()));
|
||||||
|
|
||||||
|
if (visitor.hasNext(heading)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Link;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonConfiguration;
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class LinkNodeVisitor implements MarkwonVisitor.NodeVisitor<Link> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Link link) {
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.visitChildren(link);
|
||||||
|
final MarkwonConfiguration configuration = visitor.configuration();
|
||||||
|
final String destination = configuration.urlProcessor().process(link.getDestination());
|
||||||
|
visitor.setSpans(length, visitor.factory().link(visitor.theme(), destination, configuration.linkResolver()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class ListBlockNodeVisitor implements MarkwonVisitor.NodeVisitor<Node> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Node node) {
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
visitor.visitChildren(node);
|
||||||
|
|
||||||
|
if (visitor.hasNext(node)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.ListItem;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.node.OrderedList;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class ListItemNodeVisitor implements MarkwonVisitor.NodeVisitor<ListItem> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull ListItem listItem) {
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
|
visitor.incrementBlockIndent();
|
||||||
|
visitor.incrementListLevel();
|
||||||
|
|
||||||
|
final Node parent = listItem.getParent();
|
||||||
|
if (parent instanceof OrderedList) {
|
||||||
|
|
||||||
|
final int start = ((OrderedList) parent).getStartNumber();
|
||||||
|
|
||||||
|
visitor.visitChildren(listItem);
|
||||||
|
visitor.setSpans(length, visitor.factory().orderedListItem(visitor.theme(), start));
|
||||||
|
|
||||||
|
|
||||||
|
// after we have visited the children increment start number
|
||||||
|
final OrderedList orderedList = (OrderedList) parent;
|
||||||
|
orderedList.setStartNumber(orderedList.getStartNumber() + 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
visitor.visitChildren(listItem);
|
||||||
|
visitor.setSpans(length, visitor.factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.decrementBlockIndent();
|
||||||
|
visitor.decrementListLevel();
|
||||||
|
|
||||||
|
if (visitor.hasNext(listItem)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.ListBlock;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.node.Paragraph;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class ParagraphNodeVisitor implements MarkwonVisitor.NodeVisitor<Paragraph> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Paragraph paragraph) {
|
||||||
|
|
||||||
|
final boolean inTightList = isInTightList(paragraph);
|
||||||
|
|
||||||
|
if (!inTightList) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.visitChildren(paragraph);
|
||||||
|
|
||||||
|
// @since 1.1.1 apply paragraph span
|
||||||
|
visitor.setSpans(length, visitor.factory().paragraph(inTightList));
|
||||||
|
|
||||||
|
if (!inTightList && visitor.hasNext(paragraph)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInTightList(@NonNull Paragraph paragraph) {
|
||||||
|
final Node parent = paragraph.getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
final Node gramps = parent.getParent();
|
||||||
|
if (gramps instanceof ListBlock) {
|
||||||
|
ListBlock list = (ListBlock) gramps;
|
||||||
|
return list.isTight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.SoftLineBreak;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class SoftLineBreakNodeVisitor implements MarkwonVisitor.NodeVisitor<SoftLineBreak> {
|
||||||
|
|
||||||
|
private final boolean softBreakAddsNewLine;
|
||||||
|
|
||||||
|
public SoftLineBreakNodeVisitor(boolean softBreakAddsNewLine) {
|
||||||
|
this.softBreakAddsNewLine = softBreakAddsNewLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
|
||||||
|
if (softBreakAddsNewLine) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
} else {
|
||||||
|
visitor.builder().append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,16 @@
|
|||||||
package ru.noties.markwon.core.visitor;
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
public class StrongEmphasisNodeVisitor {
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.StrongEmphasis;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class StrongEmphasisNodeVisitor implements MarkwonVisitor.NodeVisitor<StrongEmphasis> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) {
|
||||||
|
final int length = visitor.length();
|
||||||
|
visitor.visitChildren(strongEmphasis);
|
||||||
|
visitor.setSpans(length, visitor.factory().strongEmphasis());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
package ru.noties.markwon.core.visitor;
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
public class TextNodeVisitor {
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.Text;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class TextNodeVisitor implements MarkwonVisitor.NodeVisitor<Text> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
||||||
|
visitor.builder().append(text.getLiteral());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.noties.markwon.core.visitor;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.node.ThematicBreak;
|
||||||
|
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
public class ThematicBreakNodeVisitor implements MarkwonVisitor.NodeVisitor<ThematicBreak> {
|
||||||
|
@Override
|
||||||
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull ThematicBreak thematicBreak) {
|
||||||
|
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
|
// without space it won't render
|
||||||
|
visitor.builder().append('\u00a0');
|
||||||
|
|
||||||
|
visitor.setSpans(length, visitor.factory().thematicBreak(visitor.theme()));
|
||||||
|
|
||||||
|
if (visitor.hasNext(thematicBreak)) {
|
||||||
|
visitor.ensureNewLine();
|
||||||
|
visitor.forceNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user