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
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/)
---

View File

@ -56,7 +56,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
private final SpannableConfiguration configuration;
private final SpannableBuilder builder;
private final MarkwonHtmlParser htmlParser;
// private final Deque<HtmlInlineItem> 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;
// }
// }
}

View File

@ -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));
}
}
}

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.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) {

View File

@ -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);

View File

@ -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()
);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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()
);
}
}