From 23b95e70b9d28dc98ec5cee9a7ab863da0dc1ba4 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Sun, 19 Aug 2018 11:36:31 +0300 Subject: [PATCH] Improve TagHandler to allow direct set of spans --- README.md | 4 +- .../renderer/SpannableMarkdownVisitor.java | 55 ------------------- .../renderer/html2/MarkwonHtmlRenderer.java | 12 ++-- .../renderer/html2/tag/ImageHandler.java | 46 ++++++++++++++++ .../renderer/html2/tag/ListHandler.java | 19 +------ .../renderer/html2/tag/SimpleTagHandler.java | 2 +- .../renderer/html2/tag/StrikeHandler.java | 23 ++++++-- .../renderer/html2/tag/TagHandler.java | 21 ++++++- .../renderer/html2/tag/UnderlineHandler.java | 26 +++++++-- 9 files changed, 115 insertions(+), 93 deletions(-) create mode 100644 markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ImageHandler.java diff --git a/README.md b/README.md index c9745960..7136cc62 100644 --- a/README.md +++ b/README.md @@ -145,11 +145,11 @@ Please refer to [SpannableConfiguration] document for more info ## Syntax highlight -Starting with version `1.1.0` there is an artifact (`markwon-syntax`) that allows you to have syntax highlight functionality. +Starting with version `1.1.0` there is an artifact (`markwon-syntax-highlight`) that allows you to have syntax highlight functionality. It is based on [Prism4j](https://github.com/noties/Prism4j) project. It contains 2 builtin themes: `Default` (light, `Prism4jThemeDefault`) and `Darkula` (dark, `Prism4jThemeDarkula`). -[library-syntax](./library-syntax/) +[markwon-syntax-highlight](./markwon-syntax-highlight/) --- diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java index 60f3ae37..ed8f7a86 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java @@ -56,7 +56,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { private final SpannableConfiguration configuration; private final SpannableBuilder builder; private final MarkwonHtmlParser htmlParser; -// private final Deque htmlInlineItems; private final SpannableTheme theme; private final SpannableFactory factory; @@ -75,7 +74,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { this.configuration = configuration; this.builder = builder; this.htmlParser = configuration.htmlParser(); -// this.htmlInlineItems = new ArrayDeque<>(2); this.theme = configuration.theme(); this.factory = configuration.factory(); @@ -450,54 +448,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { @Override public void visit(HtmlBlock htmlBlock) { -// // http://spec.commonmark.org/0.18/#html-blocks -// final Spanned spanned = configuration.htmlParser().getSpanned(null, htmlBlock.getLiteral()); -// if (!TextUtils.isEmpty(spanned)) { -// builder.append(spanned); -// } -// htmlParser.processFragment(builder, htmlBlock.getLiteral()); visitHtml(htmlBlock.getLiteral()); } @Override public void visit(HtmlInline htmlInline) { - visitHtml(htmlInline.getLiteral()); - -// htmlParser.processFragment(builder, htmlInline.getLiteral()); - -// final SpannableHtmlParser htmlParser = configuration.htmlParser(); -// final SpannableHtmlParser.Tag tag = htmlParser.parseTag(htmlInline.getLiteral()); -// -// if (tag != null) { -// -// final boolean voidTag = tag.voidTag(); -// if (!voidTag && tag.opening()) { -// // push in stack -// htmlInlineItems.push(new HtmlInlineItem(tag, builder.length())); -// visitChildren(htmlInline); -// } else { -// -// if (!voidTag) { -// if (htmlInlineItems.size() > 0) { -// final HtmlInlineItem item = htmlInlineItems.pop(); -// final Object span = htmlParser.getSpanForTag(item.tag); -// setSpan(item.start, span); -// } -// } else { -// -// final Spanned html = htmlParser.getSpanned(tag, htmlInline.getLiteral()); -// if (!TextUtils.isEmpty(html)) { -// builder.append(html); -// } -// -// } -// } -// } else { -// // todo, should we append just literal? -//// builder.append(htmlInline.getLiteral()); -// visitChildren(htmlInline); -// } } private void visitHtml(@Nullable String html) { @@ -568,15 +524,4 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { } return out; } - -// private static class HtmlInlineItem { -// -// final SpannableHtmlParser.Tag tag; -// final int start; -// -// HtmlInlineItem(SpannableHtmlParser.Tag tag, int start) { -// this.tag = tag; -// this.start = start; -// } -// } } diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRenderer.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRenderer.java index d2efe0d4..5910eb5a 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRenderer.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRenderer.java @@ -12,6 +12,7 @@ import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.MarkwonHtmlParser; import ru.noties.markwon.renderer.html2.tag.EmphasisHandler; +import ru.noties.markwon.renderer.html2.tag.ImageHandler; import ru.noties.markwon.renderer.html2.tag.LinkHandler; import ru.noties.markwon.renderer.html2.tag.ListHandler; import ru.noties.markwon.renderer.html2.tag.StrikeHandler; @@ -46,6 +47,7 @@ public abstract class MarkwonHtmlRenderer { 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() @@ -57,13 +59,15 @@ public abstract class MarkwonHtmlRenderer { .handler("strong", strongEmphasisHandler) .handler("sup", new SuperScriptHandler()) .handler("sub", new SubScriptHandler()) - .handler("u", new UnderlineHandler()) + .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("ol", listHandler) + .handler("img", new ImageHandler()); } @NonNull @@ -82,9 +86,7 @@ public abstract class MarkwonHtmlRenderer { @NonNull public MarkwonHtmlRenderer build() { - return new MarkwonHtmlRendererImpl( - Collections.unmodifiableMap(tagHandlers) - ); + return new MarkwonHtmlRendererImpl(Collections.unmodifiableMap(tagHandlers)); } } } diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ImageHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ImageHandler.java new file mode 100644 index 00000000..6a65de7b --- /dev/null +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ImageHandler.java @@ -0,0 +1,46 @@ +package ru.noties.markwon.renderer.html2.tag; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import java.util.Map; + +import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.html.api.HtmlTag; +import ru.noties.markwon.renderer.ImageSize; + +public class ImageHandler extends SimpleTagHandler { + + @Nullable + @Override + public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { + + final Map attributes = tag.attributes(); + final String src = attributes.get("src"); + if (TextUtils.isEmpty(src)) { + return null; + } + + final String destination = configuration.urlProcessor().process(src); + + // todo: replacement text is link... as we are not at block level + // and cannot inspect the parent of this node... (img and a are both inlines) + + return configuration.factory().image( + configuration.theme(), + destination, + configuration.asyncDrawableLoader(), + configuration.imageSizeResolver(), + parseImageSize(attributes), + false + ); + } + + @Nullable + private static ImageSize parseImageSize(@NonNull Map attributes) { + // strictly speaking percents when specified directly on an attribute + // are not part of the HTML spec (I couldn't find any reference) + return null; + } +} diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ListHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ListHandler.java index f7ddccd8..fca098e7 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ListHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/ListHandler.java @@ -6,7 +6,7 @@ import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; -public class ListHandler implements TagHandler { +public class ListHandler extends TagHandler { @Override public void handle( @@ -53,23 +53,6 @@ public class ListHandler implements TagHandler { } } - private void visitChildren( - @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, - @NonNull HtmlTag.Block block) { - - TagHandler handler; - - for (HtmlTag.Block child : block.children()) { - handler = configuration.htmlRenderer().tagHandler(child.name()); - if (handler != null) { - handler.handle(configuration, builder, child); - } else { - visitChildren(configuration, builder, child); - } - } - } - private static int currentBulletListLevel(@NonNull HtmlTag.Block block) { int level = 0; while ((block = block.parent()) != null) { diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/SimpleTagHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/SimpleTagHandler.java index b29bfe78..e5940cc7 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/SimpleTagHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/SimpleTagHandler.java @@ -7,7 +7,7 @@ import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; -public abstract class SimpleTagHandler implements TagHandler { +public abstract class SimpleTagHandler extends TagHandler { @Nullable public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag); diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/StrikeHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/StrikeHandler.java index ef8d0a71..965ddfea 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/StrikeHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/StrikeHandler.java @@ -1,15 +1,28 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; -public class StrikeHandler extends SimpleTagHandler { - @Nullable +public class StrikeHandler extends TagHandler { + @Override - public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { - return configuration.factory().strikethrough(); + public void handle( + @NonNull SpannableConfiguration configuration, + @NonNull SpannableBuilder builder, + @NonNull HtmlTag tag) { + + if (tag.isBlock()) { + visitChildren(configuration, builder, tag.getAsBlock()); + } + + SpannableBuilder.setSpans( + builder, + configuration.factory().strikethrough(), + tag.start(), + tag.end() + ); } } diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/TagHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/TagHandler.java index 39bdfc2f..dabc719d 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/TagHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/TagHandler.java @@ -6,11 +6,28 @@ import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; -public interface TagHandler { +public abstract class TagHandler { - void handle( + public abstract void handle( @NonNull SpannableConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull HtmlTag tag ); + + protected static void visitChildren( + @NonNull SpannableConfiguration configuration, + @NonNull SpannableBuilder builder, + @NonNull HtmlTag.Block block) { + + TagHandler handler; + + for (HtmlTag.Block child : block.children()) { + handler = configuration.htmlRenderer().tagHandler(child.name()); + if (handler != null) { + handler.handle(configuration, builder, child); + } else { + visitChildren(configuration, builder, child); + } + } + } } diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/UnderlineHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/UnderlineHandler.java index ee4bff01..ff870ef6 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/UnderlineHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/UnderlineHandler.java @@ -1,15 +1,31 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; -public class UnderlineHandler extends SimpleTagHandler { - @Nullable +public class UnderlineHandler extends TagHandler { + @Override - public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { - return configuration.factory().underline(); + public void handle( + @NonNull SpannableConfiguration configuration, + @NonNull SpannableBuilder builder, + @NonNull HtmlTag tag) { + + // as parser doesn't treat U tag as an inline one, + // thus doesn't allow children, we must visit them first + + if (tag.isBlock()) { + visitChildren(configuration, builder, tag.getAsBlock()); + } + + SpannableBuilder.setSpans( + builder, + configuration.factory().underline(), + tag.start(), + tag.end() + ); } }