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