Working with documentation

This commit is contained in:
Dimitry Ivanov 2019-01-20 16:04:28 +03:00
parent d91f367e0a
commit 18fd56a97c
20 changed files with 217 additions and 121 deletions

View File

@ -49,17 +49,20 @@ module.exports = {
'/docs/core/configuration.md',
'/docs/core/visitor.md',
'/docs/core/spans-factory.md',
'/docs/core/html-renderer.md'
'/docs/core/html-renderer.md',
'/docs/core/core-plugin.md',
'/docs/core/movement-method-plugin.md'
]
},
'/docs/ext-latex/',
'/docs/ext-strikethrough/strikethrough.md',
'/docs/ext-tables/tables.md',
'/docs/ext-tasklist/tasklist.md',
'/docs/ext-strikethrough/',
'/docs/ext-tables/',
'/docs/ext-tasklist/',
{
title: 'HTML',
children: [
'/docs/html/html.md'
'/docs/html/',
'/docs/html/custom-tag-handler.md'
]
},
{
@ -70,8 +73,8 @@ module.exports = {
'/docs/image/svg.md'
]
},
'/docs/recycler/recycler.md',
'/docs/syntax-highlight/syntax-highlight.md',
'/docs/recycler/',
'/docs/syntax-highlight/',
'/docs/migration-2-3.md'
]
},

View File

