Working on core module documentation
This commit is contained in:
		
							parent
							
								
									02e7539881
								
							
						
					
					
						commit
						d91f367e0a
					
				| @ -17,7 +17,7 @@ | |||||||
|       </p> |       </p> | ||||||
|       <p> |       <p> | ||||||
|         <em> |         <em> | ||||||
|           ** For a little more sophisticated commonmark sandbox editor |           ** For a more sophisticated commonmark sandbox editor | ||||||
|           <a href="https://spec.commonmark.org/dingus/">the dingus</a> can be used. |           <a href="https://spec.commonmark.org/dingus/">the dingus</a> can be used. | ||||||
|         </em> |         </em> | ||||||
|       </p> |       </p> | ||||||
|  | |||||||
| @ -1,24 +1,33 @@ | |||||||
| module.exports = { | module.exports = { | ||||||
|     base: '/Markwon/', |     base: '/Markwon/', | ||||||
|     title: 'Markwon', |     title: 'Markwon', | ||||||
|     description: 'Android markdown library based on commonmark specification', |     description: 'Android markdown library based on commonmark specification that renders markdown as system-native Spannables (no WebView)', | ||||||
|     head: [ |     head: [ | ||||||
|         ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png?v=1' }], |         ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png?v=1' }], | ||||||
|         ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png?v=1' }], |         ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png?v=1' }], | ||||||
|         ['link', { rel: 'icon', href: '/favicon.ico?v=1' }], |         ['link', { rel: 'icon', href: '/favicon.ico?v=1' }], | ||||||
|         ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png?v=1' }], |         ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png?v=1' }], | ||||||
|         ['link', { rel: 'manifest', href: '/manifest.json?v=1' }], |         ['link', { rel: 'manifest', href: '/manifest.json?v=1' }], | ||||||
|  |         ['meta', { name: 'keywords', content: 'android,markdown,library,spannable,markwon,commonmark' }] | ||||||
|     ], |     ], | ||||||
|     themeConfig: { |     themeConfig: { | ||||||
|         nav: [ |         nav: [ | ||||||
|             { text: 'Install', link: '/docs/install.md' }, |             { text: 'Install', link: '/docs/install.md' }, | ||||||
|             { text: 'Changelog', link: '/CHANGELOG.md' }, |             { text: 'Changelog', link: '/CHANGELOG.md' }, | ||||||
|  |             { | ||||||
|  |                 text: 'API Version', | ||||||
|  |                 items: [ | ||||||
|  |                     { text: 'Current (3.x.x)', link: '/' }, | ||||||
|  |                     { text: 'Legacy (2.x.x)', link: '/docs/v2/' } | ||||||
|  |                 ] | ||||||
|  |             }, | ||||||
|             { text: 'Sandbox', link: '/sandbox.md' }, |             { text: 'Sandbox', link: '/sandbox.md' }, | ||||||
|  |             { text: 'Donate', link: '/donate.md' }, | ||||||
|             { text: 'Github', link: 'https://github.com/noties/Markwon' } |             { text: 'Github', link: 'https://github.com/noties/Markwon' } | ||||||
|         ], |         ], | ||||||
|         sidebar: { |         sidebar: { | ||||||
|             '/docs/v2/': [ |             '/docs/v2/': [ | ||||||
|                 'install.md', |                 '', | ||||||
|                 'getting-started.md', |                 'getting-started.md', | ||||||
|                 'configure.md', |                 'configure.md', | ||||||
|                 'theme.md', |                 'theme.md', | ||||||
| @ -34,7 +43,13 @@ module.exports = { | |||||||
|                     title: 'Core', |                     title: 'Core', | ||||||
|                     children: [ |                     children: [ | ||||||
|                         '/docs/core/getting-started.md', |                         '/docs/core/getting-started.md', | ||||||
|                         '/docs/core/theme.md' |                         '/docs/core/plugins.md', | ||||||
|  |                         '/docs/core/theme.md', | ||||||
|  |                         '/docs/core/images.md', | ||||||
|  |                         '/docs/core/configuration.md', | ||||||
|  |                         '/docs/core/visitor.md', | ||||||
|  |                         '/docs/core/spans-factory.md', | ||||||
|  |                         '/docs/core/html-renderer.md' | ||||||
|                     ] |                     ] | ||||||
|                 }, |                 }, | ||||||
|                 '/docs/ext-latex/', |                 '/docs/ext-latex/', | ||||||
| @ -57,8 +72,7 @@ module.exports = { | |||||||
|                 }, |                 }, | ||||||
|                 '/docs/recycler/recycler.md', |                 '/docs/recycler/recycler.md', | ||||||
|                 '/docs/syntax-highlight/syntax-highlight.md', |                 '/docs/syntax-highlight/syntax-highlight.md', | ||||||
|                 '/docs/migration-2-3.md', |                 '/docs/migration-2-3.md' | ||||||
|                 ['/docs/v2/install.md', 'Legacy 2.x.x documentation'] |  | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|         sidebarDepth: 2, |         sidebarDepth: 2, | ||||||
|  | |||||||
| @ -1,226 +0,0 @@ | |||||||
| # Configuration |  | ||||||
| 
 |  | ||||||
| `SpannableConfiguration` is the core component that controls how markdown is parsed and rendered. |  | ||||||
| It can be obtained via factory methods: |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| // creates default implementation |  | ||||||
| final SpannableConfiguration configuration = SpannableConfiguration.create(context); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| // creates configurablable instance via `#builder` method |  | ||||||
| final SpannableConfiguration configuration = SpannableConfiguration.builder(context) |  | ||||||
|         .asyncDrawableLoader(AsyncDrawableLoader.create()) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| :::tip Note |  | ||||||
| If `#builder` factory method is used, you do not need to specify default |  | ||||||
| values as they will be applied automatically |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| :::warning Images |  | ||||||
| If you plan on using images inside your markdown/HTML, you will have to **explicitly** |  | ||||||
| register an implementation of `AsyncDrawable.Loader` via `#asyncDrawableLoader` builder method. |  | ||||||
| `Markwon` comes with ready implementation for that and it can be found in |  | ||||||
| `markwon-image-loader` module. Refer to module [documentation](/docs/image-loader.md) |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| ## Theme |  | ||||||
| 
 |  | ||||||
| `SpannableTheme` controls how markdown is rendered. It has pretty extensive number of |  | ||||||
| options that can be found [here](/docs/theme.md) |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .theme(SpannableTheme) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If `SpannableTheme` is not provided explicitly, `SpannableTheme.create(context)` will be used |  | ||||||
| 
 |  | ||||||
| ## Images |  | ||||||
| 
 |  | ||||||
| ### Async loader |  | ||||||
| 
 |  | ||||||
| `AsyncDrawable.Loader` handles images in your markdown and HTML |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .asyncDrawableLoader(AsyncDrawable.Loader) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If `AsyncDrawable.Loader` is not provided explicitly, default **no-op** implementation will be used. |  | ||||||
| 
 |  | ||||||
| :::tip Implementation |  | ||||||
| There are no restrictions on what implementation to use, but `Markwon` has artifact that can |  | ||||||
| answer the most common needs of displaying SVG, GIF and other image formats. It can be found [here](/docs/image-loader.md) |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| ### Size resolver <Badge text="1.0.1" /> |  | ||||||
| 
 |  | ||||||
| `ImageSizeResolver` controls the size of an image to be displayed. Currently it |  | ||||||
| handles only HTML images (specified via `img` tag). |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .imageSizeResolver(ImageSizeResolver) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default `ImageSizeResolverDef` implementation will be used. |  | ||||||
| It handles 3 dimention units: |  | ||||||
| * `%` (percent) |  | ||||||
| * `em` (relative to text size) |  | ||||||
| * `px` (absolute size, every dimention 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 dimentions is missing. |  | ||||||
| 
 |  | ||||||
| :::warning Height% |  | ||||||
| There is no support for `%` units for `height` dimention. 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-refence from which to _calculate_ image height. |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| ## Syntax highlight |  | ||||||
| 
 |  | ||||||
| `SyntaxHighlight` controls the syntax highlight for code blocks (in markdown). |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .syntaxHighlight(SyntaxHighlight) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default **no-op** implementation will be used. |  | ||||||
| 
 |  | ||||||
| :::tip Syntax highlight |  | ||||||
| Although `SyntaxHighlight` interface was included with the very first version |  | ||||||
| of `Markwon` there were no ready-to-use implementations. But starting with <Badge text="1.1.0" />  |  | ||||||
| `Markwon` provides one. It can be found in `markwon-syntax-highlight` artifact. Refer |  | ||||||
| to module [documentation](/docs/syntax-highlight.md) |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| ## Link resolver |  | ||||||
| 
 |  | ||||||
| `LinkSpan.Resolver` is triggered when a link is clicked in markdown/HTML. |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .linkResolver(LinkSpan.Resolver) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default `LinkResolverDef` implementation will be used. |  | ||||||
| Underneath it constructs an `Intent` and _tries_ to start an Activity associated with it. |  | ||||||
| It no Activity is found, it will silently fail (no runtime exceptions) |  | ||||||
| 
 |  | ||||||
| ## URL processor |  | ||||||
| 
 |  | ||||||
| `UrlProcessor` is used to process found URLs in markdown/HTML. |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .urlProcessor(UrlProcessor) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default **no-op** implementation will be used. |  | ||||||
| 
 |  | ||||||
| `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 |  | ||||||
| 
 |  | ||||||
| ## Factory <Badge text="1.1.0" /> |  | ||||||
| 
 |  | ||||||
| `SpannableFactory` is used to control _what_ span implementations to be used |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .factory(SpannableFactory) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default `SpannableFactoryDef` implementation will be used. It is documented |  | ||||||
| in [this section](/docs/factory.md) |  | ||||||
| 
 |  | ||||||
| ## Soft line break <Badge text="1.1.1" /> |  | ||||||
| 
 |  | ||||||
| `softBreakAddsNewLine` option controls how _soft breaks_ are treated in the final result. |  | ||||||
| If `true` -> soft break will add a new line, else it will add a ` ` (space) char. |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .softBreakAddsNewLine(boolean) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default `false` value will be used. |  | ||||||
| 
 |  | ||||||
| <Link name="commonmark-spec#soft-break" displayName="Commonmark specification" /> |  | ||||||
| 
 |  | ||||||
| ## HTML <Badge text="2.0.0" /> |  | ||||||
| 
 |  | ||||||
| ### Parser |  | ||||||
| 
 |  | ||||||
| `MarkwonHtmlParser` is used to parse HTML content |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .htmlParser(MarkwonHtmlParser) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| if not provided explicitly, default `MarkwonHtmlParserImpl` will be used |  | ||||||
| **if** it can be found in classpath, otherwise default **no-op** implementation |  | ||||||
| wiil be used. Refer to [HTML](/docs/html.md#parser) document for more information about this behavior. |  | ||||||
| 
 |  | ||||||
| ### Renderer |  | ||||||
| 
 |  | ||||||
| `MarkwonHtmlRenderer` controls how parsed HTML content will be rendered. |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .htmlRenderer(MarkwonHtmlRenderer) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default `MarkwonHtmlRenderer` implementation will be used. |  | ||||||
| It is documented [here](/docs/html.md#renderer) |  | ||||||
| 
 |  | ||||||
| ### HTML allow non-closed tags |  | ||||||
| 
 |  | ||||||
| `htmlAllowNonClosedTags` option is used to control whether or not to |  | ||||||
| render non-closed HTML tags |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .htmlAllowNonClosedTags(boolean) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If not provided explicitly, default value `false` will be used (non-closed tags **won't** be rendered). |  | ||||||
							
								
								
									
										1
									
								
								docs/docs/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/docs/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | # Configuration | ||||||
| @ -81,39 +81,28 @@ rawInput = plugins.reduce(rawInput, (input, plugin) -> plugin.processMarkdown(in | |||||||
| // 1. after input is processed it's being parsed to a Node | // 1. after input is processed it's being parsed to a Node | ||||||
| node = parser.parse(rawInput); | node = parser.parse(rawInput); | ||||||
| 
 | 
 | ||||||
| // 2. each plugin will configure RenderProps | // 2. each plugin will be able to inspect or manipulate resulting Node | ||||||
| plugins.forEach(plugin -> plugin.configureRenderProps(renderProps)); |  | ||||||
| 
 |  | ||||||
| // 3. each plugin will be able to inspect or manipulate resulting Node |  | ||||||
| //  before rendering | //  before rendering | ||||||
| plugins.forEach(plugin -> plugin.beforeRender(node)); | plugins.forEach(plugin -> plugin.beforeRender(node)); | ||||||
| 
 | 
 | ||||||
| // 4. node is being visited by a visitor | // 3. node is being visited by a visitor | ||||||
| node.accept(visitor); | node.accept(visitor); | ||||||
| 
 | 
 | ||||||
| // 5. each plugin will be called after node is being visited (aka rendered) | // 4. each plugin will be called after node is being visited (aka rendered) | ||||||
| plugins.forEach(plugin -> plugin.afterRender(node, visitor)); | plugins.forEach(plugin -> plugin.afterRender(node, visitor)); | ||||||
| 
 | 
 | ||||||
| // 6. styled markdown ready at this point | // 5. styled markdown ready at this point | ||||||
| final Spanned markdown = visitor.markdown(); | final Spanned markdown = visitor.markdown(); | ||||||
| 
 | 
 | ||||||
| // 7. each plugin will be called before styled markdown is applied to a TextView | // 6. each plugin will be called before styled markdown is applied to a TextView | ||||||
| plugins.forEach(plugin -> plugin.beforeSetText(textView, markdown)); | plugins.forEach(plugin -> plugin.beforeSetText(textView, markdown)); | ||||||
| 
 | 
 | ||||||
| // 8. markdown is applied to a TextView | // 7. markdown is applied to a TextView | ||||||
| textView.setText(markdown); | textView.setText(markdown); | ||||||
| 
 | 
 | ||||||
| // 9. each plugin will be called after markdown is applied to a TextView | // 8. each plugin will be called after markdown is applied to a TextView | ||||||
| plugins.forEach(plugin -> plugin.afterSetText(textView)); | plugins.forEach(plugin -> plugin.afterSetText(textView)); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| As you can see a `plugin` is what lifts the most weight. We will cover | As you can see a `plugin` is what lifts the most weight. We will cover | ||||||
| plugins next. | plugins next. | ||||||
| 
 |  | ||||||
| :::tip Note |  | ||||||
| If you are having trouble with `LinkMovementMethod` you can use |  | ||||||
| `Markwon.setText(textView, markdown, movementMethod)` method <Badge text="1.0.6" /> to specify _no_ movement |  | ||||||
| method (aka `null`) or own implementation. As an alternative to the system `LinkMovementMethod` |  | ||||||
| you can use [Better-Link-Movement-Method](https://github.com/saket/Better-Link-Movement-Method). |  | ||||||
| Please note that `Markwon.setText` method expects _parsed_ markdown as the second argument. |  | ||||||
| ::: |  | ||||||
							
								
								
									
										1
									
								
								docs/docs/core/html-renderer.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/docs/core/html-renderer.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | # HTML Renderer | ||||||
							
								
								
									
										1
									
								
								docs/docs/core/images.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/docs/core/images.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | # Images | ||||||
							
								
								
									
										424
									
								
								docs/docs/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								docs/docs/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,424 @@ | |||||||
|  | # Plugins <Badge text="3.0.0" /> | ||||||
|  | 
 | ||||||
|  | Since <Badge text="3.0.0" /> `MarkwonPlugin` takes the key role in | ||||||
|  | processing and rendering markdown. Even **core** functionaly is abstracted | ||||||
|  | into a `CorePlugin`. So it's still possible to use `Markwon` with a completely | ||||||
|  | own set of plugins. | ||||||
|  | 
 | ||||||
|  | To register a plugin `Markwon.Builder` must be used: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | Markwon.builder(context) | ||||||
|  |     .usePlugin(CorePlugin.create()) | ||||||
|  |     .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | All the process of transforming _raw_ markdown into a styled text (Spanned) | ||||||
|  | will go through plugins. A plugin can: | ||||||
|  | 
 | ||||||
|  | * [configure commonmark-java `Parser`](#parser) | ||||||
|  | * [configure `MarkwonTheme`](#markwontheme) | ||||||
|  | * [configure `AsyncDrawableLoader` (used to display images in markdown)](#images) | ||||||
|  | * [configure `MarkwonConfiguration`](#configuration) | ||||||
|  | * [configure `MarkwonVisitor` (extensible commonmark-java Node visitor)](#visitor) | ||||||
|  | * [configure `MarkwonSpansFactory` (factory to hold spans information for each Node)](#spans-factory) | ||||||
|  | * [configure `MarkwonHtmlRenderer` (utility to properly display HTML in markdown)](#html-renderer) | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | * [declare a dependency on another plugin (will be used as a runtime validator)](#priority) | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | * [process raw input markdown before parsing it](#process-markdown) | ||||||
|  | * [inspect/modify commonmark-java Node after it's been parsed, but before rendering](#inspect-modify-node) | ||||||
|  | * [inspect commonmark-java Node after it's been rendered](#inspect-node-after-render) | ||||||
|  | * [prepare TextView to display markdown _before_ markdown is applied to a TextView](#prepare-textview) | ||||||
|  | * [post-process TextView _after_ markdown was applied](#textview-after-markdown-applied) | ||||||
|  | 
 | ||||||
|  | :::tip | ||||||
|  | if you need to override only few methods of `MarkwonPlugin` (since it is an interface), | ||||||
|  | `AbstractMarkwonPlugin` can be used. | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | ## Parser | ||||||
|  | 
 | ||||||
|  | For example, let's register a new commonmark-java Parser extension: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(CorePlugin.create()) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureParser(@NonNull Parser.Builder builder) { | ||||||
|  |                 // no need to call `super.configureParser(builder)` | ||||||
|  |                 builder.extensions(Collections.singleton(StrikethroughExtension.create())); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | There are no limitations on what to do with commonmark-java Parser. For more info | ||||||
|  | _what_ can be done please refer to <Link name="commonmark-java" displayName="commonmark-java documentation" />. | ||||||
|  | 
 | ||||||
|  | ## MarkwonTheme | ||||||
|  | 
 | ||||||
|  | Starting <Badge text="3.0.0" /> `MarkwonTheme` represents _core_ theme. Aka theme for | ||||||
|  | things core module knows of. For example it doesn't know anything about `strikethrough` | ||||||
|  | or `tables` (as they belong to different modules). | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||||
|  |                 builder | ||||||
|  |                         .codeTextColor(Color.BLACK) | ||||||
|  |                         .codeBackgroundColor(Color.GREEN); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::warning | ||||||
|  | `CorePlugin` has special handling - it will be **implicitly** added | ||||||
|  | if a plugin declares dependency on it. This is why in previous example we haven't | ||||||
|  | added CorePlugin _explicitly_ as `AbstractMarkwonPlugin` declares a dependency on it. | ||||||
|  | If it's not desireable override `AbstractMarkwonPlugin#priority` method to specify own rules. | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | More information about `MarkwonTheme` can be found [here](/docs/core/theme.md). | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Images | ||||||
|  | 
 | ||||||
|  | Since <Badge text="3.0.0" /> core images functionality moved to the `core` module. | ||||||
|  | Now `Markwon` comes bundled with support for regular images (no `SVG` or `GIF`, they | ||||||
|  | defined in standalone modules now). And 3(4) schemes supported by default: | ||||||
|  | * http (+https; using system built-in `HttpURLConnection`) | ||||||
|  | * file (including Android assets) | ||||||
|  | * data (image inline, `data:image/svg+xml;base64,!@#$%^&*(`) | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(ImagesPlugin.create()) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||||
|  |                 // sorry, these are not bundled with the library | ||||||
|  |                 builder | ||||||
|  |                         .addSchemeHandler("ftp", new FtpSchemeHandler("root", "")) | ||||||
|  |                         .addMediaDecoder("text/plain", new AnsiiMediaDecoder()); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::warning | ||||||
|  | Although `ImagesPlugin` is bundled with the `core` artifact, it is **not** used by default | ||||||
|  | and one must **explicitly** add it: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | Markwon.builder(context) | ||||||
|  |         .usePlugin(ImagesPlugin.create(context)); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Without explicit usage of `ImagesPlugin` all image configuration will be ignored (no-op'ed) | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | More information about dealing with images can be found [here](/docs/core/images.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Configuration | ||||||
|  | 
 | ||||||
|  | `MarkwonConfiguration` is a set of common tools that are used by different parts | ||||||
|  | of `Markwon`. It allows configurations of these: | ||||||
|  | 
 | ||||||
|  | * `SyntaxHighlight` (highlighting code blocks) | ||||||
|  | * `LinkResolver` (opens links in markdown) | ||||||
|  | * `UrlProcessor` (process URLs in markdown for both links and images) | ||||||
|  | * `MarkwonHtmlParser` (HTML parser) | ||||||
|  | * `ImageSizeResolver` (resolve image sizes, like `fit-to-canvas`, etc) | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||||
|  |                 // MarkwonHtmlParserImpl is defined in `markwon-html` artifact | ||||||
|  |                 builder.htmlParser(MarkwonHtmlParserImpl.create()); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | More information about `MarkwonConfiguration` can be found [here](/docs/core/configuration.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Visitor | ||||||
|  | 
 | ||||||
|  | `MarkwonVisitor` <Badge text="3.0.0" /> is commonmark-java Visitor that allows | ||||||
|  | configuration of how each Node is visited. There is no longer need to create | ||||||
|  | own subclass of Visitor and override required methods (like in `2.x.x` versions). | ||||||
|  | `MarkwonVisitor` also allows registration of Nodes, that `core` module knows | ||||||
|  | nothing about (instead of relying on `visit(CustomNode)` method)). | ||||||
|  | 
 | ||||||
|  | For example, let's add `strikethrough` Node visitor: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |                 builder | ||||||
|  |                         .on(Strikethrough.class, new MarkwonVisitor.NodeVisitor<Strikethrough>() { | ||||||
|  |                             @Override | ||||||
|  |                             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Strikethrough strikethrough) { | ||||||
|  |                                 final int length = visitor.length(); | ||||||
|  |                                 visitor.visitChildren(strikethrough); | ||||||
|  |                                 visitor.setSpansForNodeOptional(strikethrough, length); | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::tip | ||||||
|  | `MarkwonVisitor` also allows _overriding_ already registered nodes. For example, | ||||||
|  | we can disable `Heading` Node rendering: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | builder.on(Heading.class, null); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Please note that `Priority` plays nicely here to ensure that your | ||||||
|  | custom Node override/disable happens _after_ some plugin defines it. | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | More information about `MarkwonVisitor` can be found [here](/docs/core/visitor.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Spans Factory | ||||||
|  | 
 | ||||||
|  | `MarkwonSpansFactory` <Badge text="3.0.0" /> is an abstract factory (factory that produces other factories) | ||||||
|  | for spans that `Markwon` uses. It controls what spans to use for certain Nodes. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||||
|  |                 // override emphasis factory to make all emphasis nodes underlined | ||||||
|  |                 builder.setFactory(Emphasis.class, new SpanFactory() { | ||||||
|  |                     @Override | ||||||
|  |                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||||
|  |                         return new UnderlineSpan(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::tip | ||||||
|  | `SpanFactory` allows to return an _array_ of spans to apply multiple spans | ||||||
|  | for a Node: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | @Override | ||||||
|  | public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||||
|  |     // make underlined and set text color to red | ||||||
|  |     return new Object[]{ | ||||||
|  |             new UnderlineSpan(), | ||||||
|  |             new ForegroundColorSpan(Color.RED) | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | More information about spans factory can be found [here](/docs/core/spans-factory.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## HTML Renderer | ||||||
|  | 
 | ||||||
|  | `MarkwonHtmlRenderer` controls how HTML is rendered in markdown. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(HtmlPlugin.create()) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) { | ||||||
|  |                 // <center> tag handling (deprecated but valid in our case) | ||||||
|  |                 // can be any tag name, there is no connection with _real_ HTML tags, | ||||||
|  |                 // <just-try-to-not-go-crazy-and-remember-about-portability> | ||||||
|  |                 builder.addHandler("center", new SimpleTagHandler() { | ||||||
|  |                     @Override | ||||||
|  |                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps renderProps, @NonNull HtmlTag tag) { | ||||||
|  |                         return new AlignmentSpan() { | ||||||
|  |                             @Override | ||||||
|  |                             public Layout.Alignment getAlignment() { | ||||||
|  |                                 return Layout.Alignment.ALIGN_CENTER; | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::danger | ||||||
|  | Although `MarkwonHtmlRenderer` is bundled with `core` artifact, actual | ||||||
|  | HTML parser is placed in a standalone artifact and must be added to your | ||||||
|  | project **explicitly** and then registered via `Markwon.Builder#usePlugin(HtmlPlugin.create())`. | ||||||
|  | If not done so, no HTML will be parsed nor rendered. | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | More information about HTML rendering can be found [here](/docs/core/html-renderer.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Priority | ||||||
|  | 
 | ||||||
|  | `Priority` is an abstraction to _state_ dependency connection between plugins. It is | ||||||
|  | also used as a runtime graph validator. If a plugin defines a dependency on other, but | ||||||
|  | _other_ is not in resulting `Markwon` instance, then a runtime exception will be thrown. | ||||||
|  | `Priority` is also defines the order in which plugins will be placed. So, if a plugin `A` | ||||||
|  | states a plugin `B` as a dependency, then plugin `A` will come **after** plugin `B`. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public Priority priority() { | ||||||
|  |                 return Priority.after(CorePlugin.class); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::warning | ||||||
|  | Please note that `AbstractMarkwonPlugin` _implicitly_ defines `CorePlugin` | ||||||
|  | as a dependency (`return Priority.after(CorePlugin.class);`). This will | ||||||
|  | also add `CorePlugin` to a `Markwon` instance, because it will be added | ||||||
|  | _implicitly_ if a plugin defines it as a dependency.  | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | Use one of the factory methods to create a `Priority` instance: | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | // none | ||||||
|  | Priority.none(); | ||||||
|  | 
 | ||||||
|  | // single dependency | ||||||
|  | Priority.after(CorePlugin.class); | ||||||
|  | 
 | ||||||
|  | // 2 dependencies | ||||||
|  | Priority.after(CorePlugin.class, ImagesPlugin.class); | ||||||
|  | 
 | ||||||
|  | // for a number >2, use #builder | ||||||
|  | Priority.builder() | ||||||
|  |         .after(CorePlugin.class) | ||||||
|  |         .after(ImagesPlugin.class) | ||||||
|  |         .after(StrikethroughPlugin.class) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Process markdown | ||||||
|  | 
 | ||||||
|  | A plugin can be used to _pre-process_ input markdown (this will be called before _parsing_): | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @NonNull | ||||||
|  |             @Override | ||||||
|  |             public String processMarkdown(@NonNull String markdown) { | ||||||
|  |                 return markdown.replaceAll("foo", "bar"); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Inspect/modify Node | ||||||
|  | 
 | ||||||
|  | A plugin can inspect/modify commonmark-java Node _before_ it's being rendered. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void beforeRender(@NonNull Node node) { | ||||||
|  | 
 | ||||||
|  |                 // for example inspect it with custom visitor | ||||||
|  |                 node.accept(new MyVisitor()); | ||||||
|  | 
 | ||||||
|  |                 // or modify (you know what you are doing, right?) | ||||||
|  |                 node.appendChild(new Text("Appended")); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Inspect Node after render | ||||||
|  | 
 | ||||||
|  | A plugin can inspect commonmark-java Node after it's been rendered. | ||||||
|  | Modifying Node at this point makes not much sense (it's already been | ||||||
|  | rendered and all modifications won't change anything). But this method can be used, | ||||||
|  | for example, to clean-up some internal state (after rendering). Generally | ||||||
|  | speaking, a plugin must be stateless, but if it cannot, then this method is | ||||||
|  | the best place to clean-up. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) { | ||||||
|  |                 cleanUp(); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Prepare TextView | ||||||
|  | 
 | ||||||
|  | A plugin can _prepare_ a TextView before markdown is applied. For example `images` | ||||||
|  | unschedules all previously scheduled `AsyncDrawableSpans` (if any) here. This way | ||||||
|  | when new markdown (and set of Spannables) arrives, previous set won't be kept in | ||||||
|  | memory and could be garbage-collected. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||||
|  |                 // clean-up previous | ||||||
|  |                 AsyncDrawableScheduler.unschedule(textView); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## TextView after markdown applied | ||||||
|  | 
 | ||||||
|  | A plugin will receive a callback _after_ markdown is applied to a TextView. | ||||||
|  | For example `images` uses this callback to schedule new set of Spannables. | ||||||
|  | 
 | ||||||
|  | ```java | ||||||
|  | final Markwon markwon = Markwon.builder(context) | ||||||
|  |         .usePlugin(new AbstractMarkwonPlugin() { | ||||||
|  |             @Override | ||||||
|  |             public void afterSetText(@NonNull TextView textView) { | ||||||
|  |                 AsyncDrawableScheduler.schedule(textView); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         .build(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | :::tip | ||||||
|  | Please note that unlike `#beforeSetText`, `#afterSetText` won't receive | ||||||
|  | `Spanned` markdown. This happens because at this point spans must be | ||||||
|  | queried directly from a TextView. | ||||||
|  | ::: | ||||||
							
								
								
									
										1
									
								
								docs/docs/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/docs/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | # Spans Factory | ||||||
							
								
								
									
										1
									
								
								docs/docs/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/docs/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | # Visitor | ||||||
| @ -1,61 +0,0 @@ | |||||||
| # Factory <Badge text="1.1.0" /> |  | ||||||
| 
 |  | ||||||
| `SpannableFactory` is used to create Span implementations. |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| SpannableConfiguration.builder(context) |  | ||||||
|         .factory(SpannableFactory) |  | ||||||
|         .build(); |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| `Markwon` provides default `SpannableFactoryDef` implementation that is |  | ||||||
| used by default. |  | ||||||
| 
 |  | ||||||
| Spans: |  | ||||||
| * `strongEmphasis` |  | ||||||
| * `emphasis` |  | ||||||
| * `blockQuote` |  | ||||||
| * `code` |  | ||||||
| * `orderedListItem` |  | ||||||
| * `bulletListItem` |  | ||||||
| * `thematicBreak` |  | ||||||
| * `heading` |  | ||||||
| * `strikethrough` |  | ||||||
| * `taskListItem` |  | ||||||
| * `tableRow` |  | ||||||
| * `paragraph` <Badge text="1.1.1" /> |  | ||||||
| * `image` |  | ||||||
| * `link` |  | ||||||
| * `superScript` (HTML content only) |  | ||||||
| * `subScript` (HTML content only) |  | ||||||
| * `underline` (HTML content only) |  | ||||||
| 
 |  | ||||||
| :::tip |  | ||||||
| `SpannableFactory` can be used to ignore some kinds of text markup. If, for example, |  | ||||||
| you do not wish to apply _emphasis_ styling to your final result, just return `null` |  | ||||||
| from `emphasis` factory method: |  | ||||||
| ```java |  | ||||||
| @Nullable |  | ||||||
| @Override |  | ||||||
| public Object emphasis() { |  | ||||||
|     return null; |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| ::: |  | ||||||
| 
 |  | ||||||
| :::tip |  | ||||||
| All factory methods in `SpannableFactory` return an `Object`, but you can actually |  | ||||||
| return an **array of Objects** if you wish to apply multiple Spans to a single styling node. |  | ||||||
| For example, let's make all _emphasis_ also <span :style="{color: '#F00'}">red</span>: |  | ||||||
| 
 |  | ||||||
| ```java |  | ||||||
| @Nullable |  | ||||||
| @Override |  | ||||||
| public Object emphasis() { |  | ||||||
|     return new Object[] { |  | ||||||
|             super.emphasis(), |  | ||||||
|             new ForegroundColorSpan(Color.RED) |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| ::: |  | ||||||
| @ -1 +1,8 @@ | |||||||
| # Migration 2.x.x -> 3.x.x | # Migration 2.x.x -> 3.x.x | ||||||
|  | 
 | ||||||
|  | * strikethrough moved to standalone module | ||||||
|  | * tables moved to standalone module | ||||||
|  | * core functionality of `AsyncDrawableLoader` moved to `core` module | ||||||
|  | * * Handling of GIF and SVG media moved to standalone modules (`gif` and `svg` respectively) | ||||||
|  | * * OkHttpClient to download images moved to standalone module | ||||||
|  | * HTML no longer _implicitly_ added to core functionality, it must be specified __explicitly__ (as an artifact) | ||||||
							
								
								
									
										7
									
								
								docs/donate.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								docs/donate.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | # Donations | ||||||
|  | 
 | ||||||
|  | If you find this library useful consider making a donation  | ||||||
|  | to your local [environmental](https://en.wikipedia.org/wiki/List_of_environmental_organizations)  | ||||||
|  | or [human rights](https://en.wikipedia.org/wiki/List_of_human_rights_organisations) organization. | ||||||
|  | 
 | ||||||
|  | Thank you | ||||||
| @ -57,11 +57,6 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |  | ||||||
|     public void configureRenderProps(@NonNull RenderProps renderProps) { |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public Priority priority() { |     public Priority priority() { | ||||||
|  | |||||||
| @ -46,14 +46,7 @@ class MarkwonImpl extends Markwon { | |||||||
|     @Override |     @Override | ||||||
|     public Spanned render(@NonNull Node node) { |     public Spanned render(@NonNull Node node) { | ||||||
| 
 | 
 | ||||||
|         final RenderProps renderProps = visitor.renderProps(); |  | ||||||
| 
 |  | ||||||
|         for (MarkwonPlugin plugin : plugins) { |         for (MarkwonPlugin plugin : plugins) { | ||||||
| 
 |  | ||||||
|             // let plugins apply render properties before rendering (as we will clear |  | ||||||
|             // renderProps after rendering) |  | ||||||
|             plugin.configureRenderProps(renderProps); |  | ||||||
| 
 |  | ||||||
|             plugin.beforeRender(node); |             plugin.beforeRender(node); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -82,19 +82,6 @@ public interface MarkwonPlugin { | |||||||
|      */ |      */ | ||||||
|     void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder); |     void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder); | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * A method to store some arbitrary data in {@link RenderProps}. Although it won\'t make |  | ||||||
|      * much sense to use existing {@link Prop} keys for {@link SpanFactory}, it can be helpful |  | ||||||
|      * to establish a communication channel between multiple plugins in decoupled way (provide |  | ||||||
|      * some initial properties for example or indicate that certain plugin is registered). |  | ||||||
|      * <p> |  | ||||||
|      * This method will be called before <em>each</em> rendering step (after rendering {@link RenderProps} |  | ||||||
|      * will be cleared. This method <strong>won\'t</strong> be called during initialization stage. |  | ||||||
|      * |  | ||||||
|      * @see RenderProps |  | ||||||
|      */ |  | ||||||
|     void configureRenderProps(@NonNull RenderProps renderProps); |  | ||||||
| 
 |  | ||||||
|     @NonNull |     @NonNull | ||||||
|     Priority priority(); |     Priority priority(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package ru.noties.markwon.html; | |||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| 
 | 
 | ||||||
|  | import java.util.Collection; | ||||||
|  | 
 | ||||||
| import ru.noties.markwon.MarkwonVisitor; | import ru.noties.markwon.MarkwonVisitor; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -39,19 +41,11 @@ public abstract class MarkwonHtmlRenderer { | |||||||
|         @NonNull |         @NonNull | ||||||
|         Builder allowNonClosedTags(boolean allowNonClosedTags); |         Builder allowNonClosedTags(boolean allowNonClosedTags); | ||||||
| 
 | 
 | ||||||
|         /** |  | ||||||
|          * Please note that if there is already a {@link TagHandler} registered with specified |  | ||||||
|          * {@code tagName} it will be replaced with newly supplied one. |  | ||||||
|          * |  | ||||||
|          * @param tagHandler {@link TagHandler} |  | ||||||
|          * @param tagName    name of a tag |  | ||||||
|          * @return self |  | ||||||
|          */ |  | ||||||
|         @NonNull |         @NonNull | ||||||
|         Builder addHandler(@NonNull TagHandler tagHandler, @NonNull String tagName); |         Builder addHandler(@NonNull String tagName, @NonNull TagHandler tagHandler); | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames); |         Builder addHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler); | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         Builder removeHandler(@NonNull String tagName); |         Builder removeHandler(@NonNull String tagName); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ package ru.noties.markwon.html; | |||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| 
 | 
 | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @ -99,14 +100,14 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer { | |||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         @Override |         @Override | ||||||
|         public Builder addHandler(@NonNull TagHandler tagHandler, @NonNull String tagName) { |         public Builder addHandler(@NonNull String tagName, @NonNull TagHandler tagHandler) { | ||||||
|             tagHandlers.put(tagName, tagHandler); |             tagHandlers.put(tagName, tagHandler); | ||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         @Override |         @Override | ||||||
|         public Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames) { |         public Builder addHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler) { | ||||||
|             for (String tagName : tagNames) { |             for (String tagName : tagNames) { | ||||||
|                 if (tagName != null) { |                 if (tagName != null) { | ||||||
|                     tagHandlers.put(tagName, tagHandler); |                     tagHandlers.put(tagName, tagHandler); | ||||||
|  | |||||||
| @ -223,7 +223,6 @@ public class MarkwonBuilderImplTest { | |||||||
|         verify(plugin, atLeast(1)).priority(); |         verify(plugin, atLeast(1)).priority(); | ||||||
| 
 | 
 | ||||||
|         // note, no render props -> they must be configured on render stage |         // note, no render props -> they must be configured on render stage | ||||||
|         verify(plugin, times(0)).configureRenderProps(any(RenderProps.class)); |  | ||||||
|         verify(plugin, times(0)).processMarkdown(anyString()); |         verify(plugin, times(0)).processMarkdown(anyString()); | ||||||
|         verify(plugin, times(0)).beforeRender(any(Node.class)); |         verify(plugin, times(0)).beforeRender(any(Node.class)); | ||||||
|         verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class)); |         verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class)); | ||||||
|  | |||||||
| @ -107,8 +107,6 @@ public class MarkwonImplTest { | |||||||
|                 // mark this flag (we must ensure that this method body is executed) |                 // mark this flag (we must ensure that this method body is executed) | ||||||
|                 flag.set(true); |                 flag.set(true); | ||||||
| 
 | 
 | ||||||
|                 //noinspection ConstantConditions |  | ||||||
|                 verify(plugin, times(1)).configureRenderProps(null); |  | ||||||
|                 verify(plugin, times(1)).beforeRender(eq(node)); |                 verify(plugin, times(1)).beforeRender(eq(node)); | ||||||
|                 verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class)); |                 verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class)); | ||||||
| 
 | 
 | ||||||
| @ -175,8 +173,6 @@ public class MarkwonImplTest { | |||||||
| 
 | 
 | ||||||
|                 flag.set(true); |                 flag.set(true); | ||||||
| 
 | 
 | ||||||
|                 verify(visitor, times(1)).renderProps(); |  | ||||||
|                 verify(plugin, times(1)).configureRenderProps(eq(renderProps)); |  | ||||||
|                 verify(renderProps, times(0)).clearAll(); |                 verify(renderProps, times(0)).clearAll(); | ||||||
| 
 | 
 | ||||||
|                 return null; |                 return null; | ||||||
|  | |||||||
| @ -186,6 +186,7 @@ public class CorePluginTest { | |||||||
|             add("configureVisitor"); |             add("configureVisitor"); | ||||||
|             add("configureSpansFactory"); |             add("configureSpansFactory"); | ||||||
|             add("beforeSetText"); |             add("beforeSetText"); | ||||||
|  |             add("afterSetText"); | ||||||
|             add("priority"); |             add("priority"); | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,6 +22,8 @@ import ru.noties.markwon.html.tag.SubScriptHandler; | |||||||
| import ru.noties.markwon.html.tag.SuperScriptHandler; | import ru.noties.markwon.html.tag.SuperScriptHandler; | ||||||
| import ru.noties.markwon.html.tag.UnderlineHandler; | import ru.noties.markwon.html.tag.UnderlineHandler; | ||||||
| 
 | 
 | ||||||
|  | import static java.util.Arrays.asList; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @since 3.0.0 |  * @since 3.0.0 | ||||||
|  */ |  */ | ||||||
| @ -41,18 +43,41 @@ public class HtmlPlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) { |     public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) { | ||||||
|  | 
 | ||||||
|         builder |         builder | ||||||
|                 .addHandler(new EmphasisHandler(), "i", "em", "cite", "dfn") |                 .addHandler( | ||||||
|                 .addHandler(new StrongEmphasisHandler(), "b", "strong") |                         "img", | ||||||
|                 .addHandler(new SuperScriptHandler(), "sup") |                         ImageHandler.create()) | ||||||
|                 .addHandler(new SubScriptHandler(), "sub") |                 .addHandler( | ||||||
|                 .addHandler(new UnderlineHandler(), "u", "ins") |                         "a", | ||||||
|                 .addHandler(new StrikeHandler(), "s", "del") |                         new LinkHandler()) | ||||||
|                 .addHandler(new LinkHandler(), "a") |                 .addHandler( | ||||||
|                 .addHandler(new ListHandler(), "ul", "ol") |                         "blockquote", | ||||||
|                 .addHandler(ImageHandler.create(), "img") |                         new BlockquoteHandler()) | ||||||
|                 .addHandler(new BlockquoteHandler(), "blockquote") |                 .addHandler( | ||||||
|                 .addHandler(new HeadingHandler(), "h1", "h2", "h3", "h4", "h5", "h6"); |                         "sub", | ||||||
|  |                         new SubScriptHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         "sup", | ||||||
|  |                         new SuperScriptHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("b", "strong"), | ||||||
|  |                         new StrongEmphasisHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("s", "del"), | ||||||
|  |                         new StrikeHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("u", "ins"), | ||||||
|  |                         new UnderlineHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("ul", "ol"), | ||||||
|  |                         new ListHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("i", "em", "cite", "dfn"), | ||||||
|  |                         new EmphasisHandler()) | ||||||
|  |                 .addHandler( | ||||||
|  |                         asList("h1", "h2", "h3", "h4", "h5", "h6"), | ||||||
|  |                         new HeadingHandler()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov