Improve TagHandler to allow direct set of spans

This commit is contained in:
Dimitry Ivanov 2018-08-19 11:36:31 +03:00
parent 019ed61e69
commit 23b95e70b9
9 changed files with 115 additions and 93 deletions

View File

@ -145,11 +145,11 @@ Please refer to [SpannableConfiguration] document for more info
## Syntax highlight ## 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: It is based on [Prism4j](https://github.com/noties/Prism4j) project. It contains 2 builtin themes:
`Default` (light, `Prism4jThemeDefault`) and `Darkula` (dark, `Prism4jThemeDarkula`). `Default` (light, `Prism4jThemeDefault`) and `Darkula` (dark, `Prism4jThemeDarkula`).
[library-syntax](./library-syntax/) [markwon-syntax-highlight](./markwon-syntax-highlight/)
--- ---

View File

@ -56,7 +56,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
private final SpannableConfiguration configuration; private final SpannableConfiguration configuration;
private final SpannableBuilder builder; private final SpannableBuilder builder;
private final MarkwonHtmlParser htmlParser; private final MarkwonHtmlParser htmlParser;
// private final Deque<HtmlInlineItem> htmlInlineItems;
private final SpannableTheme theme; private final SpannableTheme theme;
private final SpannableFactory factory; private final SpannableFactory factory;
@ -75,7 +74,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
this.configuration = configuration; this.configuration = configuration;
this.builder = builder; this.builder = builder;
this.htmlParser = configuration.htmlParser(); this.htmlParser = configuration.htmlParser();
// this.htmlInlineItems = new ArrayDeque<>(2);
this.theme = configuration.theme(); this.theme = configuration.theme();
this.factory = configuration.factory(); this.factory = configuration.factory();
@ -450,54 +448,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
@Override @Override
public void visit(HtmlBlock htmlBlock) { 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()); visitHtml(htmlBlock.getLiteral());
} }
@Override @Override
public void visit(HtmlInline htmlInline) { public void visit(HtmlInline htmlInline) {
visitHtml(htmlInline.getLiteral()); 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) { private void visitHtml(@Nullable String html) {
@ -568,15 +524,4 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
} }
return out; 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;
// }
// }
} }

View File

@ -12,6 +12,7 @@ import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.MarkwonHtmlParser; import ru.noties.markwon.html.api.MarkwonHtmlParser;
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler; 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.LinkHandler;
import ru.noties.markwon.renderer.html2.tag.ListHandler; import ru.noties.markwon.renderer.html2.tag.ListHandler;
import ru.noties.markwon.renderer.html2.tag.StrikeHandler; import ru.noties.markwon.renderer.html2.tag.StrikeHandler;
@ -46,6 +47,7 @@ public abstract class MarkwonHtmlRenderer {
final EmphasisHandler emphasisHandler = new EmphasisHandler(); final EmphasisHandler emphasisHandler = new EmphasisHandler();
final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler(); final StrongEmphasisHandler strongEmphasisHandler = new StrongEmphasisHandler();
final StrikeHandler strikeHandler = new StrikeHandler(); final StrikeHandler strikeHandler = new StrikeHandler();
final UnderlineHandler underlineHandler = new UnderlineHandler();
final ListHandler listHandler = new ListHandler(); final ListHandler listHandler = new ListHandler();
return builder() return builder()
@ -57,13 +59,15 @@ public abstract class MarkwonHtmlRenderer {
.handler("strong", strongEmphasisHandler) .handler("strong", strongEmphasisHandler)
.handler("sup", new SuperScriptHandler()) .handler("sup", new SuperScriptHandler())
.handler("sub", new SubScriptHandler()) .handler("sub", new SubScriptHandler())
.handler("u", new UnderlineHandler()) .handler("u", underlineHandler)
.handler("ins", underlineHandler)
.handler("del", strikeHandler) .handler("del", strikeHandler)
.handler("s", strikeHandler) .handler("s", strikeHandler)
.handler("strike", strikeHandler) .handler("strike", strikeHandler)
.handler("a", new LinkHandler()) .handler("a", new LinkHandler())
.handler("ul", listHandler) .handler("ul", listHandler)
.handler("ol", listHandler); .handler("ol", listHandler)
.handler("img", new ImageHandler());
} }
@NonNull @NonNull
@ -82,9 +86,7 @@ public abstract class MarkwonHtmlRenderer {
@NonNull @NonNull
public MarkwonHtmlRenderer build() { public MarkwonHtmlRenderer build() {
return new MarkwonHtmlRendererImpl( return new MarkwonHtmlRendererImpl(Collections.unmodifiableMap(tagHandlers));
Collections.unmodifiableMap(tagHandlers)
);
} }
} }
} }

View File

@ -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<String, String> 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<String, String> 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;
}
}

View File

@ -6,7 +6,7 @@ import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.HtmlTag;
public class ListHandler implements TagHandler { public class ListHandler extends TagHandler {
@Override @Override
public void handle( 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) { private static int currentBulletListLevel(@NonNull HtmlTag.Block block) {
int level = 0; int level = 0;
while ((block = block.parent()) != null) { while ((block = block.parent()) != null) {

View File

@ -7,7 +7,7 @@ import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.HtmlTag;
public abstract class SimpleTagHandler implements TagHandler { public abstract class SimpleTagHandler extends TagHandler {
@Nullable @Nullable
public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag); public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag);

View File

@ -1,15 +1,28 @@
package ru.noties.markwon.renderer.html2.tag; package ru.noties.markwon.renderer.html2.tag;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.HtmlTag;
public class StrikeHandler extends SimpleTagHandler { public class StrikeHandler extends TagHandler {
@Nullable
@Override @Override
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { public void handle(
return configuration.factory().strikethrough(); @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()
);
} }
} }

View File

@ -6,11 +6,28 @@ import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.HtmlTag;
public interface TagHandler { public abstract class TagHandler {
void handle( public abstract void handle(
@NonNull SpannableConfiguration configuration, @NonNull SpannableConfiguration configuration,
@NonNull SpannableBuilder builder, @NonNull SpannableBuilder builder,
@NonNull HtmlTag tag @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);
}
}
}
} }

View File

@ -1,15 +1,31 @@
package ru.noties.markwon.renderer.html2.tag; package ru.noties.markwon.renderer.html2.tag;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.HtmlTag;
public class UnderlineHandler extends SimpleTagHandler { public class UnderlineHandler extends TagHandler {
@Nullable
@Override @Override
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { public void handle(
return configuration.factory().underline(); @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()
);
} }
} }