@ -25,18 +25,19 @@ listed in <Link name="commonmark-spec" /> are supported (including support for *
* Emphasis (`*`, `_`)
* Strong emphasis (`**`, `__`)
* Strike-through (`~~`)
* Headers (`#{1,6}`)
* Links (`[]()` && `[][]`)
* [Images](/docs/image-loader.md)
* [Images](/docs/core/images.md)
* Thematic break (`---`, `***`, `___`)
* Quotes & nested quotes (`>{1,}`)
* Ordered & non-ordered lists & nested ones
* Inline code
* Code blocks
* Tables (*with limitations*)
* [Syntax highlight](/docs/syntax-highlight.md)
* [HTML](/docs/html.md)
* [Strike-through](/docs/ext-strikethrough/) (`~~`)
* [Tables](/docs/ext-tables/) (*with limitations*)
* [Syntax highlight](/docs/syntax-highlight/)
* [LaTeX](/docs/ext-latex/) formulas
* [HTML](/docs/html/)
* Emphasis (`<i>`, `<em>`, `<cite>`, `<dfn>`)
* Strong emphasis (`<b>`, `<strong>`)
* SuperScript (`<sup>`)
@ -49,8 +50,8 @@ listed in <Link name="commonmark-spec" /> are supported (including support for *
* Blockquote (`blockquote`)
* Heading (`h1`, `h2`, `h3`, `h4`, `h5`, `h6`)
* there is support to render any HTML tag, but it will require to create a special `TagHandler`,
more information can be found in [HTML section](/docs/html.md#custom-tag-handler)
* Task lists:
more information can be found in [HTML section](/docs/html/custom-tag-handler.md)
* [Task lists](/docs/ext-tasklist/):
- [ ] Not _done_
- [X] **Done** with `X`
- [x] ~~and~~ **or** small `x`

View File

@ -0,0 +1,103 @@
# Core plugin <Badge text="3.0.0" />
Since <Badge text="3.0.0" /> with introduction of _plugins_, Markwon
**core** functionality was moved to a dedicated plugin.
```java
CorePlugin.create();
```
## Node visitors
`CorePlugin` registers these `commonmark-java` node visitors:
* `Text`
* `StrongEmphasis`
* `Emphasis`
* `BlockQuote`
* `Code`
* `FencedCodeBlock`
* `IndentedCodeBlock`
* `BulletList`
* `OrderedList`
* `ListItem`
* `ThematicBreak`
* `Heading`
* `SoftLineBreak`
* `HardLineBreak`
* `Paragraph`
* `Link`
## Span factories
`CorePlugin` adds these `SpanFactory`s:
* `StrongEmphasis`
* `Emphasis`
* `BlockQuote`
* `Code`
* `FencedCodeBlock`
* `IndentedCodeBlock`
* `ListItem`
* `Heading`
* `Link`
* `ThematicBreak`
:::tip
By default `CorePlugin` does not register a `Paragraph` `SpanFactory` but
this can be done in your custom plugin:
```java
Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(Paragraph.class, (configuration, props) ->
new ForegroundColorSpan(Color.RED));
}
})
```
:::
## Props
These props are exported by `CorePlugin` and can be found in `CoreProps`:
* `Prop<ListItemType> LIST_ITEM_TYPE` (BULLET | ORDERED)
* `Prop<Integer> BULLET_LIST_ITEM_LEVEL`
* `Prop<Integer> ORDERED_LIST_ITEM_NUMBER`
* `Prop<Integer> HEADING_LEVEL`
* `Prop<String> LINK_DESTINATION`
* `Prop<Boolean> PARAGRAPH_IS_IN_TIGHT_LIST`
:::warning List item type
Before <Badge text="3.0.0" /> `Markwon` had 2 distinct lists (bullet and ordered).
Since <Badge text="3.0.0" /> a single `SpanFactory` is used, which internally checks
for `Prop<ListItemType> LIST_ITEM_TYPE`.
Beware of this if you would like to override only one of the list types. This is
done to correspond to `commonmark-java` implementation.
:::
---
:::tip Soft line break
Since <Badge text="3.0.0" /> Markwon core does not give an option to
insert a new line when there is a soft line break in markdown. Instead a
custom plugin can be used:
```java
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(SoftLineBreak.class, (visitor, softLineBreak) ->
visitor.forceNewLine());
}
})
.build();
```
:::
:::warning
Please note that `CorePlugin` will implicitly set a `LinkMovementMethod` on a TextView
if one is not present. If you wish to customize a MovementMethod that is used, apply
one manually to a TextView (before applying markdown) or use the [MovementMethodPlugin](/docs/core/movement-method-plugin.md)
which accepts a MovementMethod as an argument.
:::

View File

@ -0,0 +1,17 @@
# Movement method plugin
`MovementMethodPlugin` can be used to apply a `MovementMethod` to a TextView
(important if you have links inside your markdown). By default `CorePlugin`
will set a `LinkMovementMethod` on a TextView if one is missing. If you have
specific needs for a `MovementMethod` and `LinkMovementMethod` doesn't answer
your needs use `MovementMethodPlugin`:
```java
Markwon.builder(context)
.usePlugin(MovementMethodPlugin.create(ScrollingMovementMethod.getInstance()))
```
:::tip
If you are having trouble with system `LinkMovementMethod` as an alternative
[BetterLinkMovementMethod](https://github.com/saket/Better-Link-Movement-Method) library can be used.
:::

View File

@ -24,6 +24,19 @@ final Markwon markwon = Markwon.builder(context)
.usePlugin(TablePlugin.create(tableTheme))
```
```java
Markwon.builder(context)
.usePlugin(TablePlugin.create(builder ->
builder
.tableBorderColor(Color.RED)
.tableBorderWidth(0)
.tableCellPadding(0)
.tableHeaderRowBackgroundColor(Color.BLACK)
.tableEvenRowBackgroundColor(Color.GREEN)
.tableOddRowBackgroundColor(Color.YELLOW)
))
```
Please note, that _by default_ tables have limitations. For example, there is no support
for images inside table cells. And table contents won't be copied to clipboard if a TextView
has such functionality. Table will always take full width of a TextView in which it is displayed.

View File

@ -0,0 +1 @@
# HTML custom tag handler

View File

@ -50,18 +50,10 @@ public class CorePlugin extends AbstractMarkwonPlugin {
@NonNull
public static CorePlugin create() {
return create(false);
return new CorePlugin();
}
@NonNull
public static CorePlugin create(boolean softBreakAddsNewLine) {
return new CorePlugin(softBreakAddsNewLine);
}
private final boolean softBreakAddsNewLine;
protected CorePlugin(boolean softBreakAddsNewLine) {
this.softBreakAddsNewLine = softBreakAddsNewLine;
protected CorePlugin() {
}
@Override
@ -78,7 +70,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
listItem(builder);
thematicBreak(builder);
heading(builder);
softLineBreak(builder, softBreakAddsNewLine);
softLineBreak(builder);
hardLineBreak(builder);
paragraph(builder);
link(builder);
@ -341,15 +333,11 @@ public class CorePlugin extends AbstractMarkwonPlugin {
});
}
private static void softLineBreak(@NonNull MarkwonVisitor.Builder builder, final boolean softBreakAddsNewLine) {
private static void softLineBreak(@NonNull MarkwonVisitor.Builder builder) {
builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) {
if (softBreakAddsNewLine) {
visitor.ensureNewLine();
} else {
visitor.builder().append(' ');
}
visitor.builder().append(' ');
}
});
}

View File

@ -126,7 +126,7 @@ public class MarkwonBuilderImplTest {
};
// our subclass
final CorePlugin corePlugin = new CorePlugin(false) {
final CorePlugin corePlugin = new CorePlugin() {
};

View File

@ -2,6 +2,8 @@ package ru.noties.markwon.core;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.method.MovementMethod;
import android.widget.TextView;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.BulletList;
@ -46,6 +48,7 @@ import ru.noties.markwon.SpannableBuilder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@ -223,26 +226,9 @@ public class CorePluginTest {
}
@Test
public void softbreak_adds_new_line_default() {
// default is false
softbreak_adds_new_line(CorePlugin.create(), false);
}
public void softbreak() {
@Test
public void softbreak_adds_new_line_false() {
// a space character will be added
softbreak_adds_new_line(CorePlugin.create(false), false);
}
@Test
public void softbreak_adds_new_line_true() {
// a new line will be added
softbreak_adds_new_line(CorePlugin.create(true), true);
}
private static void softbreak_adds_new_line(
@NonNull CorePlugin plugin,
boolean softBreakAddsNewLine) {
final CorePlugin plugin = CorePlugin.create();
final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class);
when(builder.on(any(Class.class), any(MarkwonVisitor.NodeVisitor.class))).thenReturn(builder);
@ -259,21 +245,44 @@ public class CorePluginTest {
final MarkwonVisitor.NodeVisitor<SoftLineBreak> nodeVisitor = captor.getValue();
final MarkwonVisitor visitor = mock(MarkwonVisitor.class);
if (!softBreakAddsNewLine) {
// we must mock SpannableBuilder and verify that it has a space character appended
final SpannableBuilder spannableBuilder = mock(SpannableBuilder.class);
when(visitor.builder()).thenReturn(spannableBuilder);
nodeVisitor.visit(visitor, mock(SoftLineBreak.class));
// we must mock SpannableBuilder and verify that it has a space character appended
final SpannableBuilder spannableBuilder = mock(SpannableBuilder.class);
when(visitor.builder()).thenReturn(spannableBuilder);
nodeVisitor.visit(visitor, mock(SoftLineBreak.class));
verify(visitor, times(1)).builder();
verify(spannableBuilder, times(1)).append(eq(' '));
}
verify(visitor, times(1)).builder();
verify(spannableBuilder, times(1)).append(eq(' '));
@Test
public void implicit_movement_method_after_set_text_added() {
// validate that CorePlugin will implicitly add LinkMovementMethod if one is missing
final TextView textView = mock(TextView.class);
when(textView.getMovementMethod()).thenReturn(null);
} else {
final CorePlugin plugin = CorePlugin.create();
nodeVisitor.visit(visitor, mock(SoftLineBreak.class));
assertNull(textView.getMovementMethod());
verify(visitor, times(1)).ensureNewLine();
}
plugin.afterSetText(textView);
final ArgumentCaptor<MovementMethod> captor = ArgumentCaptor.forClass(MovementMethod.class);
verify(textView, times(1)).setMovementMethod(captor.capture());
assertNotNull(captor.getValue());
}
@Test
public void implicit_movement_method_after_set_text_no_op() {
// validate that CorePlugin won't change movement method if one is present on a TextView
final TextView textView = mock(TextView.class);
when(textView.getMovementMethod()).thenReturn(mock(MovementMethod.class));
final CorePlugin plugin = CorePlugin.create();
plugin.afterSetText(textView);
verify(textView, times(0)).setMovementMethod(any(MovementMethod.class));
}
}

View File

@ -73,7 +73,7 @@ abstract class BaseSuiteTest {
@NonNull
Markwon markwon() {
return Markwon.builder(RuntimeEnvironment.application)
.usePlugin(CorePlugin.create(softBreakAddsNewLine()))
.usePlugin(CorePlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
@ -99,10 +99,6 @@ abstract class BaseSuiteTest {
return false;
}
boolean softBreakAddsNewLine() {
return false;
}
private static final Map<Class<? extends Node>, SpanFactory> CORE_NODES;
static {

View File

@ -1,39 +0,0 @@
package ru.noties.markwon.core.suite;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import ru.noties.markwon.test.TestSpan.Document;
import static ru.noties.markwon.test.TestSpan.document;
import static ru.noties.markwon.test.TestSpan.text;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SoftBreakAddsNewLineTest extends BaseSuiteTest {
/*
hello there!
this one is on the next line
hard break to the full extend
*/
@Test
public void test() {
final Document document = document(
text("hello there!\n"),
text("this one is on the next line\n"),
text("hard break to the full extend")
);
matchInput("soft-break-adds-new-line.md", document);
}
@Override
boolean softBreakAddsNewLine() {
return true;
}
}

View File

@ -14,20 +14,15 @@ import static ru.noties.markwon.test.TestSpan.text;
@Config(manifest = Config.NONE)
public class SoftBreakTest extends BaseSuiteTest {
@Test
public void test() {
@Test
public void test() {
final Document document = document(
text("First line "),
text("same line but with space between "),
text("this is also the first line")
);
final Document document = document(
text("First line "),
text("same line but with space between "),
text("this is also the first line")
);
matchInput("soft-break.md", document);
}
@Override
boolean softBreakAddsNewLine() {
return false;
}
matchInput("soft-break.md", document);
}
}

View File

@ -109,7 +109,7 @@ public class PriorityProcessorTest {
public void subclass_found() {
// when a plugin comes after another, but _another_ was subclassed and placed in the list
final MarkwonPlugin core = new CorePlugin(false) {
final MarkwonPlugin core = new CorePlugin() {
};
final MarkwonPlugin plugin = new AbstractMarkwonPlugin() {
@NonNull

View File

@ -30,6 +30,13 @@ public class TablePlugin extends AbstractMarkwonPlugin {
void configureTheme(@NonNull TableTheme.Builder builder);
}
/**
* Factory method to create a {@link TablePlugin} with default {@link TableTheme} instance
* (obtained via {@link TableTheme#create(Context)} method)
*
* @see #create(TableTheme)
* @see #create(ThemeConfigure)
*/
@NonNull
public static TablePlugin create(@NonNull Context context) {
return new TablePlugin(TableTheme.create(context));

View File

@ -81,6 +81,8 @@ public class RecyclerActivity extends Activity {
.usePlugin(CorePlugin.create())
.usePlugin(ImagesPlugin.createWithAssets(context))
.usePlugin(SvgPlugin.create(context.getResources()))
// although we will be rendering table differently we still need
// to register commonmark-java tables extension (which TablePlugin does)
.usePlugin(TablePlugin.create(context))
.usePlugin(HtmlPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {