Working with documentation
This commit is contained in:
		
							parent
							
								
									c390cb0502
								
							
						
					
					
						commit
						e1d5530962
					
				| @ -40,7 +40,8 @@ module.exports = { | ||||
|                         '/docs/v3/core/spans-factory.md', | ||||
|                         '/docs/v3/core/html-renderer.md', | ||||
|                         '/docs/v3/core/core-plugin.md', | ||||
|                         '/docs/v3/core/movement-method-plugin.md' | ||||
|                         '/docs/v3/core/movement-method-plugin.md', | ||||
|                         '/docs/v3/core/render-props.md' | ||||
|                     ] | ||||
|                 }, | ||||
|                 '/docs/v3/ext-latex/', | ||||
|  | ||||
| @ -86,7 +86,7 @@ and 2 themes included: Light & Dark. It can be downloaded from [releases](ht | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| [Help to improve][awesome_link] this section by submitting your application/library/anything | ||||
| [Help to improve][awesome_link] this section by submitting your application or library | ||||
| that is using `Markwon` | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1 +1,181 @@ | ||||
| # Configuration | ||||
| # Configuration | ||||
| 
 | ||||
| `MarkwonConfiguration` class holds common Markwon functionality. | ||||
| These are _configurable_ properties: | ||||
| * `SyntaxHighlight` | ||||
| * `LinkSpan.Resolver` | ||||
| * `UrlProcessor` | ||||
| * `ImageSizeResolver` | ||||
| * `MarkwonHtmlParser` | ||||
| 
 | ||||
| :::tip | ||||
| Additionally `MarkwonConfiguration` holds: | ||||
| * `MarkwonTheme` | ||||
| * `AsyncDrawableLoader` | ||||
| * `MarkwonHtmlRenderer` | ||||
| * `MarkwonSpansFactory` | ||||
| 
 | ||||
| Please note that these values can be retrieved from `MarkwonConfiguration` | ||||
| instance, but their _configuration_ must be done by a `Plugin` by overriding | ||||
| one of the methods: | ||||
| * `Plugin#configureTheme` | ||||
| * `Plugin#configureImages` | ||||
| * `Plugin#configureHtmlRenderer` | ||||
| * `Plugin#configureSpansFactory` | ||||
| ::: | ||||
| 
 | ||||
| ## SyntaxHighlight | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.syntaxHighlight(new SyntaxHighlightNoOp()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Use [syntax-highlight](/docs/v3/syntax-highlight/) to add syntax highlighting | ||||
| to your application | ||||
| ::: | ||||
| 
 | ||||
| ## LinkSpan.Resolver | ||||
| 
 | ||||
| React to a link click event. By default `LinkResolverDef` is used, | ||||
| which tries to start an Activity given the `link` argument. If no | ||||
| Activity can handle `link` `LinkResolverDef` silently ignores click event | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.linkResolver(new LinkSpan.Resolver() { | ||||
|                     @Override | ||||
|                     public void resolve(View view, @NonNull String link) { | ||||
|                         // react to link click here | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Please note that `Markwon` will apply `LinkMovementMethod` to a resulting TextView | ||||
| if there is none registered. if you wish to register own instance of a `MovementMethod` | ||||
| apply it directly to a TextView or use [MovementMethodPlugin](/docs/v3/core/movement-method-plugin.md) | ||||
| ::: | ||||
| 
 | ||||
| ## UrlProcessor | ||||
| 
 | ||||
| Process URLs in your markdown (for links and images). If not provided explicitly,  | ||||
| default **no-op** implementation will be used, which does not modify URLs (keeping them as-is). | ||||
| 
 | ||||
| `Markwon` provides 2 implementations of `UrlProcessor`: | ||||
| * `UrlProcessorRelativeToAbsolute` | ||||
| * `UrlProcessorAndroidAssets` | ||||
| 
 | ||||
| ### UrlProcessorRelativeToAbsolute | ||||
| 
 | ||||
| `UrlProcessorRelativeToAbsolute` can be used to make relative URL absolute. For example if an image is | ||||
| defined like this: `` and `UrlProcessorRelativeToAbsolute` | ||||
| is created with `https://github.com/noties/Markwon/raw/master/` as the base:  | ||||
| `new UrlProcessorRelativeToAbsolute("https://github.com/noties/Markwon/raw/master/")`, | ||||
| then final image will have `https://github.com/noties/Markwon/raw/master/art/image.JPG` | ||||
| as the destination. | ||||
| 
 | ||||
| ### UrlProcessorAndroidAssets | ||||
| 
 | ||||
| `UrlProcessorAndroidAssets` can be used to make processed links to point to Android assets folder. | ||||
| So an image: `` will have `file:///android_asset/art/image.JPG` as the | ||||
| destination. | ||||
| 
 | ||||
| :::tip | ||||
| Please note that `UrlProcessorAndroidAssets` will process only URLs that have no `scheme` information, | ||||
| so a `./art/image.png` will become `file:///android_asset/art/image.JPG` whilst `https://so.me/where.png` | ||||
| will be kept as-is. | ||||
| ::: | ||||
| 
 | ||||
| :::warning | ||||
| In order to display an image from assets you still need to register `ImagesPlugin#createWithAssets(Context)` | ||||
| plugin in resulting `Markwon` instance. As `UrlProcessorAndroidAssets` only | ||||
| _processes_ URLs and doesn't take any part in displaying an image. | ||||
| ::: | ||||
| 
 | ||||
| 
 | ||||
| ## ImageSizeResolver | ||||
| 
 | ||||
| `ImageSizeResolver` controls the size of an image to be displayed. Currently it | ||||
| handles only HTML images (specified via `img` tag). | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.imageSizeResolver(new ImageSizeResolver() { | ||||
|                     @NonNull | ||||
|                     @Override | ||||
|                     public Rect resolveImageSize( | ||||
|                             @Nullable ImageSize imageSize, | ||||
|                             @NonNull Rect imageBounds, | ||||
|                             int canvasWidth, | ||||
|                             float textSize) { | ||||
|                         return null; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| If not provided explicitly, default `ImageSizeResolverDef` implementation will be used. | ||||
| It handles 3 dimension units: | ||||
| * `%` (percent, relative to Canvas width) | ||||
| * `em` (relative to text size) | ||||
| * `px` (absolute size, every dimension that is not `%` or `em` is considered to be _absolute_) | ||||
| 
 | ||||
| ```html | ||||
| <img width="100%"> | ||||
| <img width="2em" height="10px"> | ||||
| <img style="{width: 100%; height: 8em;}"> | ||||
| ``` | ||||
| 
 | ||||
| `ImageSizeResolverDef` keeps the ratio of original image if one of the dimensions is missing. | ||||
| 
 | ||||
| :::warning Height% | ||||
| There is no support for `%` units for `height` dimension. This is due to the fact that | ||||
| height of an TextView in which markdown is displayed is non-stable and changes with time | ||||
| (for example when image is loaded and applied to a TextView it will _increase_ TextView's height), | ||||
| so we will have no point-of-reference from which to _calculate_ image height. | ||||
| ::: | ||||
| 
 | ||||
| :::tip | ||||
| `ImageSizeResolverDef` also takes care for an image to **not** exceed | ||||
| canvas width. If an image has greater width than a TextView Canvas, then | ||||
| image will be _scaled-down_ to fit the canvas. Please note that this rule | ||||
| applies only if image has no absolute sizes (for example width is specified | ||||
| in pixels). | ||||
| ::: | ||||
| 
 | ||||
| ## MarkwonHtmlParser | ||||
| 
 | ||||
| Specify which HTML parser to use. Default implementation is **no-op**. | ||||
| 
 | ||||
| :::warning | ||||
| One must explicitly use [HtmlPlugin](/docs/v3/html/) in order to display | ||||
| HTML content in markdown. Without specified HTML parser **no HTML content | ||||
| will be rendered**. | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|     .usePlugin(HtmlPlugin.create()) | ||||
| ``` | ||||
| 
 | ||||
| Please note that adding `HtmlPlugin` will take care of initializing parser, | ||||
| so after `HtmlPlugin` is used, no additional configuration steps are required. | ||||
| ::: | ||||
| @ -75,6 +75,8 @@ Beware of this if you would like to override only one of the list types. This is | ||||
| done to correspond to `commonmark-java` implementation. | ||||
| ::: | ||||
| 
 | ||||
| More information about props can be found [here](/docs/v3/core/render-props.md) | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| :::tip Soft line break | ||||
|  | ||||
| @ -113,6 +113,40 @@ be used. | ||||
| 
 | ||||
| ## MediaDecoder | ||||
| 
 | ||||
| By default `core` artifact comes with _default image decoder_ only. It's called | ||||
| `ImageMediaDecoder` and it can decode all the formats that `BitmapFactory#decodeStream(InputStream)` | ||||
| can. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(ImagesPlugin.create(this)) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
|                 builder.addMediaDecoder("text/plain", new TextPlainMediaDecoder()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| `MediaDecoder` is a class to turn `InputStream` into a `Drawable`: | ||||
| 
 | ||||
| ```java | ||||
| public abstract class MediaDecoder { | ||||
| 
 | ||||
|     @Nullable | ||||
|     public abstract Drawable decode(@NonNull InputStream inputStream); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| If you want to display GIF or SVG images also, you can use [image-gif](/docs/v3/image/gif.md) | ||||
| and [image-svg](/docs/v3/image/svg.md) modules. | ||||
| ::: | ||||
| 
 | ||||
| ##  | ||||
| 
 | ||||
| :::tip | ||||
| If you are using [html](/docs/v3/html/) you do not have to additionally setup | ||||
| images displayed via `<img>` tag, as `HtmlPlugin` automatically uses configured | ||||
|  | ||||
| @ -425,7 +425,7 @@ queried directly from a TextView. | ||||
| 
 | ||||
| ## What happens underneath | ||||
| 
 | ||||
| Here is an approximation of how a `Markwon` instance will handle plugins: | ||||
| Here is what happens inside `Markwon` when `setMarkdown` method is called: | ||||
| 
 | ||||
| ```java | ||||
| // `Markwon#create` implicitly uses CorePlugin | ||||
|  | ||||
							
								
								
									
										75
									
								
								docs/docs/v3/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								docs/docs/v3/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| # RenderProps <Badge text="3.0.0" /> | ||||
| 
 | ||||
| `RenderProps` encapsulates passing arguments from a node visitor to a node renderer. | ||||
| Without hardcoding arguments into an API method calls. | ||||
| 
 | ||||
| `RenderProps` is the state collection for `Props` that are set by a node visitor and | ||||
| retrieved by a node renderer. | ||||
| 
 | ||||
| ```java | ||||
| public class Prop<T> { | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static <T> Prop<T> of(@NonNull String name) { | ||||
|         return new Prop<>(name); | ||||
|     } | ||||
| 
 | ||||
|     /* ... */ | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| For example `CorePlugin` defines a _Heading level_ prop (inside `CoreProps` class): | ||||
| 
 | ||||
| ```java | ||||
| public static final Prop<Integer> HEADING_LEVEL = Prop.of("heading-level"); | ||||
| ``` | ||||
| 
 | ||||
| Then CorePlugin registers a `Heading` node visitor and applies heading value: | ||||
| 
 | ||||
| ```java | ||||
| @Override | ||||
| public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||
|         @Override | ||||
|         public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
|              | ||||
|             /* Heading node handling logic */ | ||||
| 
 | ||||
|             // set heading level | ||||
|             CoreProps.HEADING_LEVEL.set(visitor.renderProps(), heading.getLevel()); | ||||
|              | ||||
|             // a helper method to apply span(s) for a node  | ||||
|             // (internally obtains a SpanFactory for Heading or silently ignores | ||||
|             // this call if no factory for a Heading is registered) | ||||
|             visitor.setSpansForNodeOptional(heading, start); | ||||
| 
 | ||||
|             /* Heading node handling logic */ | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| And finally `HeadingSpanFactory` (which is also registered by `CorePlugin`): | ||||
| 
 | ||||
| ```java | ||||
| public class HeadingSpanFactory implements SpanFactory { | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|         return new HeadingSpan( | ||||
|                 configuration.theme(), | ||||
|                 CoreProps.HEADING_LEVEL.require(props) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| `Prop<T>` has these methods: | ||||
| 
 | ||||
| * `@Nullable T get(RenderProps)` - returns value stored in RenderProps or `null` if none is present | ||||
| * `@NonNull T get(RenderProps, @NonNull T defValue)` - returns value stored in RenderProps or default value (this method always return non-null value) | ||||
| * `@NonNull T require(RenderProps)` - returns value stored in RenderProps or _throws an exception_ if none is present | ||||
| * `void set(RenderProps, @Nullable T value)` - updates value stored in RenderProps, passing `null` as value is the same as calling `clear` | ||||
| * `void clear(RenderProps)` - clears value stored in RenderProps | ||||
| @ -1 +1,73 @@ | ||||
| # Visitor | ||||
| # Visitor | ||||
| 
 | ||||
| Starting with <Badge text="3.0.0" /> _visiting_ of parsed markdown | ||||
| nodes does not require creating own instance of commonmark-java `Visitor`, | ||||
| instead a composable/configurable `MarkwonVisitor` is used. | ||||
| 
 | ||||
| ## Visitor.Builder | ||||
| There is no need to create own instance of `MarkwonVisitor.Builder` as | ||||
| it is done by `Markwon` itself. One still can configure it as one wishes: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(contex) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||
|                     @Override | ||||
|                     public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||
|                         visitor.forceNewLine(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| `MarkwonVisitor` encapsulates most of the functionality of rendering parsed markdown. | ||||
| 
 | ||||
| It holds rendering configuration: | ||||
| * `MarkwonVisitor#configuration` - getter for current [MarkwonConfiguration](/docs/v3/core/configuration.md) | ||||
| * `MarkwonVisitor#renderProps` - getter for current [RenderProps](/docs/v3/core/render-props.md) | ||||
| * `MarkwonVisitor#builder` - getter for current `SpannableBuilder` | ||||
| 
 | ||||
| It contains also a number of utility functions: | ||||
| * `visitChildren(Node)` - will visit all children of supplied Node | ||||
| * `hasNext(Node)` - utility function to check if supplied Node has a Node after it (useful for white-space management, so there should be no blank new line after last BlockNode) | ||||
| * `ensureNewLine` - will insert a new line at current `SpannableBuilder` position only if current (last) character is not a new-line | ||||
| * `forceNewLine` - will insert a new line character without any condition checking | ||||
| * `length` - helper function to call `visitor.builder().length()`, returns current length of `SpannableBuilder` | ||||
| * `clear` - will clear state for `RenderProps` and `SpannableBuilder`, this is done by `Markwon` automatically after each render call | ||||
| 
 | ||||
| And some utility functions to control the spans: | ||||
| * `setSpans(int start, Object spans)` - will apply supplied `spans` on `SpannableBuilder` starting at `start` position and ending at `SpannableBuilder#length`. `spans` can be `null` (no spans will be applied) or an array of spans (each span of this array will be applied) | ||||
| * `setSpansForNodeOptional(N node, int start)` - helper method to set spans for specified `node` (internally obtains `SpanFactory` for that node and uses it to apply spans) | ||||
| * `setSpansForNode(N node, int start)` - almost the same as `setSpansForNodeOptional` but instead of silently ignoring call if none `SpanFactory` is registered, this method will throw an exception. | ||||
| 
 | ||||
| ```java | ||||
| @Override | ||||
| public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||
|         @Override | ||||
|         public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
| 
 | ||||
|             // or just `visitor.length()` | ||||
|             final int start = visitor.builder().length(); | ||||
| 
 | ||||
|             visitor.visitChildren(heading); | ||||
| 
 | ||||
|             // or just `visitor.setSpansForNodeOptional(heading, start)` | ||||
|             final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass()); | ||||
|             if (factory != null) { | ||||
|                 visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps())); | ||||
|             } | ||||
|              | ||||
|             if (visitor.hasNext(heading)) { | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.forceNewLine(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| ``` | ||||
| @ -79,6 +79,11 @@ public interface MarkwonVisitor extends Visitor { | ||||
|      */ | ||||
|     int length(); | ||||
| 
 | ||||
|     /** | ||||
|      * Clears state of visitor (both {@link RenderProps} and {@link SpannableBuilder} will be cleared | ||||
|      */ | ||||
|     void clear(); | ||||
| 
 | ||||
|     /** | ||||
|      * Sets <code>spans</code> to underlying {@link SpannableBuilder} from <em>start</em> | ||||
|      * to <em>{@link SpannableBuilder#length()}</em>. | ||||
| @ -88,11 +93,6 @@ public interface MarkwonVisitor extends Visitor { | ||||
|      */ | ||||
|     void setSpans(int start, @Nullable Object spans); | ||||
| 
 | ||||
|     /** | ||||
|      * Clears state of visitor (both {@link RenderProps} and {@link SpannableBuilder} will be cleared | ||||
|      */ | ||||
|     void clear(); | ||||
| 
 | ||||
|     /** | ||||
|      * Helper method to obtain and apply spans for supplied Node. Internally queries {@link SpanFactory} | ||||
|      * for the node (via {@link MarkwonSpansFactory#require(Class)} thus throwing an exception | ||||
|  | ||||
| @ -0,0 +1,110 @@ | ||||
| package ru.noties.markwon.utils; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Block; | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.node.Visitor; | ||||
| 
 | ||||
| import java.lang.reflect.InvocationHandler; | ||||
| import java.lang.reflect.Method; | ||||
| import java.lang.reflect.Proxy; | ||||
| 
 | ||||
| // utility class to print parsed Nodes hierarchy | ||||
| public abstract class DumpNodes { | ||||
| 
 | ||||
|     public interface NodeProcessor { | ||||
|         @NonNull | ||||
|         String process(@NonNull Node node); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static String dump(@NonNull Node node) { | ||||
|         return dump(node, null); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static String dump(@NonNull Node node, @Nullable NodeProcessor nodeProcessor) { | ||||
| 
 | ||||
|         final NodeProcessor processor = nodeProcessor != null | ||||
|                 ? nodeProcessor | ||||
|                 : new NodeProcessorToString(); | ||||
| 
 | ||||
|         final Indent indent = new Indent(); | ||||
|         final StringBuilder builder = new StringBuilder(); | ||||
|         final Visitor visitor = (Visitor) Proxy.newProxyInstance( | ||||
|                 Visitor.class.getClassLoader(), | ||||
|                 new Class[]{Visitor.class}, | ||||
|                 new InvocationHandler() { | ||||
|                     @Override | ||||
|                     public Object invoke(Object proxy, Method method, Object[] args) { | ||||
| 
 | ||||
|                         final Node argument = (Node) args[0]; | ||||
| 
 | ||||
|                         // initial indent | ||||
|                         indent.appendTo(builder); | ||||
| 
 | ||||
|                         // node info | ||||
|                         builder.append(processor.process(argument)); | ||||
| 
 | ||||
|                         if (argument instanceof Block) { | ||||
|                             builder.append(" [\n"); | ||||
|                             indent.increment(); | ||||
|                             visitChildren((Visitor) proxy, argument); | ||||
|                             indent.decrement(); | ||||
|                             indent.appendTo(builder); | ||||
|                             builder.append("]\n"); | ||||
|                         } else { | ||||
|                             builder.append('\n'); | ||||
|                         } | ||||
|                         return null; | ||||
|                     } | ||||
|                 }); | ||||
|         node.accept(visitor); | ||||
|         return builder.toString(); | ||||
|     } | ||||
| 
 | ||||
|     private DumpNodes() { | ||||
|     } | ||||
| 
 | ||||
|     private static class Indent { | ||||
| 
 | ||||
|         private int count; | ||||
| 
 | ||||
|         void increment() { | ||||
|             count += 1; | ||||
|         } | ||||
| 
 | ||||
|         void decrement() { | ||||
|             count -= 1; | ||||
|         } | ||||
| 
 | ||||
|         void appendTo(@NonNull StringBuilder builder) { | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 builder | ||||
|                         .append(' ') | ||||
|                         .append(' '); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void visitChildren(@NonNull Visitor visitor, @NonNull Node parent) { | ||||
|         Node node = parent.getFirstChild(); | ||||
|         while (node != null) { | ||||
|             // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no | ||||
|             // node after visiting it. So get the next node before visiting. | ||||
|             Node next = node.getNext(); | ||||
|             node.accept(visitor); | ||||
|             node = next; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static class NodeProcessorToString implements NodeProcessor { | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public String process(@NonNull Node node) { | ||||
|             return node.toString(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -13,6 +13,8 @@ import android.text.TextUtils; | ||||
| import org.commonmark.ext.gfm.tables.TableBlock; | ||||
| import org.commonmark.ext.gfm.tables.TablesExtension; | ||||
| import org.commonmark.node.FencedCodeBlock; | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.SoftLineBreak; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| @ -27,7 +29,9 @@ import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.SpanFactory; | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import ru.noties.markwon.core.CoreProps; | ||||
| import ru.noties.markwon.html.HtmlPlugin; | ||||
| import ru.noties.markwon.image.ImagesPlugin; | ||||
| import ru.noties.markwon.image.svg.SvgPlugin; | ||||
| @ -48,6 +52,36 @@ public class RecyclerActivity extends Activity { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_recycler); | ||||
| 
 | ||||
|         { | ||||
| final Markwon markwon = Markwon.builder(contex) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
| @Override | ||||
| public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||
|         @Override | ||||
|         public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
| 
 | ||||
|             // or just `visitor.length()` | ||||
|             final int start = visitor.builder().length(); | ||||
| 
 | ||||
|             visitor.visitChildren(heading); | ||||
| 
 | ||||
|             // or just `visitor.setSpansForNodeOptional(heading, start)` | ||||
|             final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass()); | ||||
|             if (factory != null) { | ||||
|                 visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps())); | ||||
|             } | ||||
| 
 | ||||
|             if (visitor.hasNext(heading)) { | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.forceNewLine(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|         }); | ||||
|         } | ||||
| 
 | ||||
|         // create MarkwonAdapter and register two blocks that will be rendered differently | ||||
|         // * fenced code block (can also specify the same Entry for indended code block) | ||||
|         // * table block | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov