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 { | ||||
| 
 | ||||
|         @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 | ||||
|         MarkwonVisitor build(@NonNull MarkwonConfiguration configuration); | ||||
|  | ||||
| @ -281,7 +281,7 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | ||||
| 
 | ||||
|         @NonNull | ||||
|         @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 | ||||
|             // some functionality) | ||||
|             if (nodeVisitor == null) { | ||||
| @ -292,6 +292,13 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Nullable | ||||
|         @Override | ||||
|         public <N extends Node> NodeVisitor<N> registeredVisitor(@NonNull Class<N> node) { | ||||
|             //noinspection unchecked | ||||
|             return (NodeVisitor<N>) nodes.get(node); | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration) { | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package ru.noties.markwon.core; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import org.commonmark.node.BlockQuote; | ||||
| @ -13,9 +12,7 @@ import org.commonmark.node.HardLineBreak; | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.IndentedCodeBlock; | ||||
| import org.commonmark.node.Link; | ||||
| import org.commonmark.node.ListBlock; | ||||
| import org.commonmark.node.ListItem; | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.node.OrderedList; | ||||
| import org.commonmark.node.Paragraph; | ||||
| import org.commonmark.node.SoftLineBreak; | ||||
| @ -24,21 +21,25 @@ import org.commonmark.node.Text; | ||||
| import org.commonmark.node.ThematicBreak; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| 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; | ||||
| 
 | ||||
| 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 | ||||
|     public static CorePlugin create() { | ||||
|         return create(false); | ||||
| @ -72,7 +73,6 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|         softLineBreak(builder); | ||||
|         hardLineBreak(builder); | ||||
|         paragraph(builder); | ||||
| //        image(builder); | ||||
|         link(builder); | ||||
|     } | ||||
| 
 | ||||
| @ -88,303 +88,66 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|     } | ||||
| 
 | ||||
|     protected void text(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Text.class, new MarkwonVisitor.NodeVisitor<Text>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) { | ||||
|                 visitor.builder().append(text.getLiteral()); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(Text.class, new TextNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void strongEmphasis(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(StrongEmphasis.class, new 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()); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(StrongEmphasis.class, new StrongEmphasisNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void emphasis(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Emphasis.class, new 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()); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(Emphasis.class, new EmphasisNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void blockQuote(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(BlockQuote.class, new 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(BlockQuote.class, new BlockQuoteNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void code(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Code.class, new 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)); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(Code.class, new CodeNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void fencedCodeBlock(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(FencedCodeBlock.class, new MarkwonVisitor.NodeVisitor<FencedCodeBlock>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull FencedCodeBlock fencedCodeBlock) { | ||||
|                 visitCodeBlock(visitor, fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral(), fencedCodeBlock); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(FencedCodeBlock.class, new CodeBlockNodeVisitor.Fenced()); | ||||
|     } | ||||
| 
 | ||||
|     protected void indentedCodeBlock(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(IndentedCodeBlock.class, new MarkwonVisitor.NodeVisitor<IndentedCodeBlock>() { | ||||
|             @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(); | ||||
|         } | ||||
|         builder.on(IndentedCodeBlock.class, new CodeBlockNodeVisitor.Indented()); | ||||
|     } | ||||
| 
 | ||||
|     protected void bulletList(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(BulletList.class, new MarkwonVisitor.NodeVisitor<BulletList>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull BulletList bulletList) { | ||||
|                 visitList(visitor, bulletList); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(BulletList.class, new ListBlockNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void orderedList(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(OrderedList.class, new MarkwonVisitor.NodeVisitor<OrderedList>() { | ||||
|             @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(); | ||||
|             } | ||||
|         } | ||||
|         builder.on(OrderedList.class, new ListBlockNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void listItem(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(ListItem.class, new 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(ListItem.class, new ListItemNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void thematicBreak(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(ThematicBreak.class, new 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(ThematicBreak.class, new ThematicBreakNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void heading(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Heading.class, new 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(Heading.class, new HeadingNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void softLineBreak(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||
|                 if (softBreakAddsNewLine) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                 } else { | ||||
|                     visitor.builder().append(' '); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(SoftLineBreak.class, new SoftLineBreakNodeVisitor(softBreakAddsNewLine)); | ||||
|     } | ||||
| 
 | ||||
|     protected void hardLineBreak(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(HardLineBreak.class, new MarkwonVisitor.NodeVisitor<HardLineBreak>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull HardLineBreak hardLineBreak) { | ||||
|                 visitor.ensureNewLine(); | ||||
|             } | ||||
|         }); | ||||
|         builder.on(HardLineBreak.class, new HardLineBreakNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void paragraph(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Paragraph.class, new 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         builder.on(Paragraph.class, new ParagraphNodeVisitor()); | ||||
|     } | ||||
| 
 | ||||
|     protected void link(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Link.class, new 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())); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|         builder.on(Link.class, new LinkNodeVisitor()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,26 @@ | ||||
| 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; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 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
	 Dimitry Ivanov
						Dimitry Ivanov