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
	 Dimitry Ivanov
						Dimitry Ivanov