Working on core module documentation

This commit is contained in:
Dimitry Ivanov 2019-01-14 18:42:30 +03:00
parent 02e7539881
commit d91f367e0a
23 changed files with 515 additions and 365 deletions

View File

@ -17,7 +17,7 @@
</p>
<p>
<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.
</em>
</p>

View File

@ -1,24 +1,33 @@
module.exports = {
base: '/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: [
['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', href: '/favicon.ico?v=1' }],
['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png?v=1' }],
['link', { rel: 'manifest', href: '/manifest.json?v=1' }],
['meta', { name: 'keywords', content: 'android,markdown,library,spannable,markwon,commonmark' }]
],
themeConfig: {
nav: [
{ text: 'Install', link: '/docs/install.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: 'Donate', link: '/donate.md' },
{ text: 'Github', link: 'https://github.com/noties/Markwon' }
],
sidebar: {
'/docs/v2/': [
'install.md',
'',
'getting-started.md',
'configure.md',
'theme.md',
@ -34,7 +43,13 @@ module.exports = {
title: 'Core',
children: [
'/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/',
@ -57,8 +72,7 @@ module.exports = {
},
'/docs/recycler/recycler.md',
'/docs/syntax-highlight/syntax-highlight.md',
'/docs/migration-2-3.md',
['/docs/v2/install.md', 'Legacy 2.x.x documentation']
'/docs/migration-2-3.md'
]
},
sidebarDepth: 2,

View File

@ -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: `![img](./art/image.JPG)` 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: `![img](./art/image.JPG)` 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).

View File

@ -0,0 +1 @@
# Configuration

View File

@ -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
node = parser.parse(rawInput);
// 2. each plugin will configure RenderProps
plugins.forEach(plugin -> plugin.configureRenderProps(renderProps));
// 3. each plugin will be able to inspect or manipulate resulting Node
// 2. each plugin will be able to inspect or manipulate resulting Node
// before rendering
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);
// 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));
// 6. styled markdown ready at this point
// 5. styled markdown ready at this point
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));
// 8. markdown is applied to a TextView
// 7. markdown is applied to a TextView
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));
```
As you can see a `plugin` is what lifts the most weight. We will cover
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.
:::

View File

@ -0,0 +1 @@
# HTML Renderer

1
docs/docs/core/images.md Normal file
View File

@ -0,0 +1 @@
# Images

424
docs/docs/core/plugins.md Normal file
View 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.
:::

View File

@ -0,0 +1 @@
# Spans Factory

View File

@ -0,0 +1 @@
# Visitor

View File

@ -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)
};
}
```
:::

View File

@ -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
View 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

View File

@ -57,11 +57,6 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin {
}
@Override
public void configureRenderProps(@NonNull RenderProps renderProps) {
}
@NonNull
@Override
public Priority priority() {

View File

@ -46,14 +46,7 @@ class MarkwonImpl extends Markwon {
@Override
public Spanned render(@NonNull Node node) {
final RenderProps renderProps = visitor.renderProps();
for (MarkwonPlugin plugin : plugins) {
// let plugins apply render properties before rendering (as we will clear
// renderProps after rendering)
plugin.configureRenderProps(renderProps);
plugin.beforeRender(node);
}

View File

@ -82,19 +82,6 @@ public interface MarkwonPlugin {
*/
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
Priority priority();

View File

@ -3,6 +3,8 @@ package ru.noties.markwon.html;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Collection;
import ru.noties.markwon.MarkwonVisitor;
/**
@ -39,19 +41,11 @@ public abstract class MarkwonHtmlRenderer {
@NonNull
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
Builder addHandler(@NonNull TagHandler tagHandler, @NonNull String tagName);
Builder addHandler(@NonNull String tagName, @NonNull TagHandler tagHandler);
@NonNull
Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames);
Builder addHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler);
@NonNull
Builder removeHandler(@NonNull String tagName);

View File

@ -3,6 +3,7 @@ package ru.noties.markwon.html;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -99,14 +100,14 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
@NonNull
@Override
public Builder addHandler(@NonNull TagHandler tagHandler, @NonNull String tagName) {
public Builder addHandler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
tagHandlers.put(tagName, tagHandler);
return this;
}
@NonNull
@Override
public Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames) {
public Builder addHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler) {
for (String tagName : tagNames) {
if (tagName != null) {
tagHandlers.put(tagName, tagHandler);

View File

@ -223,7 +223,6 @@ public class MarkwonBuilderImplTest {
verify(plugin, atLeast(1)).priority();
// 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)).beforeRender(any(Node.class));
verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class));

View File

@ -107,8 +107,6 @@ public class MarkwonImplTest {
// mark this flag (we must ensure that this method body is executed)
flag.set(true);
//noinspection ConstantConditions
verify(plugin, times(1)).configureRenderProps(null);
verify(plugin, times(1)).beforeRender(eq(node));
verify(plugin, times(0)).afterRender(any(Node.class), any(MarkwonVisitor.class));
@ -175,8 +173,6 @@ public class MarkwonImplTest {
flag.set(true);
verify(visitor, times(1)).renderProps();
verify(plugin, times(1)).configureRenderProps(eq(renderProps));
verify(renderProps, times(0)).clearAll();
return null;

View File

@ -186,6 +186,7 @@ public class CorePluginTest {
add("configureVisitor");
add("configureSpansFactory");
add("beforeSetText");
add("afterSetText");
add("priority");
}};

View File

@ -22,6 +22,8 @@ import ru.noties.markwon.html.tag.SubScriptHandler;
import ru.noties.markwon.html.tag.SuperScriptHandler;
import ru.noties.markwon.html.tag.UnderlineHandler;
import static java.util.Arrays.asList;
/**
* @since 3.0.0
*/
@ -41,18 +43,41 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
@Override
public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) {
builder
.addHandler(new EmphasisHandler(), "i", "em", "cite", "dfn")
.addHandler(new StrongEmphasisHandler(), "b", "strong")
.addHandler(new SuperScriptHandler(), "sup")
.addHandler(new SubScriptHandler(), "sub")
.addHandler(new UnderlineHandler(), "u", "ins")
.addHandler(new StrikeHandler(), "s", "del")
.addHandler(new LinkHandler(), "a")
.addHandler(new ListHandler(), "ul", "ol")
.addHandler(ImageHandler.create(), "img")
.addHandler(new BlockquoteHandler(), "blockquote")
.addHandler(new HeadingHandler(), "h1", "h2", "h3", "h4", "h5", "h6");
.addHandler(
"img",
ImageHandler.create())
.addHandler(
"a",
new LinkHandler())
.addHandler(
"blockquote",
new BlockquoteHandler())
.addHandler(
"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