Core functionality plugin

This commit is contained in:
Dimitry Ivanov 2018-11-24 16:59:20 +03:00
parent 498c811987
commit 3526e16565
17 changed files with 260 additions and 68 deletions

View File

@ -41,17 +41,17 @@
<!--<data--> <!--<data-->
<!--android:host="*"--> <!--android:host="*"-->
<!--android:scheme="http"--> <!--android:scheme="http"-->
<!--android:mimeType="text/markdown"/>--> <!--android:mimeType="text/toMarkdown"/>-->
<!--<data--> <!--<data-->
<!--android:host="*"--> <!--android:host="*"-->
<!--android:scheme="file"--> <!--android:scheme="file"-->
<!--android:mimeType="text/markdown"/>--> <!--android:mimeType="text/toMarkdown"/>-->
<!--<data--> <!--<data-->
<!--android:host="*"--> <!--android:host="*"-->
<!--android:scheme="https"--> <!--android:scheme="https"-->
<!--android:mimeType="text/markdown"/>--> <!--android:mimeType="text/toMarkdown"/>-->
<data android:pathPattern=".*\\.markdown" /> <data android:pathPattern=".*\\.markdown" />
<data android:pathPattern=".*\\.mdown" /> <data android:pathPattern=".*\\.mdown" />

View File

@ -71,7 +71,7 @@ public class MainActivity extends Activity {
.use(new CorePlugin()) .use(new CorePlugin())
.use(TaskListPlugin.create(new TaskListDrawable(0xffff0000, 0xffff0000, -1))) .use(TaskListPlugin.create(new TaskListDrawable(0xffff0000, 0xffff0000, -1)))
.build(); .build();
final CharSequence markdown = markwon2.markdown("**hello _dear_** `code`\n\n- [ ] first\n- [x] second"); final CharSequence markdown = markwon2.toMarkdown("**hello _dear_** `code`\n\n- [ ] first\n- [x] second");
textView.setText(markdown); textView.setText(markdown);
return; return;
} }

View File

@ -104,7 +104,7 @@ public class MarkdownRenderer {
final long end = SystemClock.uptimeMillis(); final long end = SystemClock.uptimeMillis();
Debug.i("markdown rendered: %d ms", end - start); Debug.i("toMarkdown rendered: %d ms", end - start);
if (!isCancelled()) { if (!isCancelled()) {
handler.post(new Runnable() { handler.post(new Runnable() {

View File

@ -60,7 +60,7 @@ public class MarkwonViewHelper implements IMarkwonView {
this.provider = provider; this.provider = provider;
this.configuration = provider.provide(textView.getContext()); this.configuration = provider.provide(textView.getContext());
if (!TextUtils.isEmpty(markdown)) { if (!TextUtils.isEmpty(markdown)) {
// invalidate rendered markdown // invalidate rendered toMarkdown
setMarkdown(markdown); setMarkdown(markdown);
} }
} }

View File

@ -35,12 +35,12 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin {
} }
@Override @Override
public void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
} }
@Override @Override
public void afterSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { public void afterSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
} }
} }

View File

@ -48,12 +48,12 @@ public abstract class Markwon {
} }
/** /**
* Parses submitted raw markdown, converts it to CharSequence (with Spannables) * Parses submitted raw toMarkdown, converts it to CharSequence (with Spannables)
* and applies it to view * and applies it to view
* *
* @param view {@link TextView} to set markdown into * @param view {@link TextView} to set toMarkdown into
* @param configuration a {@link MarkwonConfiguration} instance * @param configuration a {@link MarkwonConfiguration} instance
* @param markdown raw markdown String (for example: {@code `**Hello**`}) * @param markdown raw toMarkdown String (for example: {@code `**Hello**`})
* @see #markdown(MarkwonConfiguration, String) * @see #markdown(MarkwonConfiguration, String)
* @see #setText(TextView, CharSequence) * @see #setText(TextView, CharSequence)
* @see MarkwonConfiguration * @see MarkwonConfiguration
@ -69,13 +69,13 @@ public abstract class Markwon {
} }
/** /**
* Helper method to apply parsed markdown. * Helper method to apply parsed toMarkdown.
* <p> * <p>
* Since 1.0.6 redirects it\'s call to {@link #setText(TextView, CharSequence, MovementMethod)} * Since 1.0.6 redirects it\'s call to {@link #setText(TextView, CharSequence, MovementMethod)}
* with LinkMovementMethod as an argument to preserve current API. * with LinkMovementMethod as an argument to preserve current API.
* *
* @param view {@link TextView} to set markdown into * @param view {@link TextView} to set toMarkdown into
* @param text parsed markdown * @param text parsed toMarkdown
* @see #setText(TextView, CharSequence, MovementMethod) * @see #setText(TextView, CharSequence, MovementMethod)
* @since 1.0.0 * @since 1.0.0
*/ */
@ -84,13 +84,13 @@ public abstract class Markwon {
} }
/** /**
* Helper method to apply parsed markdown with additional argument of a MovementMethod. Used * Helper method to apply parsed toMarkdown with additional argument of a MovementMethod. Used
* to workaround problems that occur when using system LinkMovementMethod (for example: * to workaround problems that occur when using system LinkMovementMethod (for example:
* https://issuetracker.google.com/issues/37068143). As a better alternative to it consider * https://issuetracker.google.com/issues/37068143). As a better alternative to it consider
* using: https://github.com/saket/Better-Link-Movement-Method * using: https://github.com/saket/Better-Link-Movement-Method
* *
* @param view TextView to set markdown into * @param view TextView to set toMarkdown into
* @param text parsed markdown * @param text parsed toMarkdown
* @param movementMethod an implementation if MovementMethod or null * @param movementMethod an implementation if MovementMethod or null
* @see #scheduleDrawables(TextView) * @see #scheduleDrawables(TextView)
* @see #scheduleTableRows(TextView) * @see #scheduleTableRows(TextView)
@ -102,7 +102,7 @@ public abstract class Markwon {
unscheduleTableRows(view); unscheduleTableRows(view);
// @since 2.0.1 we must measure ordered-list-item-spans before applying text to a TextView. // @since 2.0.1 we must measure ordered-list-item-spans before applying text to a TextView.
// if markdown has a lot of ordered list items (or text size is relatively big, or block-margin // if toMarkdown has a lot of ordered list items (or text size is relatively big, or block-margin
// is relatively small) then this list won't be rendered properly: it will take correct // is relatively small) then this list won't be rendered properly: it will take correct
// layout (width and margin) but will be clipped if margin is not _consistent_ between calls. // layout (width and margin) but will be clipped if margin is not _consistent_ between calls.
OrderedListItemSpan.measure(view, text); OrderedListItemSpan.measure(view, text);
@ -117,11 +117,11 @@ public abstract class Markwon {
} }
/** /**
* Returns parsed markdown with default {@link MarkwonConfiguration} obtained from {@link Context} * Returns parsed toMarkdown with default {@link MarkwonConfiguration} obtained from {@link Context}
* *
* @param context {@link Context} * @param context {@link Context}
* @param markdown raw markdown * @param markdown raw toMarkdown
* @return parsed markdown * @return parsed toMarkdown
* @since 1.0.0 * @since 1.0.0
*/ */
@NonNull @NonNull
@ -131,11 +131,11 @@ public abstract class Markwon {
} }
/** /**
* Returns parsed markdown with provided {@link MarkwonConfiguration} * Returns parsed toMarkdown with provided {@link MarkwonConfiguration}
* *
* @param configuration a {@link MarkwonConfiguration} * @param configuration a {@link MarkwonConfiguration}
* @param markdown raw markdown * @param markdown raw toMarkdown
* @return parsed markdown * @return parsed toMarkdown
* @see MarkwonConfiguration * @see MarkwonConfiguration
* @since 1.0.0 * @since 1.0.0
*/ */

View File

@ -2,6 +2,7 @@ package ru.noties.markwon;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.widget.TextView;
import org.commonmark.node.Node; import org.commonmark.node.Node;
@ -18,8 +19,13 @@ public abstract class Markwon2 {
@NonNull @NonNull
public abstract CharSequence render(@NonNull Node node); public abstract CharSequence render(@NonNull Node node);
// parse + render
@NonNull @NonNull
public abstract CharSequence markdown(@NonNull String input); public abstract CharSequence toMarkdown(@NonNull String input);
public abstract void setMarkdown(@NonNull TextView textView, @NonNull String markdown);
public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown);
public interface Builder { public interface Builder {

View File

@ -46,7 +46,7 @@ class MarkwonBuilderImpl implements Markwon2.Builder {
return new MarkwonImpl( return new MarkwonImpl(
parserBuilder.build(), parserBuilder.build(),
visitorBuilder.build(themeBuilder.build(), configurationBuilder.build()), visitorBuilder.build(configurationBuilder.build(themeBuilder.build())),
Collections.unmodifiableList(plugins) Collections.unmodifiableList(plugins)
); );
} }

View File

@ -20,7 +20,7 @@ public class MarkwonConfiguration {
// creates default configuration // creates default configuration
@NonNull @NonNull
public static MarkwonConfiguration create(@NonNull Context context) { public static MarkwonConfiguration create(@NonNull Context context) {
return new Builder(context).build(); return new Builder(context).build(MarkwonTheme.create(context));
} }
@NonNull @NonNull
@ -28,9 +28,8 @@ public class MarkwonConfiguration {
return new Builder(context); return new Builder(context);
} }
@Deprecated
private final MarkwonTheme theme;
private final MarkwonTheme theme;
private final AsyncDrawable.Loader asyncDrawableLoader; private final AsyncDrawable.Loader asyncDrawableLoader;
private final SyntaxHighlight syntaxHighlight; private final SyntaxHighlight syntaxHighlight;
private final LinkSpan.Resolver linkResolver; private final LinkSpan.Resolver linkResolver;
@ -65,7 +64,6 @@ public class MarkwonConfiguration {
} }
@NonNull @NonNull
@Deprecated
public MarkwonTheme theme() { public MarkwonTheme theme() {
return theme; return theme;
} }
@ -137,7 +135,6 @@ public class MarkwonConfiguration {
private final Context context; private final Context context;
@Deprecated
private MarkwonTheme theme; private MarkwonTheme theme;
private AsyncDrawable.Loader asyncDrawableLoader; private AsyncDrawable.Loader asyncDrawableLoader;
private SyntaxHighlight syntaxHighlight; private SyntaxHighlight syntaxHighlight;
@ -169,12 +166,12 @@ public class MarkwonConfiguration {
this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags; this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags;
} }
@NonNull // @NonNull
@Deprecated // @Deprecated
public Builder theme(@NonNull MarkwonTheme theme) { // public Builder theme(@NonNull MarkwonTheme theme) {
this.theme = theme; // this.theme = theme;
return this; // return this;
} // }
@NonNull @NonNull
public Builder asyncDrawableLoader(@NonNull AsyncDrawable.Loader asyncDrawableLoader) { public Builder asyncDrawableLoader(@NonNull AsyncDrawable.Loader asyncDrawableLoader) {
@ -263,11 +260,9 @@ public class MarkwonConfiguration {
} }
@NonNull @NonNull
public MarkwonConfiguration build() { public MarkwonConfiguration build(@NonNull MarkwonTheme theme) {
if (theme == null) { this.theme = theme;
theme = MarkwonTheme.create(context);
}
if (asyncDrawableLoader == null) { if (asyncDrawableLoader == null) {
asyncDrawableLoader = new AsyncDrawableLoaderNoOp(); asyncDrawableLoader = new AsyncDrawableLoaderNoOp();

View File

@ -1,6 +1,7 @@
package ru.noties.markwon; package ru.noties.markwon;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.widget.TextView;
import org.commonmark.node.Node; import org.commonmark.node.Node;
import org.commonmark.parser.Parser; import org.commonmark.parser.Parser;
@ -42,7 +43,26 @@ class MarkwonImpl extends Markwon2 {
@NonNull @NonNull
@Override @Override
public CharSequence markdown(@NonNull String input) { public CharSequence toMarkdown(@NonNull String input) {
return render(parse(input)); return render(parse(input));
} }
@Override
public void setMarkdown(@NonNull TextView textView, @NonNull String markdown) {
setParsedMarkdown(textView, toMarkdown(markdown));
}
@Override
public void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown) {
for (MarkwonPlugin plugin : plugins) {
plugin.beforeSetText(textView, markdown);
}
textView.setText(markdown);
for (MarkwonPlugin plugin : plugins) {
plugin.afterSetText(textView, markdown);
}
}
} }

View File

@ -23,7 +23,7 @@ public interface MarkwonPlugin {
@NonNull @NonNull
String processMarkdown(@NonNull String markdown); String processMarkdown(@NonNull String markdown);
void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder); void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown);
void afterSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder); void afterSetText(@NonNull TextView textView, @NonNull CharSequence markdown);
} }

View File

@ -20,14 +20,17 @@ public interface MarkwonVisitor extends Visitor {
<N extends Node> Builder on(@NonNull Class<N> node, @NonNull NodeVisitor<N> nodeVisitor); <N extends Node> Builder on(@NonNull Class<N> node, @NonNull NodeVisitor<N> nodeVisitor);
@NonNull @NonNull
MarkwonVisitor build(@NonNull MarkwonTheme theme, @NonNull MarkwonConfiguration configuration); MarkwonVisitor build(@NonNull MarkwonConfiguration configuration);
} }
@NonNull
MarkwonConfiguration configuration();
@NonNull @NonNull
MarkwonTheme theme(); MarkwonTheme theme();
@NonNull @NonNull
MarkwonConfiguration configuration(); SpannableFactory factory();
@NonNull @NonNull
SpannableBuilder builder(); SpannableBuilder builder();

View File

@ -37,19 +37,21 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes; private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes;
private final MarkwonTheme theme;
private final MarkwonConfiguration configuration; private final MarkwonConfiguration configuration;
private final MarkwonTheme theme;
private final SpannableFactory factory;
private final SpannableBuilder builder = new SpannableBuilder(); private final SpannableBuilder builder = new SpannableBuilder();
private int blockQuoteIndent; private int blockQuoteIndent;
private int listLevel; private int listLevel;
private MarkwonVisitorImpl( private MarkwonVisitorImpl(
@NonNull MarkwonTheme theme,
@NonNull MarkwonConfiguration configuration, @NonNull MarkwonConfiguration configuration,
@NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) {
this.theme = theme;
this.configuration = configuration; this.configuration = configuration;
this.theme = configuration.theme();
this.factory = configuration.factory();
this.nodes = nodes; this.nodes = nodes;
} }
@ -173,6 +175,12 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
} }
} }
@NonNull
@Override
public MarkwonConfiguration configuration() {
return configuration;
}
@NonNull @NonNull
@Override @Override
public MarkwonTheme theme() { public MarkwonTheme theme() {
@ -181,8 +189,8 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
@NonNull @NonNull
@Override @Override
public MarkwonConfiguration configuration() { public SpannableFactory factory() {
return configuration; return factory;
} }
@NonNull @NonNull
@ -280,9 +288,8 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
@NonNull @NonNull
@Override @Override
public MarkwonVisitor build(@NonNull MarkwonTheme theme, @NonNull MarkwonConfiguration configuration) { public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration) {
return new MarkwonVisitorImpl( return new MarkwonVisitorImpl(
theme,
configuration, configuration,
Collections.unmodifiableMap(nodes)); Collections.unmodifiableMap(nodes));
} }

View File

@ -9,17 +9,24 @@ import org.commonmark.node.BulletList;
import org.commonmark.node.Code; import org.commonmark.node.Code;
import org.commonmark.node.Emphasis; import org.commonmark.node.Emphasis;
import org.commonmark.node.FencedCodeBlock; import org.commonmark.node.FencedCodeBlock;
import org.commonmark.node.HardLineBreak;
import org.commonmark.node.Heading;
import org.commonmark.node.Image;
import org.commonmark.node.IndentedCodeBlock; import org.commonmark.node.IndentedCodeBlock;
import org.commonmark.node.Link;
import org.commonmark.node.ListBlock;
import org.commonmark.node.ListItem; import org.commonmark.node.ListItem;
import org.commonmark.node.Node; import org.commonmark.node.Node;
import org.commonmark.node.OrderedList; import org.commonmark.node.OrderedList;
import org.commonmark.node.Paragraph;
import org.commonmark.node.SoftLineBreak;
import org.commonmark.node.StrongEmphasis; import org.commonmark.node.StrongEmphasis;
import org.commonmark.node.Text; import org.commonmark.node.Text;
import org.commonmark.node.ThematicBreak; import org.commonmark.node.ThematicBreak;
import ru.noties.markwon.AbstractMarkwonPlugin; import ru.noties.markwon.AbstractMarkwonPlugin;
import ru.noties.markwon.MarkwonConfiguration;
import ru.noties.markwon.MarkwonVisitor; import ru.noties.markwon.MarkwonVisitor;
import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.spans.OrderedListItemSpan; import ru.noties.markwon.spans.OrderedListItemSpan;
public class CorePlugin extends AbstractMarkwonPlugin { public class CorePlugin extends AbstractMarkwonPlugin {
@ -30,6 +37,25 @@ public class CorePlugin extends AbstractMarkwonPlugin {
// todo: softBreak adds new line should be here (or maybe removed even?) // todo: softBreak adds new line should be here (or maybe removed even?)
// todo: add a simple HTML handler
// todo: configure primitive images (without okhttp -> just HttpUrlConnection and simple types (static, data)
@NonNull
public static CorePlugin create() {
return create(false);
}
@NonNull
public static CorePlugin create(boolean softBreakAddsNewLine) {
return new CorePlugin(softBreakAddsNewLine);
}
private final boolean softBreakAddsNewLine;
protected CorePlugin(boolean softBreakAddsNewLine) {
this.softBreakAddsNewLine = softBreakAddsNewLine;
}
@Override @Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
text(builder); text(builder);
@ -43,11 +69,17 @@ public class CorePlugin extends AbstractMarkwonPlugin {
orderedList(builder); orderedList(builder);
listItem(builder); listItem(builder);
thematicBreak(builder); thematicBreak(builder);
heading(builder);
softLineBreak(builder);
hardLineBreak(builder);
paragraph(builder);
image(builder);
link(builder);
} }
@Override @Override
public void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) {
OrderedListItemSpan.measure(textView, builder); OrderedListItemSpan.measure(textView, markdown);
} }
protected void text(@NonNull MarkwonVisitor.Builder builder) { protected void text(@NonNull MarkwonVisitor.Builder builder) {
@ -65,7 +97,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) {
final int length = visitor.length(); final int length = visitor.length();
visitor.visitChildren(strongEmphasis); visitor.visitChildren(strongEmphasis);
visitor.setSpans(length, visitor.configuration().factory().strongEmphasis()); visitor.setSpans(length, visitor.factory().strongEmphasis());
} }
}); });
} }
@ -76,7 +108,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) { public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) {
final int length = visitor.length(); final int length = visitor.length();
visitor.visitChildren(emphasis); visitor.visitChildren(emphasis);
visitor.setSpans(length, visitor.configuration().factory().emphasis()); visitor.setSpans(length, visitor.factory().emphasis());
} }
}); });
} }
@ -95,7 +127,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
final int length = visitor.length(); final int length = visitor.length();
visitor.incrementBlockQuoteIndent(); visitor.incrementBlockQuoteIndent();
visitor.visitChildren(blockQuote); visitor.visitChildren(blockQuote);
visitor.setSpans(length, visitor.configuration().factory().blockQuote(visitor.theme())); visitor.setSpans(length, visitor.factory().blockQuote(visitor.theme()));
visitor.decrementBlockQuoteIndent(); visitor.decrementBlockQuoteIndent();
if (visitor.hasNext(blockQuote)) { if (visitor.hasNext(blockQuote)) {
@ -122,7 +154,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
.append(code.getLiteral()) .append(code.getLiteral())
.append('\u00a0'); .append('\u00a0');
visitor.setSpans(length, visitor.configuration().factory().code(visitor.theme(), false)); visitor.setSpans(length, visitor.factory().code(visitor.theme(), false));
} }
}); });
} }
@ -163,7 +195,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
visitor.builder().append('\u00a0'); visitor.builder().append('\u00a0');
visitor.setSpans(length, visitor.configuration().factory().code(visitor.theme(), true)); visitor.setSpans(length, visitor.factory().code(visitor.theme(), true));
if (visitor.hasNext(node)) { if (visitor.hasNext(node)) {
visitor.ensureNewLine(); visitor.ensureNewLine();
@ -220,7 +252,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
final int start = ((OrderedList) parent).getStartNumber(); final int start = ((OrderedList) parent).getStartNumber();
visitor.visitChildren(listItem); visitor.visitChildren(listItem);
visitor.setSpans(length, visitor.configuration().factory().orderedListItem(visitor.theme(), start)); visitor.setSpans(length, visitor.factory().orderedListItem(visitor.theme(), start));
// after we have visited the children increment start number // after we have visited the children increment start number
@ -230,7 +262,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
} else { } else {
visitor.visitChildren(listItem); visitor.visitChildren(listItem);
visitor.setSpans(length, visitor.configuration().factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1)); visitor.setSpans(length, visitor.factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1));
} }
@ -256,7 +288,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
// without space it won't render // without space it won't render
visitor.builder().append('\u00a0'); visitor.builder().append('\u00a0');
visitor.setSpans(length, visitor.configuration().factory().thematicBreak(visitor.theme())); visitor.setSpans(length, visitor.factory().thematicBreak(visitor.theme()));
if (visitor.hasNext(thematicBreak)) { if (visitor.hasNext(thematicBreak)) {
visitor.ensureNewLine(); visitor.ensureNewLine();
@ -265,4 +297,132 @@ public class CorePlugin extends AbstractMarkwonPlugin {
} }
}); });
} }
protected void heading(@NonNull MarkwonVisitor.Builder builder) {
builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
visitor.ensureNewLine();
final int length = visitor.length();
visitor.visitChildren(heading);
visitor.setSpans(length, visitor.factory().heading(visitor.theme(), heading.getLevel()));
if (visitor.hasNext(heading)) {
visitor.ensureNewLine();
visitor.forceNewLine();
}
}
});
}
protected 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(' ');
}
}
});
}
protected void hardLineBreak(@NonNull MarkwonVisitor.Builder builder) {
builder.on(HardLineBreak.class, new MarkwonVisitor.NodeVisitor<HardLineBreak>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HardLineBreak hardLineBreak) {
visitor.ensureNewLine();
}
});
}
protected void paragraph(@NonNull MarkwonVisitor.Builder builder) {
builder.on(Paragraph.class, new MarkwonVisitor.NodeVisitor<Paragraph>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Paragraph paragraph) {
final boolean inTightList = isInTightList(paragraph);
if (!inTightList) {
visitor.ensureNewLine();
}
final int length = visitor.length();
visitor.visitChildren(paragraph);
// @since 1.1.1 apply paragraph span
visitor.setSpans(length, visitor.factory().paragraph(inTightList));
if (!inTightList && visitor.hasNext(paragraph)) {
visitor.ensureNewLine();
if (visitor.blockQuoteIndent() == 0) {
visitor.forceNewLine();
}
}
}
});
}
protected void image(@NonNull MarkwonVisitor.Builder builder) {
builder.on(Image.class, new MarkwonVisitor.NodeVisitor<Image>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) {
final int length = visitor.length();
visitor.visitChildren(image);
// we must check if anything _was_ added, as we need at least one char to render
if (length == visitor.length()) {
visitor.builder().append('\uFFFC');
}
final MarkwonConfiguration configuration = visitor.configuration();
final Node parent = image.getParent();
final boolean link = parent instanceof Link;
final String destination = configuration
.urlProcessor()
.process(image.getDestination());
final Object spans = visitor.factory().image(
visitor.theme(),
destination,
configuration.asyncDrawableLoader(),
configuration.imageSizeResolver(),
null,
link);
visitor.setSpans(length, spans);
}
});
}
protected void link(@NonNull MarkwonVisitor.Builder builder) {
builder.on(Link.class, new MarkwonVisitor.NodeVisitor<Link>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Link link) {
final int length = visitor.length();
visitor.visitChildren(link);
final MarkwonConfiguration configuration = visitor.configuration();
final String destination = configuration.urlProcessor().process(link.getDestination());
visitor.setSpans(length, visitor.factory().link(visitor.theme(), destination, configuration.linkResolver()));
}
});
}
private static boolean isInTightList(@NonNull Paragraph paragraph) {
final Node parent = paragraph.getParent();
if (parent != null) {
final Node gramps = parent.getParent();
if (gramps instanceof ListBlock) {
ListBlock list = (ListBlock) gramps;
return list.isTight();
}
}
return false;
}
} }

View File

@ -20,8 +20,8 @@ public class OrderedListItemSpan implements LeadingMarginSpan {
* NB, this method must be called <em>before</em> setting text to a TextView (`TextView#setText` * NB, this method must be called <em>before</em> setting text to a TextView (`TextView#setText`
* internally can trigger new Layout creation which will ask for leading margins right away) * internally can trigger new Layout creation which will ask for leading margins right away)
* *
* @param textView to which markdown will be applied * @param textView to which toMarkdown will be applied
* @param text parsed markdown to process * @param text parsed toMarkdown to process
* @since 2.0.1 * @since 2.0.1
*/ */
public static void measure(@NonNull TextView textView, @NonNull CharSequence text) { public static void measure(@NonNull TextView textView, @NonNull CharSequence text) {

View File

@ -4,6 +4,7 @@ import android.text.Spanned;
public abstract class LeadingMarginUtils { public abstract class LeadingMarginUtils {
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public static boolean selfStart(int start, CharSequence text, Object span) { public static boolean selfStart(int start, CharSequence text, Object span) {
return text instanceof Spanned && ((Spanned) text).getSpanStart(span) == start; return text instanceof Spanned && ((Spanned) text).getSpanStart(span) == start;
} }

View File

@ -47,7 +47,7 @@ public class MainActivity extends Activity {
final SpannableBuilder builder = new SpannableBuilder(); final SpannableBuilder builder = new SpannableBuilder();
// please note that here I am passing `0` as fallback it means that if markdown references // please note that here I am passing `0` as fallback it means that if toMarkdown references
// unknown icon, it will try to load fallback one and will fail with ResourceNotFound. It's // unknown icon, it will try to load fallback one and will fail with ResourceNotFound. It's
// better to provide a valid fallback option // better to provide a valid fallback option
final IconSpanProvider spanProvider = IconSpanProvider.create(this, 0); final IconSpanProvider spanProvider = IconSpanProvider.create(this, 0);
@ -59,7 +59,7 @@ public class MainActivity extends Activity {
.headingTextSizeMultipliers(textSizeMultipliers) .headingTextSizeMultipliers(textSizeMultipliers)
.build()) .build())
.build(); .build();
// create an instance of visitor to process parsed markdown // create an instance of visitor to process parsed toMarkdown
final IconVisitor visitor = new IconVisitor( final IconVisitor visitor = new IconVisitor(
configuration, configuration,
builder, builder,