Create HtmlPlugin
This commit is contained in:
parent
27ed17aaff
commit
66bb33a76b
app
markwon-html/src/main/java/ru/noties/markwon/html/impl
markwon/src/main/java/ru/noties/markwon
@ -29,6 +29,7 @@ android {
|
||||
dependencies {
|
||||
|
||||
implementation project(':markwon')
|
||||
implementation project(':markwon-html')
|
||||
implementation project(':markwon-image-gif')
|
||||
implementation project(':markwon-image-svg')
|
||||
implementation project(':markwon-syntax-highlight')
|
||||
|
@ -9,6 +9,7 @@ import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import pl.droidsonroids.gif.GifDrawable;
|
||||
import ru.noties.debug.Debug;
|
||||
import ru.noties.markwon.spans.AsyncDrawableSpan;
|
||||
|
||||
public abstract class GifProcessor {
|
||||
@ -25,16 +26,21 @@ public abstract class GifProcessor {
|
||||
@Override
|
||||
public void process(@NonNull final TextView textView) {
|
||||
|
||||
Debug.i("textView: %s", textView);
|
||||
|
||||
// here is what we will do additionally:
|
||||
// we query for all asyncDrawableSpans
|
||||
// we check if they are inside clickableSpan
|
||||
// if not we apply onGifListener
|
||||
|
||||
final Spannable spannable = spannable(textView);
|
||||
Debug.i(spannable);
|
||||
if (spannable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.i(spannable);
|
||||
|
||||
final AsyncDrawableSpan[] asyncDrawableSpans =
|
||||
spannable.getSpans(0, spannable.length(), AsyncDrawableSpan.class);
|
||||
if (asyncDrawableSpans == null
|
||||
@ -42,6 +48,8 @@ public abstract class GifProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.i(asyncDrawableSpans);
|
||||
|
||||
int start;
|
||||
int end;
|
||||
ClickableSpan[] clickableSpans;
|
||||
@ -51,6 +59,8 @@ public abstract class GifProcessor {
|
||||
start = spannable.getSpanStart(asyncDrawableSpan);
|
||||
end = spannable.getSpanEnd(asyncDrawableSpan);
|
||||
|
||||
Debug.i(asyncDrawableSpan, start, end);
|
||||
|
||||
if (start < 0
|
||||
|| end < 0) {
|
||||
continue;
|
||||
@ -74,6 +84,7 @@ public abstract class GifProcessor {
|
||||
@Nullable
|
||||
private static Spannable spannable(@NonNull TextView textView) {
|
||||
final CharSequence charSequence = textView.getText();
|
||||
Debug.i("type: %s, spanned: %s, spannable: %s", charSequence.getClass().getName(), charSequence instanceof Spanned, charSequence instanceof Spannable);
|
||||
if (charSequence instanceof Spannable) {
|
||||
return (Spannable) charSequence;
|
||||
}
|
||||
@ -85,6 +96,8 @@ public abstract class GifProcessor {
|
||||
@NonNull AsyncDrawableSpan span,
|
||||
@NonNull GifAwareAsyncDrawable drawable) {
|
||||
|
||||
Debug.i("textView: %s, span: %s, drawable: %s", textView, span, drawable);
|
||||
|
||||
// important thing here is to obtain new spannable from textView
|
||||
// as with each `setText()` new spannable is created and keeping reference
|
||||
// to an older one won't affect textView
|
||||
|
@ -14,6 +14,7 @@ import javax.inject.Inject;
|
||||
|
||||
import ru.noties.debug.Debug;
|
||||
import ru.noties.markwon.core.CorePlugin;
|
||||
import ru.noties.markwon.html.impl.HtmlPlugin;
|
||||
import ru.noties.markwon.image.ImagesPlugin;
|
||||
import ru.noties.markwon.image.gif.GifPlugin;
|
||||
import ru.noties.markwon.image.svg.SvgPlugin;
|
||||
@ -21,6 +22,8 @@ import ru.noties.markwon.syntax.Prism4jTheme;
|
||||
import ru.noties.markwon.syntax.Prism4jThemeDarkula;
|
||||
import ru.noties.markwon.syntax.Prism4jThemeDefault;
|
||||
import ru.noties.markwon.syntax.SyntaxHighlightPlugin;
|
||||
import ru.noties.markwon.table.TablePlugin;
|
||||
import ru.noties.markwon.tasklist.TaskListPlugin;
|
||||
import ru.noties.prism4j.Prism4j;
|
||||
|
||||
@ActivityScope
|
||||
@ -84,6 +87,9 @@ public class MarkdownRenderer {
|
||||
.use(GifPlugin.create(false))
|
||||
.use(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))
|
||||
.use(GifAwarePlugin.create(context))
|
||||
.use(TablePlugin.create(context))
|
||||
.use(TaskListPlugin.create(context))
|
||||
.use(HtmlPlugin.create())
|
||||
.use(new AbstractMarkwonPlugin() {
|
||||
@Override
|
||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||
|
@ -0,0 +1,85 @@
|
||||
package ru.noties.markwon.html.impl;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.commonmark.node.Document;
|
||||
import org.commonmark.node.HtmlBlock;
|
||||
import org.commonmark.node.HtmlInline;
|
||||
|
||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.html.MarkwonHtmlParser;
|
||||
import ru.noties.markwon.html.MarkwonHtmlRenderer;
|
||||
|
||||
public class HtmlPlugin extends AbstractMarkwonPlugin {
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create() {
|
||||
return create(MarkwonHtmlRendererImpl.create(), MarkwonHtmlParserImpl.create());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static HtmlPlugin create(@NonNull MarkwonHtmlRenderer renderer) {
|
||||
return create(renderer, 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 configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||
builder
|
||||
.htmlParser(parser)
|
||||
.htmlRenderer(renderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||
builder
|
||||
.on(Document.class, new MarkwonVisitor.NodeVisitor<Document>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Document document) {
|
||||
|
||||
visitor.visitChildren(document);
|
||||
|
||||
final MarkwonConfiguration configuration = visitor.configuration();
|
||||
configuration.htmlRenderer().render(configuration, visitor.builder(), configuration.htmlParser());
|
||||
}
|
||||
})
|
||||
.on(HtmlBlock.class, new MarkwonVisitor.NodeVisitor<HtmlBlock>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlBlock htmlBlock) {
|
||||
visitHtml(visitor, htmlBlock.getLiteral());
|
||||
}
|
||||
})
|
||||
.on(HtmlInline.class, new MarkwonVisitor.NodeVisitor<HtmlInline>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlInline htmlInline) {
|
||||
visitHtml(visitor, htmlInline.getLiteral());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void visitHtml(@NonNull MarkwonVisitor visitor, @Nullable String html) {
|
||||
if (html != null) {
|
||||
visitor.configuration().htmlParser().processFragment(visitor.builder(), html);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,8 +34,24 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
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();
|
||||
@ -44,6 +60,7 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
final ListHandler listHandler = new ListHandler();
|
||||
|
||||
return builder()
|
||||
.allowNonClosedTags(allowNonClosedTags)
|
||||
.handler("i", emphasisHandler)
|
||||
.handler("em", emphasisHandler)
|
||||
.handler("cite", emphasisHandler)
|
||||
@ -77,9 +94,11 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
|
||||
public static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
|
||||
|
||||
private final boolean allowNonClosedTags;
|
||||
private final Map<String, TagHandler> tagHandlers;
|
||||
|
||||
private MarkwonHtmlRendererImpl(@NonNull Map<String, TagHandler> tagHandlers) {
|
||||
private MarkwonHtmlRendererImpl(boolean allowNonClosedTags, @NonNull Map<String, TagHandler> tagHandlers) {
|
||||
this.allowNonClosedTags = allowNonClosedTags;
|
||||
this.tagHandlers = tagHandlers;
|
||||
}
|
||||
|
||||
@ -90,7 +109,7 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
@NonNull MarkwonHtmlParser parser) {
|
||||
|
||||
final int end;
|
||||
if (!configuration.htmlAllowNonClosedTags()) {
|
||||
if (!allowNonClosedTags) {
|
||||
end = HtmlTag.NO_END;
|
||||
} else {
|
||||
end = builder.length();
|
||||
@ -152,6 +171,7 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
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) {
|
||||
@ -159,9 +179,23 @@ public class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
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(Collections.unmodifiableMap(tagHandlers));
|
||||
return new MarkwonHtmlRendererImpl(allowNonClosedTags, Collections.unmodifiableMap(tagHandlers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class MarkwonConfiguration {
|
||||
private final SpannableFactory factory; // @since 1.1.0
|
||||
private final MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||
private final MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||
private final boolean htmlAllowNonClosedTags; // @since 2.0.0
|
||||
// private final boolean htmlAllowNonClosedTags; // @since 2.0.0
|
||||
|
||||
private MarkwonConfiguration(@NonNull Builder builder) {
|
||||
this.theme = builder.theme;
|
||||
@ -51,7 +51,7 @@ public class MarkwonConfiguration {
|
||||
this.factory = builder.factory;
|
||||
this.htmlParser = builder.htmlParser;
|
||||
this.htmlRenderer = builder.htmlRenderer;
|
||||
this.htmlAllowNonClosedTags = builder.htmlAllowNonClosedTags;
|
||||
// this.htmlAllowNonClosedTags = builder.htmlAllowNonClosedTags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,12 +113,12 @@ public class MarkwonConfiguration {
|
||||
return htmlRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public boolean htmlAllowNonClosedTags() {
|
||||
return htmlAllowNonClosedTags;
|
||||
}
|
||||
// /**
|
||||
// * @since 2.0.0
|
||||
// */
|
||||
// public boolean htmlAllowNonClosedTags() {
|
||||
// return htmlAllowNonClosedTags;
|
||||
// }
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Builder {
|
||||
@ -134,7 +134,7 @@ public class MarkwonConfiguration {
|
||||
private SpannableFactory factory; // @since 1.1.0
|
||||
private MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||
private MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||
private boolean htmlAllowNonClosedTags; // @since 2.0.0
|
||||
// private boolean htmlAllowNonClosedTags; // @since 2.0.0
|
||||
|
||||
Builder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
@ -151,7 +151,7 @@ public class MarkwonConfiguration {
|
||||
this.factory = configuration.factory;
|
||||
this.htmlParser = configuration.htmlParser;
|
||||
this.htmlRenderer = configuration.htmlRenderer;
|
||||
this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags;
|
||||
// this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -208,18 +208,18 @@ public class MarkwonConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param htmlAllowNonClosedTags 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()}
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@NonNull
|
||||
public Builder htmlAllowNonClosedTags(boolean htmlAllowNonClosedTags) {
|
||||
this.htmlAllowNonClosedTags = htmlAllowNonClosedTags;
|
||||
return this;
|
||||
}
|
||||
// /**
|
||||
// * @param htmlAllowNonClosedTags 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()}
|
||||
// * @since 2.0.0
|
||||
// */
|
||||
// @NonNull
|
||||
// public Builder htmlAllowNonClosedTags(boolean htmlAllowNonClosedTags) {
|
||||
// this.htmlAllowNonClosedTags = htmlAllowNonClosedTags;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
@NonNull
|
||||
public MarkwonConfiguration build(@NonNull MarkwonTheme theme, @NonNull AsyncDrawableLoader asyncDrawableLoader) {
|
||||
|
@ -4,23 +4,16 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
|
||||
import org.commonmark.ext.gfm.tables.TableBody;
|
||||
import org.commonmark.ext.gfm.tables.TableCell;
|
||||
import org.commonmark.ext.gfm.tables.TableHead;
|
||||
import org.commonmark.ext.gfm.tables.TableRow;
|
||||
import org.commonmark.node.AbstractVisitor;
|
||||
import org.commonmark.node.BlockQuote;
|
||||
import org.commonmark.node.BulletList;
|
||||
import org.commonmark.node.Code;
|
||||
import org.commonmark.node.CustomBlock;
|
||||
import org.commonmark.node.CustomNode;
|
||||
import org.commonmark.node.Document;
|
||||
import org.commonmark.node.Emphasis;
|
||||
import org.commonmark.node.FencedCodeBlock;
|
||||
import org.commonmark.node.HardLineBreak;
|
||||
import org.commonmark.node.Heading;
|
||||
import org.commonmark.node.HtmlBlock;
|
||||
import org.commonmark.node.HtmlInline;
|
||||
import org.commonmark.node.Image;
|
||||
import org.commonmark.node.IndentedCodeBlock;
|
||||
import org.commonmark.node.Link;
|
||||
@ -29,29 +22,25 @@ import org.commonmark.node.ListItem;
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.node.OrderedList;
|
||||
import org.commonmark.node.Paragraph;
|
||||
import org.commonmark.node.SoftLineBreak;
|
||||
import org.commonmark.node.StrongEmphasis;
|
||||
import org.commonmark.node.Text;
|
||||
import org.commonmark.node.ThematicBreak;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.noties.markwon.SpannableBuilder;
|
||||
import ru.noties.markwon.MarkwonConfiguration;
|
||||
import ru.noties.markwon.SpannableBuilder;
|
||||
import ru.noties.markwon.SpannableFactory;
|
||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||
import ru.noties.markwon.spans.MarkwonTheme;
|
||||
import ru.noties.markwon.table.TableRowSpan;
|
||||
import ru.noties.markwon.tasklist.TaskListBlock;
|
||||
import ru.noties.markwon.tasklist.TaskListItem;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
private final MarkwonConfiguration configuration;
|
||||
private final SpannableBuilder builder;
|
||||
private final MarkwonHtmlParser htmlParser;
|
||||
// private final MarkwonHtmlParser htmlParser;
|
||||
|
||||
private final MarkwonTheme theme;
|
||||
private final SpannableFactory factory;
|
||||
@ -69,18 +58,18 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
this.builder = builder;
|
||||
this.htmlParser = configuration.htmlParser();
|
||||
// this.htmlParser = configuration.htmlParser();
|
||||
|
||||
this.theme = configuration.theme();
|
||||
this.factory = configuration.factory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Document document) {
|
||||
super.visit(document);
|
||||
|
||||
configuration.htmlRenderer().render(configuration, builder, htmlParser);
|
||||
}
|
||||
// @Override
|
||||
// public void visit(Document document) {
|
||||
// super.visit(document);
|
||||
//
|
||||
// configuration.htmlRenderer().render(configuration, builder, htmlParser);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void visit(Text text) {
|
||||
@ -276,15 +265,15 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SoftLineBreak softLineBreak) {
|
||||
// @since 1.1.1 there is an option to treat soft break as a hard break (thus adding new line)
|
||||
if (configuration.softBreakAddsNewLine()) {
|
||||
newLine();
|
||||
} else {
|
||||
builder.append(' ');
|
||||
}
|
||||
}
|
||||
// @Override
|
||||
// public void visit(SoftLineBreak softLineBreak) {
|
||||
// // @since 1.1.1 there is an option to treat soft break as a hard break (thus adding new line)
|
||||
// if (configuration.softBreakAddsNewLine()) {
|
||||
// newLine();
|
||||
// } else {
|
||||
// builder.append(' ');
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void visit(HardLineBreak hardLineBreak) {
|
||||
@ -463,21 +452,21 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
// user can open it in external viewer?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(HtmlBlock htmlBlock) {
|
||||
visitHtml(htmlBlock.getLiteral());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(HtmlInline htmlInline) {
|
||||
visitHtml(htmlInline.getLiteral());
|
||||
}
|
||||
|
||||
private void visitHtml(@Nullable String html) {
|
||||
if (html != null) {
|
||||
htmlParser.processFragment(builder, html);
|
||||
}
|
||||
}
|
||||
// @Override
|
||||
// public void visit(HtmlBlock htmlBlock) {
|
||||
// visitHtml(htmlBlock.getLiteral());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void visit(HtmlInline htmlInline) {
|
||||
// visitHtml(htmlInline.getLiteral());
|
||||
// }
|
||||
//
|
||||
// private void visitHtml(@Nullable String html) {
|
||||
// if (html != null) {
|
||||
// htmlParser.processFragment(builder, html);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void visit(Link link) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user