Extracted span generation into a separate factory
This commit is contained in:
parent
194218df2a
commit
bef7fd235f
127
library/src/main/java/ru/noties/markwon/SpanFactoryDef.java
Normal file
127
library/src/main/java/ru/noties/markwon/SpanFactoryDef.java
Normal file
@ -0,0 +1,127 @@
|
||||
package ru.noties.markwon;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
|
||||
import ru.noties.markwon.renderer.ImageSizeResolver;
|
||||
import ru.noties.markwon.spans.AsyncDrawable;
|
||||
import ru.noties.markwon.spans.AsyncDrawableSpan;
|
||||
import ru.noties.markwon.spans.BlockQuoteSpan;
|
||||
import ru.noties.markwon.spans.BulletListItemSpan;
|
||||
import ru.noties.markwon.spans.CodeSpan;
|
||||
import ru.noties.markwon.spans.EmphasisSpan;
|
||||
import ru.noties.markwon.spans.HeadingSpan;
|
||||
import ru.noties.markwon.spans.LinkSpan;
|
||||
import ru.noties.markwon.spans.OrderedListItemSpan;
|
||||
import ru.noties.markwon.spans.SpanFactory;
|
||||
import ru.noties.markwon.spans.SpannableTheme;
|
||||
import ru.noties.markwon.spans.StrongEmphasisSpan;
|
||||
import ru.noties.markwon.spans.TaskListSpan;
|
||||
import ru.noties.markwon.spans.ThematicBreakSpan;
|
||||
|
||||
public class SpanFactoryDef implements SpanFactory {
|
||||
@NonNull
|
||||
private final SpannableTheme theme;
|
||||
|
||||
@NonNull
|
||||
private final LinkSpan.Resolver linkResolver;
|
||||
|
||||
@NonNull
|
||||
private final AsyncDrawable.Loader drawableLoader;
|
||||
|
||||
@NonNull
|
||||
private final ImageSizeResolver imageSizeResolver;
|
||||
|
||||
public SpanFactoryDef(@NonNull SpannableTheme theme,
|
||||
@NonNull LinkSpan.Resolver linkResolver,
|
||||
@NonNull AsyncDrawable.Loader drawableLoader,
|
||||
@NonNull ImageSizeResolver imageSizeResolver) {
|
||||
this.theme = theme;
|
||||
this.linkResolver = linkResolver;
|
||||
this.drawableLoader = drawableLoader;
|
||||
this.imageSizeResolver = imageSizeResolver;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createBlockQuote() {
|
||||
return new BlockQuoteSpan(theme);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createBulletListItem(int level) {
|
||||
return new BulletListItemSpan(theme, level);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createCode(boolean multiline) {
|
||||
return new CodeSpan(theme, multiline);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createEmphasis() {
|
||||
return new EmphasisSpan();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createHeading(int level) {
|
||||
return new HeadingSpan(theme, level);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createImage(@NonNull String destination, boolean link) {
|
||||
return new AsyncDrawableSpan(
|
||||
theme,
|
||||
new AsyncDrawable(
|
||||
destination,
|
||||
drawableLoader,
|
||||
imageSizeResolver,
|
||||
null
|
||||
),
|
||||
AsyncDrawableSpan.ALIGN_BOTTOM,
|
||||
link
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createLink(@NonNull String destination) {
|
||||
return new LinkSpan(theme, destination, linkResolver);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createOrderedListItem(int order) {
|
||||
// todo| in order to provide real RTL experience there must be a way to provide this string
|
||||
return new OrderedListItemSpan(theme, String.valueOf(order) + "." + '\u00a0');
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createStrikethrough() {
|
||||
return new StrikethroughSpan();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createStrongEmphasis() {
|
||||
return new StrongEmphasisSpan();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createTaskList(int indent, boolean done) {
|
||||
return new TaskListSpan(theme, indent, done);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object createThematicBreak() {
|
||||
return new ThematicBreakSpan(theme);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import ru.noties.markwon.renderer.ImageSizeResolverDef;
|
||||
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
|
||||
import ru.noties.markwon.spans.AsyncDrawable;
|
||||
import ru.noties.markwon.spans.LinkSpan;
|
||||
import ru.noties.markwon.spans.SpanFactory;
|
||||
import ru.noties.markwon.spans.SpannableTheme;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@ -31,6 +32,7 @@ public class SpannableConfiguration {
|
||||
private final UrlProcessor urlProcessor;
|
||||
private final SpannableHtmlParser htmlParser;
|
||||
private final ImageSizeResolver imageSizeResolver;
|
||||
private final SpanFactory spanFactory;
|
||||
|
||||
private SpannableConfiguration(@NonNull Builder builder) {
|
||||
this.theme = builder.theme;
|
||||
@ -40,6 +42,7 @@ public class SpannableConfiguration {
|
||||
this.urlProcessor = builder.urlProcessor;
|
||||
this.htmlParser = builder.htmlParser;
|
||||
this.imageSizeResolver = builder.imageSizeResolver;
|
||||
this.spanFactory = builder.spanFactory;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -77,6 +80,11 @@ public class SpannableConfiguration {
|
||||
return imageSizeResolver;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SpanFactory spanFactory() {
|
||||
return spanFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Builder {
|
||||
|
||||
@ -88,6 +96,7 @@ public class SpannableConfiguration {
|
||||
private UrlProcessor urlProcessor;
|
||||
private SpannableHtmlParser htmlParser;
|
||||
private ImageSizeResolver imageSizeResolver;
|
||||
private SpanFactory spanFactory;
|
||||
|
||||
Builder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
@ -138,6 +147,12 @@ public class SpannableConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder spanFactory(@NonNull SpanFactory spanFactory) {
|
||||
this.spanFactory = spanFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SpannableConfiguration build() {
|
||||
|
||||
@ -165,6 +180,10 @@ public class SpannableConfiguration {
|
||||
imageSizeResolver = new ImageSizeResolverDef();
|
||||
}
|
||||
|
||||
if (spanFactory == null) {
|
||||
spanFactory = new SpanFactoryDef(theme, linkResolver, asyncDrawableLoader, imageSizeResolver);
|
||||
}
|
||||
|
||||
if (htmlParser == null) {
|
||||
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver, imageSizeResolver);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
|
||||
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
|
||||
import org.commonmark.ext.gfm.tables.TableBody;
|
||||
@ -44,19 +43,8 @@ import java.util.List;
|
||||
import ru.noties.markwon.ReverseSpannableStringBuilder;
|
||||
import ru.noties.markwon.SpannableConfiguration;
|
||||
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
|
||||
import ru.noties.markwon.spans.AsyncDrawable;
|
||||
import ru.noties.markwon.spans.AsyncDrawableSpan;
|
||||
import ru.noties.markwon.spans.BlockQuoteSpan;
|
||||
import ru.noties.markwon.spans.BulletListItemSpan;
|
||||
import ru.noties.markwon.spans.CodeSpan;
|
||||
import ru.noties.markwon.spans.EmphasisSpan;
|
||||
import ru.noties.markwon.spans.HeadingSpan;
|
||||
import ru.noties.markwon.spans.LinkSpan;
|
||||
import ru.noties.markwon.spans.OrderedListItemSpan;
|
||||
import ru.noties.markwon.spans.StrongEmphasisSpan;
|
||||
import ru.noties.markwon.spans.SpanFactory;
|
||||
import ru.noties.markwon.spans.TableRowSpan;
|
||||
import ru.noties.markwon.spans.TaskListSpan;
|
||||
import ru.noties.markwon.spans.ThematicBreakSpan;
|
||||
import ru.noties.markwon.tasklist.TaskListBlock;
|
||||
import ru.noties.markwon.tasklist.TaskListItem;
|
||||
|
||||
@ -64,6 +52,7 @@ import ru.noties.markwon.tasklist.TaskListItem;
|
||||
public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
private final SpannableConfiguration configuration;
|
||||
private final SpanFactory spanFactory;
|
||||
private final SpannableStringBuilder builder;
|
||||
private final Deque<HtmlInlineItem> htmlInlineItems;
|
||||
|
||||
@ -79,6 +68,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
@NonNull SpannableStringBuilder builder
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
this.spanFactory = configuration.spanFactory();
|
||||
this.builder = builder;
|
||||
this.htmlInlineItems = new ArrayDeque<>(2);
|
||||
}
|
||||
@ -92,14 +82,14 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
public void visit(StrongEmphasis strongEmphasis) {
|
||||
final int length = builder.length();
|
||||
visitChildren(strongEmphasis);
|
||||
setSpan(length, new StrongEmphasisSpan());
|
||||
setSpan(length, spanFactory.createStrongEmphasis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Emphasis emphasis) {
|
||||
final int length = builder.length();
|
||||
visitChildren(emphasis);
|
||||
setSpan(length, new EmphasisSpan());
|
||||
setSpan(length, spanFactory.createEmphasis());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +106,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
visitChildren(blockQuote);
|
||||
|
||||
setSpan(length, new BlockQuoteSpan(configuration.theme()));
|
||||
setSpan(length, spanFactory.createBlockQuote());
|
||||
|
||||
blockQuoteIndent -= 1;
|
||||
|
||||
@ -137,10 +127,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
builder.append(code.getLiteral());
|
||||
builder.append('\u00a0');
|
||||
|
||||
setSpan(length, new CodeSpan(
|
||||
configuration.theme(),
|
||||
false
|
||||
));
|
||||
setSpan(length, spanFactory.createCode(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,10 +162,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
);
|
||||
builder.append('\u00a0').append('\n');
|
||||
|
||||
setSpan(length, new CodeSpan(
|
||||
configuration.theme(),
|
||||
true
|
||||
));
|
||||
setSpan(length, spanFactory.createCode(true));
|
||||
|
||||
newLine();
|
||||
builder.append('\n');
|
||||
@ -218,11 +202,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
visitChildren(listItem);
|
||||
|
||||
// todo| in order to provide real RTL experience there must be a way to provide this string
|
||||
setSpan(length, new OrderedListItemSpan(
|
||||
configuration.theme(),
|
||||
String.valueOf(start) + "." + '\u00a0'
|
||||
));
|
||||
setSpan(length, spanFactory.createOrderedListItem(start));
|
||||
|
||||
// after we have visited the children increment start number
|
||||
final OrderedList orderedList = (OrderedList) parent;
|
||||
@ -232,10 +212,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
visitChildren(listItem);
|
||||
|
||||
setSpan(length, new BulletListItemSpan(
|
||||
configuration.theme(),
|
||||
listLevel - 1
|
||||
));
|
||||
setSpan(length, spanFactory.createBulletListItem(listLevel - 1));
|
||||
}
|
||||
|
||||
blockQuoteIndent -= 1;
|
||||
@ -251,7 +228,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
final int length = builder.length();
|
||||
builder.append(' '); // without space it won't render
|
||||
setSpan(length, new ThematicBreakSpan(configuration.theme()));
|
||||
setSpan(length, spanFactory.createThematicBreak());
|
||||
|
||||
newLine();
|
||||
builder.append('\n');
|
||||
@ -264,7 +241,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
final int length = builder.length();
|
||||
visitChildren(heading);
|
||||
setSpan(length, new HeadingSpan(configuration.theme(), heading.getLevel()));
|
||||
setSpan(length, spanFactory.createHeading(heading.getLevel()));
|
||||
|
||||
newLine();
|
||||
|
||||
@ -306,7 +283,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
final int length = builder.length();
|
||||
visitChildren(customNode);
|
||||
setSpan(length, new StrikethroughSpan());
|
||||
setSpan(length, spanFactory.createStrikethrough());
|
||||
|
||||
} else if (customNode instanceof TaskListItem) {
|
||||
|
||||
@ -320,11 +297,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
visitChildren(customNode);
|
||||
|
||||
setSpan(length, new TaskListSpan(
|
||||
configuration.theme(),
|
||||
blockQuoteIndent,
|
||||
listItem.done()
|
||||
));
|
||||
setSpan(length, spanFactory.createTaskList(blockQuoteIndent, listItem.done()));
|
||||
|
||||
newLine();
|
||||
|
||||
@ -448,20 +421,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
final boolean link = parent != null && parent instanceof Link;
|
||||
final String destination = configuration.urlProcessor().process(image.getDestination());
|
||||
|
||||
setSpan(
|
||||
length,
|
||||
new AsyncDrawableSpan(
|
||||
configuration.theme(),
|
||||
new AsyncDrawable(
|
||||
destination,
|
||||
configuration.asyncDrawableLoader(),
|
||||
configuration.imageSizeResolver(),
|
||||
null
|
||||
),
|
||||
AsyncDrawableSpan.ALIGN_BOTTOM,
|
||||
link
|
||||
)
|
||||
);
|
||||
setSpan(length, spanFactory.createImage(destination, link));
|
||||
|
||||
// todo, maybe, if image is not inside a link, we should make it clickable, so
|
||||
// user can open it in external viewer?
|
||||
@ -520,11 +480,17 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
final int length = builder.length();
|
||||
visitChildren(link);
|
||||
final String destination = configuration.urlProcessor().process(link.getDestination());
|
||||
setSpan(length, new LinkSpan(configuration.theme(), destination, configuration.linkResolver()));
|
||||
setSpan(length, spanFactory.createLink(destination));
|
||||
}
|
||||
|
||||
private void setSpan(int start, @NonNull Object span) {
|
||||
builder.setSpan(span, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
private void setSpan(int start, @NonNull Object spans) {
|
||||
if (spans instanceof Object[]) {
|
||||
for (final Object span : (Object[]) spans) {
|
||||
builder.setSpan(span, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
} else {
|
||||
builder.setSpan(spans, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
private void newLine() {
|
||||
|
@ -0,0 +1,42 @@
|
||||
package ru.noties.markwon.spans;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public interface SpanFactory {
|
||||
@NonNull
|
||||
Object createBlockQuote();
|
||||
|
||||
@NonNull
|
||||
Object createBulletListItem(int level);
|
||||
|
||||
@NonNull
|
||||
Object createCode(boolean multiline);
|
||||
|
||||
@NonNull
|
||||
Object createEmphasis();
|
||||
|
||||
@NonNull
|
||||
Object createHeading(int level);
|
||||
|
||||
@NonNull
|
||||
Object createImage(@NonNull String destination, boolean link);
|
||||
|
||||
@NonNull
|
||||
Object createLink(@NonNull String destination);
|
||||
|
||||
@NonNull
|
||||
Object createOrderedListItem(int order);
|
||||
|
||||
@NonNull
|
||||
Object createStrongEmphasis();
|
||||
|
||||
@NonNull
|
||||
Object createStrikethrough();
|
||||
|
||||
@NonNull
|
||||
Object createTaskList(int indent, boolean done);
|
||||
|
||||
@NonNull
|
||||
Object createThematicBreak();
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user