BlockHandler abstraction in core module
This commit is contained in:
		
							parent
							
								
									69c2d1255c
								
							
						
					
					
						commit
						0b813e43f7
					
				| @ -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]) | ||||
| * `linkify` - option to use `LinkifyCompat` in `LinkifyPlugin` ([#201]) | ||||
| <br>Thanks to [@drakeet] | ||||
| * `MarkwonVisitor.BlockHandler` and `BlockHandlerDef` implementation to control how blocks insert new lines after them | ||||
| 
 | ||||
| 
 | ||||
| ```java | ||||
|  | ||||
| @ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -23,6 +23,19 @@ public interface MarkwonVisitor extends Visitor { | ||||
|         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 { | ||||
| 
 | ||||
|         /** | ||||
| @ -33,6 +46,16 @@ public interface MarkwonVisitor extends Visitor { | ||||
|         @NonNull | ||||
|         <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 | ||||
|         MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps); | ||||
|     } | ||||
| @ -133,4 +156,14 @@ public interface MarkwonVisitor extends Visitor { | ||||
|      */ | ||||
|     @SuppressWarnings("unused") | ||||
|     <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); | ||||
| } | ||||
|  | ||||
| @ -45,15 +45,20 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | ||||
| 
 | ||||
|     private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes; | ||||
| 
 | ||||
|     // @since $nap; | ||||
|     private final BlockHandler blockHandler; | ||||
| 
 | ||||
|     MarkwonVisitorImpl( | ||||
|             @NonNull MarkwonConfiguration configuration, | ||||
|             @NonNull RenderProps renderProps, | ||||
|             @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.renderProps = renderProps; | ||||
|         this.builder = builder; | ||||
|         this.nodes = nodes; | ||||
|         this.blockHandler = blockHandler; | ||||
|     } | ||||
| 
 | ||||
|     @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 { | ||||
| 
 | ||||
|         private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes = new HashMap<>(); | ||||
|         private BlockHandler blockHandler; | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
| @ -290,14 +306,28 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public Builder blockHandler(@NonNull BlockHandler blockHandler) { | ||||
|             this.blockHandler = blockHandler; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) { | ||||
|             // @since $nap; | ||||
|             BlockHandler blockHandler = this.blockHandler; | ||||
|             if (blockHandler == null) { | ||||
|                 blockHandler = new BlockHandlerDef(); | ||||
|             } | ||||
| 
 | ||||
|             return new MarkwonVisitorImpl( | ||||
|                     configuration, | ||||
|                     renderProps, | ||||
|                     new SpannableBuilder(), | ||||
|                     Collections.unmodifiableMap(nodes)); | ||||
|                     Collections.unmodifiableMap(nodes), | ||||
|                     blockHandler); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ public class SoftBreakAddsNewLinePlugin extends AbstractMarkwonPlugin { | ||||
|         builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||
|                 visitor.forceNewLine(); | ||||
|                 visitor.ensureNewLine(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -210,17 +210,14 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull BlockQuote blockQuote) { | ||||
| 
 | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.blockStart(blockQuote); | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
| 
 | ||||
|                 visitor.visitChildren(blockQuote); | ||||
|                 visitor.setSpansForNodeOptional(blockQuote, length); | ||||
| 
 | ||||
|                 if (visitor.hasNext(blockQuote)) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.forceNewLine(); | ||||
|                 } | ||||
|                 visitor.blockEnd(blockQuote); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| @ -316,7 +313,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|             @NonNull String code, | ||||
|             @NonNull Node node) { | ||||
| 
 | ||||
|         visitor.ensureNewLine(); | ||||
|         visitor.blockStart(node); | ||||
| 
 | ||||
|         final int length = visitor.length(); | ||||
| 
 | ||||
| @ -333,10 +330,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|         visitor.setSpansForNodeOptional(node, length); | ||||
| 
 | ||||
|         if (visitor.hasNext(node)) { | ||||
|             visitor.ensureNewLine(); | ||||
|             visitor.forceNewLine(); | ||||
|         } | ||||
|         visitor.blockEnd(node); | ||||
|     } | ||||
| 
 | ||||
|     private static void bulletList(@NonNull MarkwonVisitor.Builder builder) { | ||||
| @ -402,7 +396,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull ThematicBreak thematicBreak) { | ||||
| 
 | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.blockStart(thematicBreak); | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
| 
 | ||||
| @ -411,10 +405,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|                 visitor.setSpansForNodeOptional(thematicBreak, length); | ||||
| 
 | ||||
|                 if (visitor.hasNext(thematicBreak)) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.forceNewLine(); | ||||
|                 } | ||||
|                 visitor.blockEnd(thematicBreak); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| @ -424,7 +415,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
| 
 | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.blockStart(heading); | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
|                 visitor.visitChildren(heading); | ||||
| @ -433,10 +424,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|                 visitor.setSpansForNodeOptional(heading, length); | ||||
| 
 | ||||
|                 if (visitor.hasNext(heading)) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.forceNewLine(); | ||||
|                 } | ||||
|                 visitor.blockEnd(heading); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| @ -467,7 +455,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|                 final boolean inTightList = isInTightList(paragraph); | ||||
| 
 | ||||
|                 if (!inTightList) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.blockStart(paragraph); | ||||
|                 } | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
| @ -478,9 +466,8 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|                 // @since 1.1.1 apply paragraph span | ||||
|                 visitor.setSpansForNodeOptional(paragraph, length); | ||||
| 
 | ||||
|                 if (!inTightList && visitor.hasNext(paragraph)) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.forceNewLine(); | ||||
|                 if (!inTightList) { | ||||
|                     visitor.blockEnd(paragraph); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
| @ -17,19 +17,16 @@ public class SimpleBlockNodeVisitor implements MarkwonVisitor.NodeVisitor<Node> | ||||
|     @Override | ||||
|     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) | ||||
|         final int length = visitor.length(); | ||||
| 
 | ||||
|         visitor.ensureNewLine(); | ||||
| 
 | ||||
|         visitor.visitChildren(node); | ||||
| 
 | ||||
|         // @since 3.0.1 we apply optional spans | ||||
|         visitor.setSpansForNodeOptional(node, length); | ||||
| 
 | ||||
|         if (visitor.hasNext(node)) { | ||||
|             visitor.ensureNewLine(); | ||||
|             visitor.forceNewLine(); | ||||
|         } | ||||
|         visitor.blockEnd(node); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,7 +12,8 @@ public class AbstractMarkwonVisitorImpl extends MarkwonVisitorImpl { | ||||
|             @NonNull MarkwonConfiguration configuration, | ||||
|             @NonNull RenderProps renderProps, | ||||
|             @NonNull SpannableBuilder spannableBuilder, | ||||
|             @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { | ||||
|         super(configuration, renderProps, spannableBuilder, nodes); | ||||
|             @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes, | ||||
|             @NonNull BlockHandler blockHandler) { | ||||
|         super(configuration, renderProps, spannableBuilder, nodes, blockHandler); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -43,7 +43,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 renderProps, | ||||
|                 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(); | ||||
| 
 | ||||
| @ -61,7 +62,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 mock(RenderProps.class), | ||||
|                 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 | ||||
|         impl.ensureNewLine(); | ||||
| @ -92,7 +94,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 mock(RenderProps.class), | ||||
|                 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()); | ||||
| 
 | ||||
| @ -144,7 +147,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 mock(RenderProps.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 Node child = mock(Node.class); | ||||
| @ -163,7 +167,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 mock(RenderProps.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); | ||||
|         assertFalse(impl.hasNext(noNext)); | ||||
| @ -195,7 +200,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 mock(RenderProps.class), | ||||
|                 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++) { | ||||
|             builder.setLength(i); | ||||
| @ -221,7 +227,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 configuration, | ||||
|                 mock(RenderProps.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); | ||||
| 
 | ||||
| @ -252,7 +259,8 @@ public class MarkwonVisitorImplTest { | ||||
|                 configuration, | ||||
|                 mock(RenderProps.class), | ||||
|                 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 | ||||
|         builder.append("no-spans-test"); | ||||
|  | ||||
| @ -107,6 +107,12 @@ public class CorePluginTest { | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             @NonNull | ||||
|             @Override | ||||
|             public MarkwonVisitor.Builder blockHandler(@NonNull MarkwonVisitor.BlockHandler blockHandler) { | ||||
|                 throw new RuntimeException(); | ||||
|             } | ||||
| 
 | ||||
|             @NonNull | ||||
|             @Override | ||||
|             public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps) { | ||||
|  | ||||
| @ -91,7 +91,8 @@ public class SyntaxHighlightTest { | ||||
|                 configuration, | ||||
|                 mock(RenderProps.class), | ||||
|                 new SpannableBuilder(), | ||||
|                 visitorMap); | ||||
|                 visitorMap, | ||||
|                 mock(MarkwonVisitor.BlockHandler.class)); | ||||
| 
 | ||||
|         final SpannableBuilder builder = visitor.builder(); | ||||
| 
 | ||||
|  | ||||
| @ -191,7 +191,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathBlock jLatexMathBlock) { | ||||
| 
 | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.blockStart(jLatexMathBlock); | ||||
| 
 | ||||
|                 final String latex = jLatexMathBlock.latex(); | ||||
| 
 | ||||
| @ -217,10 +217,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|                 visitor.setSpans(length, span); | ||||
| 
 | ||||
|                 if (visitor.hasNext(jLatexMathBlock)) { | ||||
|                     visitor.ensureNewLine(); | ||||
|                     visitor.forceNewLine(); | ||||
|                 } | ||||
|                 visitor.blockEnd(jLatexMathBlock); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -121,12 +121,15 @@ public class TablePlugin extends AbstractMarkwonPlugin { | ||||
|                         @Override | ||||
|                         public void visit(@NonNull MarkwonVisitor visitor, @NonNull TableBlock tableBlock) { | ||||
| 
 | ||||
|                             visitor.blockStart(tableBlock); | ||||
| 
 | ||||
|                             visitor.visitChildren(tableBlock); | ||||
| 
 | ||||
|                             if (visitor.hasNext(tableBlock)) { | ||||
|                                 visitor.ensureNewLine(); | ||||
|                                 visitor.forceNewLine(); | ||||
|                             } | ||||
| //                            if (visitor.hasNext(tableBlock)) { | ||||
| //                                visitor.ensureNewLine(); | ||||
| //                                visitor.forceNewLine(); | ||||
| //                            } | ||||
|                             visitor.blockEnd(tableBlock); | ||||
|                         } | ||||
|                     }) | ||||
|                     .on(TableBody.class, new MarkwonVisitor.NodeVisitor<TableBody>() { | ||||
|  | ||||
| @ -11,19 +11,19 @@ import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.node.Paragraph; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| 
 | ||||
| import io.noties.markwon.AbstractMarkwonPlugin; | ||||
| import io.noties.markwon.BlockHandlerDef; | ||||
| import io.noties.markwon.Markwon; | ||||
| import io.noties.markwon.MarkwonConfiguration; | ||||
| import io.noties.markwon.MarkwonSpansFactory; | ||||
| import io.noties.markwon.MarkwonVisitor; | ||||
| import io.noties.markwon.RenderProps; | ||||
| import io.noties.markwon.SoftBreakAddsNewLinePlugin; | ||||
| import io.noties.markwon.SpanFactory; | ||||
| import io.noties.markwon.core.CoreProps; | ||||
| import io.noties.markwon.core.MarkwonTheme; | ||||
| import io.noties.markwon.core.spans.LastLineSpacingSpan; | ||||
| @ -52,7 +52,9 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions { | ||||
|                 .add("softBreakAddsSpace", this::softBreakAddsSpace) | ||||
|                 .add("softBreakAddsNewLine", this::softBreakAddsNewLine) | ||||
|                 .add("additionalSpacing", this::additionalSpacing) | ||||
|                 .add("headingNoSpace", this::headingNoSpace); | ||||
|                 .add("headingNoSpace", this::headingNoSpace) | ||||
|                 .add("headingNoSpaceBlockHandler", this::headingNoSpaceBlockHandler) | ||||
|                 .add("allBlocksNoForcedLine", this::allBlocksNoForcedLine); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -311,6 +313,68 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions { | ||||
|         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() { | ||||
| // | ||||
| //        final Markwon markwon = Markwon.builder(this) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov