Bring back html-api to core module
This commit is contained in:
parent
ec3d596d8a
commit
6f025b9a95
@ -8,41 +8,57 @@ import org.commonmark.node.HtmlInline;
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.html.tag.BlockquoteHandler;
|
||||
import ru.noties.markwon.html.tag.EmphasisHandler;
|
||||
import ru.noties.markwon.html.tag.HeadingHandler;
|
||||
import ru.noties.markwon.html.tag.ImageHandler;
|
||||
import ru.noties.markwon.html.tag.LinkHandler;
|
||||
import ru.noties.markwon.html.tag.ListHandler;
|
||||
import ru.noties.markwon.html.tag.StrikeHandler;
|
||||
import ru.noties.markwon.html.tag.StrongEmphasisHandler;
|
||||
import ru.noties.markwon.html.tag.SubScriptHandler;
|
||||
import ru.noties.markwon.html.tag.SuperScriptHandler;
|
||||
import ru.noties.markwon.html.tag.UnderlineHandler;
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class HtmlPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create() {
|
||||
return create(MarkwonHtmlRendererImpl.create(), MarkwonHtmlParserImpl.create());
|
||||
return new HtmlPlugin();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create(@NonNull MarkwonHtmlRenderer renderer) {
|
||||
return create(renderer, MarkwonHtmlParserImpl.create());
|
||||
public static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
|
||||
|
||||
@Override
|
||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||
builder.htmlParser(MarkwonHtmlParserImpl.create());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create(@NonNull MarkwonHtmlParser parser) {
|
||||
return create(MarkwonHtmlRendererImpl.create(), parser);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create(@NonNull MarkwonHtmlRenderer renderer, @NonNull MarkwonHtmlParser parser) {
|
||||
return new HtmlPlugin(renderer, parser);
|
||||
}
|
||||
|
||||
private final MarkwonHtmlRenderer renderer;
|
||||
private final MarkwonHtmlParser parser;
|
||||
|
||||
public HtmlPlugin(@NonNull MarkwonHtmlRenderer renderer, @NonNull MarkwonHtmlParser parser) {
|
||||
this.renderer = renderer;
|
||||
this.parser = parser;
|
||||
@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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) {
|
||||
renderer.render(visitor, parser);
|
||||
final MarkwonConfiguration configuration = visitor.configuration();
|
||||
configuration.htmlRenderer().render(visitor, configuration.htmlParser());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,7 +80,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
private void visitHtml(@NonNull MarkwonVisitor visitor, @Nullable String html) {
|
||||
if (html != null) {
|
||||
parser.processFragment(visitor.builder(), html);
|
||||
visitor.configuration().htmlParser().processFragment(visitor.builder(), html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
package ru.noties.markwon.html;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class MarkwonHtmlRenderer {
|
||||
|
||||
public abstract void render(
|
||||
@NonNull MarkwonVisitor visitor,
|
||||
@NonNull MarkwonHtmlParser parser
|
||||
);
|
||||
|
||||
@Nullable
|
||||
public abstract TagHandler tagHandler(@NonNull String tagName);
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
package ru.noties.markwon.html;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.html.tag.BlockquoteHandler;
|
||||
import ru.noties.markwon.html.tag.EmphasisHandler;
|
||||
import ru.noties.markwon.html.tag.HeadingHandler;
|
||||
import ru.noties.markwon.html.tag.ImageHandler;
|
||||
import ru.noties.markwon.html.tag.LinkHandler;
|
||||
import ru.noties.markwon.html.tag.ListHandler;
|
||||
import ru.noties.markwon.html.tag.StrikeHandler;
|
||||
import ru.noties.markwon.html.tag.StrongEmphasisHandler;
|
||||
import ru.noties.markwon.html.tag.SubScriptHandler;
|
||||
import ru.noties.markwon.html.tag.SuperScriptHandler;
|
||||
import ru.noties.markwon.html.tag.UnderlineHandler;
|
||||
|
||||
public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
|
||||
@NonNull
|
||||
public static MarkwonHtmlRendererImpl create() {
|
||||
return builderWithDefaults().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NonNull
|
||||
public static MarkwonHtmlRendererImpl create(boolean allowNonClosedTags) {
|
||||
return builderWithDefaults(allowNonClosedTags).build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Builder builderWithDefaults() {
|
||||
return builderWithDefaults(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NonNull
|
||||
public static Builder builderWithDefaults(boolean allowNonClosedTags) {
|
||||
|
||||
final EmphasisHandler emphasisHandler = new EmphasisHandler();
|
||||
final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler();
|
||||
final StrikeHandler strikeHandler = new StrikeHandler();
|
||||
final UnderlineHandler underlineHandler = new UnderlineHandler();
|
||||
final ListHandler listHandler = new ListHandler();
|
||||
|
||||
return builder()
|
||||
.allowNonClosedTags(allowNonClosedTags)
|
||||
.handler("i", emphasisHandler)
|
||||
.handler("em", emphasisHandler)
|
||||
.handler("cite", emphasisHandler)
|
||||
.handler("dfn", emphasisHandler)
|
||||
.handler("b", strongEmphasisHandler)
|
||||
.handler("strong", strongEmphasisHandler)
|
||||
.handler("sup", new SuperScriptHandler())
|
||||
.handler("sub", new SubScriptHandler())
|
||||
.handler("u", underlineHandler)
|
||||
.handler("ins", underlineHandler)
|
||||
.handler("del", strikeHandler)
|
||||
.handler("s", strikeHandler)
|
||||
.handler("strike", strikeHandler)
|
||||
.handler("a", new LinkHandler())
|
||||
.handler("ul", listHandler)
|
||||
.handler("ol", listHandler)
|
||||
.handler("img", ImageHandler.create())
|
||||
.handler("blockquote", new BlockquoteHandler())
|
||||
.handler("h1", new HeadingHandler(1))
|
||||
.handler("h2", new HeadingHandler(2))
|
||||
.handler("h3", new HeadingHandler(3))
|
||||
.handler("h4", new HeadingHandler(4))
|
||||
.handler("h5", new HeadingHandler(5))
|
||||
.handler("h6", new HeadingHandler(6));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
|
||||
|
||||
private final boolean allowNonClosedTags;
|
||||
private final Map<String, TagHandler> tagHandlers;
|
||||
|
||||
private MarkwonHtmlRendererImpl(boolean allowNonClosedTags, @NonNull Map<String, TagHandler> tagHandlers) {
|
||||
this.allowNonClosedTags = allowNonClosedTags;
|
||||
this.tagHandlers = tagHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
@NonNull final MarkwonVisitor visitor,
|
||||
@NonNull MarkwonHtmlParser parser) {
|
||||
|
||||
final int end;
|
||||
if (!allowNonClosedTags) {
|
||||
end = HtmlTag.NO_END;
|
||||
} else {
|
||||
end = visitor.length();
|
||||
}
|
||||
|
||||
parser.flushInlineTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
|
||||
@Override
|
||||
public void apply(@NonNull List<HtmlTag.Inline> tags) {
|
||||
|
||||
TagHandler handler;
|
||||
|
||||
for (HtmlTag.Inline inline : tags) {
|
||||
|
||||
// if tag is not closed -> do not render
|
||||
if (!inline.isClosed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = tagHandler(inline.name());
|
||||
if (handler != null) {
|
||||
handler.handle(visitor, MarkwonHtmlRendererImpl.this, inline);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parser.flushBlockTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
|
||||
@Override
|
||||
public void apply(@NonNull List<HtmlTag.Block> tags) {
|
||||
|
||||
TagHandler handler;
|
||||
|
||||
for (HtmlTag.Block block : tags) {
|
||||
|
||||
if (!block.isClosed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = tagHandler(block.name());
|
||||
if (handler != null) {
|
||||
handler.handle(visitor, MarkwonHtmlRendererImpl.this, block);
|
||||
} else {
|
||||
// see if any of children can be handled
|
||||
apply(block.children());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parser.reset();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagHandler tagHandler(@NonNull String tagName) {
|
||||
return tagHandlers.get(tagName);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final Map<String, TagHandler> tagHandlers = new HashMap<>(2);
|
||||
private boolean allowNonClosedTags;
|
||||
|
||||
@NonNull
|
||||
public Builder handler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
|
||||
tagHandlers.put(tagName.toLowerCase(Locale.US), tagHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowNonClosedTags that indicates if non-closed html tags should be rendered.
|
||||
* If this argument is true then all non-closed HTML tags
|
||||
* will be closed at the end of a document. Otherwise they will
|
||||
* be delivered non-closed {@code HtmlTag#isClosed()} and thus not
|
||||
* rendered at all
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@NonNull
|
||||
public Builder allowNonClosedTags(boolean allowNonClosedTags) {
|
||||
this.allowNonClosedTags = allowNonClosedTags;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MarkwonHtmlRendererImpl build() {
|
||||
return new MarkwonHtmlRendererImpl(allowNonClosedTags, Collections.unmodifiableMap(tagHandlers));
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
|
||||
import ru.noties.markwon.html.MarkwonHtmlRendererImpl;
|
||||
import ru.noties.markwon.html.HtmlPlugin;
|
||||
|
||||
public class SubScriptSpan extends MetricAffectingSpan {
|
||||
|
||||
@ -19,7 +19,7 @@ public class SubScriptSpan extends MetricAffectingSpan {
|
||||
}
|
||||
|
||||
private void apply(TextPaint paint) {
|
||||
paint.setTextSize(paint.getTextSize() * MarkwonHtmlRendererImpl.SCRIPT_DEF_TEXT_SIZE_RATIO);
|
||||
paint.setTextSize(paint.getTextSize() * HtmlPlugin.SCRIPT_DEF_TEXT_SIZE_RATIO);
|
||||
paint.baselineShift -= (int) (paint.ascent() / 2);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
|
||||
import ru.noties.markwon.html.MarkwonHtmlRendererImpl;
|
||||
import ru.noties.markwon.html.HtmlPlugin;
|
||||
|
||||
public class SuperScriptSpan extends MetricAffectingSpan {
|
||||
|
||||
@ -19,7 +19,7 @@ public class SuperScriptSpan extends MetricAffectingSpan {
|
||||
}
|
||||
|
||||
private void apply(TextPaint paint) {
|
||||
paint.setTextSize(paint.getTextSize() * MarkwonHtmlRendererImpl.SCRIPT_DEF_TEXT_SIZE_RATIO);
|
||||
paint.setTextSize(paint.getTextSize() * HtmlPlugin.SCRIPT_DEF_TEXT_SIZE_RATIO);
|
||||
paint.baselineShift += (int) (paint.ascent() / 2);
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,6 @@ import ru.noties.markwon.html.HtmlTag;
|
||||
|
||||
public class HeadingHandler extends SimpleTagHandler {
|
||||
|
||||
private final int level;
|
||||
|
||||
public HeadingHandler(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getSpans(
|
||||
@ -31,6 +25,18 @@ public class HeadingHandler extends SimpleTagHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
int level;
|
||||
try {
|
||||
level = Integer.parseInt(tag.name().substring(1));
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
level = 0;
|
||||
}
|
||||
|
||||
if (level < 1 || level > 6) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CoreProps.HEADING_LEVEL.set(renderProps, level);
|
||||
|
||||
return factory.getSpans(configuration, renderProps);
|
||||
|
@ -9,6 +9,7 @@ import org.commonmark.parser.Parser;
|
||||
|
||||
import ru.noties.markwon.core.CorePlugin;
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.priority.Priority;
|
||||
|
||||
@ -69,6 +70,14 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
|
||||
import ru.noties.markwon.core.CorePlugin;
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.priority.PriorityProcessor;
|
||||
|
||||
@ -102,7 +103,7 @@ class MarkwonBuilderImpl implements Markwon.Builder {
|
||||
final MarkwonConfiguration.Builder configurationBuilder = new MarkwonConfiguration.Builder();
|
||||
final MarkwonVisitor.Builder visitorBuilder = new MarkwonVisitorImpl.BuilderImpl();
|
||||
final MarkwonSpansFactory.Builder spanFactoryBuilder = new MarkwonSpansFactoryImpl.BuilderImpl();
|
||||
final RenderProps renderProps = new RenderPropsImpl();
|
||||
final MarkwonHtmlRenderer.Builder htmlRendererBuilder = MarkwonHtmlRenderer.builder();
|
||||
|
||||
for (MarkwonPlugin plugin : plugins) {
|
||||
plugin.configureParser(parserBuilder);
|
||||
@ -111,13 +112,17 @@ class MarkwonBuilderImpl implements Markwon.Builder {
|
||||
plugin.configureConfiguration(configurationBuilder);
|
||||
plugin.configureVisitor(visitorBuilder);
|
||||
plugin.configureSpansFactory(spanFactoryBuilder);
|
||||
plugin.configureHtmlRenderer(htmlRendererBuilder);
|
||||
}
|
||||
|
||||
final MarkwonConfiguration configuration = configurationBuilder.build(
|
||||
themeBuilder.build(),
|
||||
asyncDrawableLoaderBuilder.build(),
|
||||
htmlRendererBuilder.build(),
|
||||
spanFactoryBuilder.build());
|
||||
|
||||
final RenderProps renderProps = new RenderPropsImpl();
|
||||
|
||||
return new MarkwonImpl(
|
||||
bufferType,
|
||||
parserBuilder.build(),
|
||||
|
@ -4,6 +4,8 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.core.spans.LinkSpan;
|
||||
import ru.noties.markwon.html.MarkwonHtmlParser;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.image.ImageSizeResolver;
|
||||
import ru.noties.markwon.image.ImageSizeResolverDef;
|
||||
@ -29,6 +31,8 @@ public class MarkwonConfiguration {
|
||||
private final LinkSpan.Resolver linkResolver;
|
||||
private final UrlProcessor urlProcessor;
|
||||
private final ImageSizeResolver imageSizeResolver;
|
||||
private final MarkwonHtmlParser htmlParser;
|
||||
private final MarkwonHtmlRenderer htmlRenderer;
|
||||
|
||||
// @since 3.0.0
|
||||
private final MarkwonSpansFactory spansFactory;
|
||||
@ -41,6 +45,8 @@ public class MarkwonConfiguration {
|
||||
this.urlProcessor = builder.urlProcessor;
|
||||
this.imageSizeResolver = builder.imageSizeResolver;
|
||||
this.spansFactory = builder.spansFactory;
|
||||
this.htmlParser = builder.htmlParser;
|
||||
this.htmlRenderer = builder.htmlRenderer;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -73,6 +79,16 @@ public class MarkwonConfiguration {
|
||||
return imageSizeResolver;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MarkwonHtmlParser htmlParser() {
|
||||
return htmlParser;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MarkwonHtmlRenderer htmlRenderer() {
|
||||
return htmlRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ -90,6 +106,8 @@ public class MarkwonConfiguration {
|
||||
private LinkSpan.Resolver linkResolver;
|
||||
private UrlProcessor urlProcessor;
|
||||
private ImageSizeResolver imageSizeResolver;
|
||||
private MarkwonHtmlParser htmlParser;
|
||||
private MarkwonHtmlRenderer htmlRenderer;
|
||||
private MarkwonSpansFactory spansFactory;
|
||||
|
||||
Builder() {
|
||||
@ -113,6 +131,12 @@ public class MarkwonConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder htmlParser(@NonNull MarkwonHtmlParser htmlParser) {
|
||||
this.htmlParser = htmlParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.0.1
|
||||
*/
|
||||
@ -126,10 +150,12 @@ public class MarkwonConfiguration {
|
||||
public MarkwonConfiguration build(
|
||||
@NonNull MarkwonTheme theme,
|
||||
@NonNull AsyncDrawableLoader asyncDrawableLoader,
|
||||
@NonNull MarkwonHtmlRenderer htmlRenderer,
|
||||
@NonNull MarkwonSpansFactory spansFactory) {
|
||||
|
||||
this.theme = theme;
|
||||
this.asyncDrawableLoader = asyncDrawableLoader;
|
||||
this.htmlRenderer = htmlRenderer;
|
||||
this.spansFactory = spansFactory;
|
||||
|
||||
if (syntaxHighlight == null) {
|
||||
@ -148,6 +174,10 @@ public class MarkwonConfiguration {
|
||||
imageSizeResolver = new ImageSizeResolverDef();
|
||||
}
|
||||
|
||||
if (htmlParser == null) {
|
||||
htmlParser = MarkwonHtmlParser.noOp();
|
||||
}
|
||||
|
||||
return new MarkwonConfiguration(this);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.image.MediaDecoder;
|
||||
import ru.noties.markwon.image.SchemeHandler;
|
||||
@ -73,7 +74,13 @@ public interface MarkwonPlugin {
|
||||
*/
|
||||
void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder);
|
||||
|
||||
// can be used to configure own properties and use between plugins
|
||||
/**
|
||||
* Configure {@link MarkwonHtmlRenderer} to add or remove HTML {@link ru.noties.markwon.html.TagHandler}s
|
||||
*
|
||||
* @see MarkwonHtmlRenderer
|
||||
* @see MarkwonHtmlRenderer.Builder
|
||||
*/
|
||||
void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder);
|
||||
|
||||
/**
|
||||
* A method to store some arbitrary data in {@link RenderProps}. Although it won\'t make
|
||||
|
@ -4,15 +4,10 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @see MarkwonHtmlParser
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class MarkwonHtmlParserNoOp extends MarkwonHtmlParser {
|
||||
|
||||
@Override
|
||||
public <T extends Appendable & CharSequence> void processFragment(@NonNull T output, @NonNull String htmlFragment) {
|
||||
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -27,6 +22,6 @@ class MarkwonHtmlParserNoOp extends MarkwonHtmlParser {
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
||||
// no op
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package ru.noties.markwon.html;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class MarkwonHtmlRenderer {
|
||||
|
||||
@NonNull
|
||||
public static Builder builder() {
|
||||
return new MarkwonHtmlRendererImpl.BuilderImpl();
|
||||
}
|
||||
|
||||
public abstract void render(
|
||||
@NonNull MarkwonVisitor visitor,
|
||||
@NonNull MarkwonHtmlParser parser
|
||||
);
|
||||
|
||||
@Nullable
|
||||
public abstract TagHandler tagHandler(@NonNull String tagName);
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* @param allowNonClosedTags parameter to indicate that all non-closed HTML tags should be
|
||||
* closed at the end of a document. if {@code true} all non-closed
|
||||
* tags will be force-closed at the end. Otherwise these tags will be
|
||||
* ignored and thus not rendered.
|
||||
* @return self
|
||||
*/
|
||||
@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);
|
||||
|
||||
@NonNull
|
||||
Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames);
|
||||
|
||||
@NonNull
|
||||
Builder removeHandler(@NonNull String tagName);
|
||||
|
||||
@NonNull
|
||||
Builder removeHandlers(@NonNull String... tagNames);
|
||||
|
||||
@NonNull
|
||||
MarkwonHtmlRenderer build();
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package ru.noties.markwon.html;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
|
||||
class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
|
||||
private final boolean allowNonClosedTags;
|
||||
private final Map<String, TagHandler> tagHandlers;
|
||||
|
||||
MarkwonHtmlRendererImpl(boolean allowNonClosedTags, @NonNull Map<String, TagHandler> tagHandlers) {
|
||||
this.allowNonClosedTags = allowNonClosedTags;
|
||||
this.tagHandlers = tagHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
@NonNull final MarkwonVisitor visitor,
|
||||
@NonNull MarkwonHtmlParser parser) {
|
||||
|
||||
final int end;
|
||||
if (!allowNonClosedTags) {
|
||||
end = HtmlTag.NO_END;
|
||||
} else {
|
||||
end = visitor.length();
|
||||
}
|
||||
|
||||
parser.flushInlineTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
|
||||
@Override
|
||||
public void apply(@NonNull List<HtmlTag.Inline> tags) {
|
||||
|
||||
TagHandler handler;
|
||||
|
||||
for (HtmlTag.Inline inline : tags) {
|
||||
|
||||
// if tag is not closed -> do not render
|
||||
if (!inline.isClosed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = tagHandler(inline.name());
|
||||
if (handler != null) {
|
||||
handler.handle(visitor, MarkwonHtmlRendererImpl.this, inline);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parser.flushBlockTags(end, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
|
||||
@Override
|
||||
public void apply(@NonNull List<HtmlTag.Block> tags) {
|
||||
|
||||
TagHandler handler;
|
||||
|
||||
for (HtmlTag.Block block : tags) {
|
||||
|
||||
if (!block.isClosed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = tagHandler(block.name());
|
||||
if (handler != null) {
|
||||
handler.handle(visitor, MarkwonHtmlRendererImpl.this, block);
|
||||
} else {
|
||||
// see if any of children can be handled
|
||||
apply(block.children());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parser.reset();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagHandler tagHandler(@NonNull String tagName) {
|
||||
return tagHandlers.get(tagName);
|
||||
}
|
||||
|
||||
static class BuilderImpl implements Builder {
|
||||
|
||||
private final Map<String, TagHandler> tagHandlers = new HashMap<>(2);
|
||||
private boolean allowNonClosedTags;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder allowNonClosedTags(boolean allowNonClosedTags) {
|
||||
this.allowNonClosedTags = allowNonClosedTags;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder addHandler(@NonNull TagHandler tagHandler, @NonNull String tagName) {
|
||||
tagHandlers.put(tagName, tagHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder addHandler(@NonNull TagHandler tagHandler, String... tagNames) {
|
||||
for (String tagName : tagNames) {
|
||||
if (tagName != null) {
|
||||
tagHandlers.put(tagName, tagHandler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder removeHandler(@NonNull String tagName) {
|
||||
tagHandlers.remove(tagName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder removeHandlers(@NonNull String... tagNames) {
|
||||
for (String tagName : tagNames) {
|
||||
if (tagName != null) {
|
||||
tagHandlers.remove(tagName);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MarkwonHtmlRenderer build() {
|
||||
// okay, let's validate that we have at least one tagHandler registered
|
||||
// if we have none -> return no-op implementation
|
||||
return tagHandlers.size() > 0
|
||||
? new MarkwonHtmlRendererImpl(allowNonClosedTags, Collections.unmodifiableMap(tagHandlers))
|
||||
: new MarkwonHtmlRendererNoOp();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.noties.markwon.html;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
|
||||
class MarkwonHtmlRendererNoOp extends MarkwonHtmlRenderer {
|
||||
|
||||
@Override
|
||||
public void render(@NonNull MarkwonVisitor visitor, @NonNull MarkwonHtmlParser parser) {
|
||||
parser.reset();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TagHandler tagHandler(@NonNull String tagName) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import ru.noties.markwon.core.CorePlugin;
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
import ru.noties.markwon.priority.Priority;
|
||||
import ru.noties.markwon.priority.PriorityProcessor;
|
||||
@ -216,6 +217,7 @@ public class MarkwonBuilderImplTest {
|
||||
verify(plugin, times(1)).configureConfiguration(any(MarkwonConfiguration.Builder.class));
|
||||
verify(plugin, times(1)).configureVisitor(any(MarkwonVisitor.Builder.class));
|
||||
verify(plugin, times(1)).configureSpansFactory(any(MarkwonSpansFactory.Builder.class));
|
||||
verify(plugin, times(1)).configureHtmlRenderer(any(MarkwonHtmlRenderer.Builder.class));
|
||||
|
||||
// we do not know how many times exactly, but at least once it must be called
|
||||
verify(plugin, atLeast(1)).priority();
|
||||
|
@ -26,6 +26,7 @@ import ru.noties.markwon.SpanFactory;
|
||||
import ru.noties.markwon.SpannableBuilder;
|
||||
import ru.noties.markwon.core.CorePluginBridge;
|
||||
import ru.noties.markwon.core.MarkwonTheme;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
import ru.noties.markwon.image.AsyncDrawableLoader;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@ -83,7 +84,7 @@ public class SyntaxHighlightTest {
|
||||
|
||||
final MarkwonConfiguration configuration = MarkwonConfiguration.builder()
|
||||
.syntaxHighlight(highlight)
|
||||
.build(mock(MarkwonTheme.class), mock(AsyncDrawableLoader.class), spansFactory);
|
||||
.build(mock(MarkwonTheme.class), mock(AsyncDrawableLoader.class), mock(MarkwonHtmlRenderer.class), spansFactory);
|
||||
|
||||
final Map<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>> visitorMap = Collections.emptyMap();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user