Add htmlIgnoreNonClosedTags option for SpannableConfiguration
This commit is contained in:
parent
97e6eb47a3
commit
5c9ba0f252
@ -2,9 +2,11 @@
|
||||
|
||||
# Markwon
|
||||
|
||||
<i>
|
||||
|
||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon%22)
|
||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-image-loader%22)
|
||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22)
|
||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22)
|
||||
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-view%22)
|
||||
|
||||
**Markwon** is a library for Android that renders markdown as system-native Spannables. It gives ability to display markdown in all TextView widgets (**TextView**, **Button**, **Switch**, **CheckBox**, etc), **Notifications**, **Toasts**, etc. <u>**No WebView is required**</u>. Library provides reasonable defaults for display style of markdown but also gives all the means to tweak the appearance if desired. All markdown features are supported (including limited support for inlined HTML code, markdown tables and images).
|
||||
|
@ -25,6 +25,16 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
||||
|
||||
public static void setSpans(@NonNull SpannableBuilder builder, @Nullable Object spans, int start, int end) {
|
||||
if (spans != null) {
|
||||
|
||||
// let's filter non-valid positions here, so there is no need to validate
|
||||
// it whilst applying non-closed html tags
|
||||
//
|
||||
// setting a span for an invalid position can lead to silent fail (no exception,
|
||||
// but execution is stopped)
|
||||
if (!isPositionValid(builder.length(), start, end)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (spans.getClass().isArray()) {
|
||||
for (Object o : ((Object[]) spans)) {
|
||||
builder.setSpan(o, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
@ -35,6 +45,12 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPositionValid(int length, int start, int end) {
|
||||
return !(end <= start
|
||||
|| start < 0
|
||||
|| end > length);
|
||||
}
|
||||
|
||||
|
||||
private final StringBuilder builder;
|
||||
|
||||
|
@ -3,6 +3,7 @@ package ru.noties.markwon;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.noties.markwon.html.api.HtmlTag;
|
||||
import ru.noties.markwon.html.api.MarkwonHtmlParser;
|
||||
import ru.noties.markwon.renderer.ImageSizeResolver;
|
||||
import ru.noties.markwon.renderer.ImageSizeResolverDef;
|
||||
@ -30,13 +31,13 @@ public class SpannableConfiguration {
|
||||
private final SyntaxHighlight syntaxHighlight;
|
||||
private final LinkSpan.Resolver linkResolver;
|
||||
private final UrlProcessor urlProcessor;
|
||||
// private final SpannableHtmlParser htmlParser;
|
||||
private final ImageSizeResolver imageSizeResolver;
|
||||
private final SpannableFactory factory; // @since 1.1.0
|
||||
private final boolean softBreakAddsNewLine; // @since 1.1.1
|
||||
private final boolean trimWhiteSpaceEnd; // @since 2.0.0
|
||||
private final MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||
private final MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||
private final boolean htmlIgnoreNonClosedTags; // @since 2.0.0
|
||||
|
||||
private SpannableConfiguration(@NonNull Builder builder) {
|
||||
this.theme = builder.theme;
|
||||
@ -44,13 +45,13 @@ public class SpannableConfiguration {
|
||||
this.syntaxHighlight = builder.syntaxHighlight;
|
||||
this.linkResolver = builder.linkResolver;
|
||||
this.urlProcessor = builder.urlProcessor;
|
||||
// this.htmlParser = builder.htmlParser;
|
||||
this.imageSizeResolver = builder.imageSizeResolver;
|
||||
this.factory = builder.factory;
|
||||
this.softBreakAddsNewLine = builder.softBreakAddsNewLine;
|
||||
this.trimWhiteSpaceEnd = builder.trimWhiteSpaceEnd;
|
||||
this.htmlParser = builder.htmlParser;
|
||||
this.htmlRenderer = builder.htmlRenderer;
|
||||
this.htmlIgnoreNonClosedTags = builder.htmlIgnoreNonClosedTags;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -78,11 +79,6 @@ public class SpannableConfiguration {
|
||||
return urlProcessor;
|
||||
}
|
||||
|
||||
// @NonNull
|
||||
// public SpannableHtmlParser htmlParser() {
|
||||
// return htmlParser;
|
||||
// }
|
||||
|
||||
@NonNull
|
||||
public ImageSizeResolver imageSizeResolver() {
|
||||
return imageSizeResolver;
|
||||
@ -125,6 +121,13 @@ public class SpannableConfiguration {
|
||||
return htmlRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public boolean htmlIgnoreNonClosedTags() {
|
||||
return htmlIgnoreNonClosedTags;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class Builder {
|
||||
|
||||
@ -134,13 +137,13 @@ public class SpannableConfiguration {
|
||||
private SyntaxHighlight syntaxHighlight;
|
||||
private LinkSpan.Resolver linkResolver;
|
||||
private UrlProcessor urlProcessor;
|
||||
// private SpannableHtmlParser htmlParser;
|
||||
private ImageSizeResolver imageSizeResolver;
|
||||
private SpannableFactory factory; // @since 1.1.0
|
||||
private boolean softBreakAddsNewLine; // @since 1.1.1
|
||||
private boolean trimWhiteSpaceEnd = true; // @since 2.0.0
|
||||
private MarkwonHtmlParser htmlParser; // @since 2.0.0
|
||||
private MarkwonHtmlRenderer htmlRenderer; // @since 2.0.0
|
||||
private boolean htmlIgnoreNonClosedTags = true; // @since 2.0.0
|
||||
|
||||
Builder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
@ -176,12 +179,6 @@ public class SpannableConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
// @NonNull
|
||||
// public Builder htmlParser(@NonNull SpannableHtmlParser htmlParser) {
|
||||
// this.htmlParser = htmlParser;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @since 1.0.1
|
||||
*/
|
||||
@ -243,6 +240,19 @@ public class SpannableConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param htmlIgnoreNonClosedTags that indicates if non-closed html tags should be kept open.
|
||||
* If this argument is false then all non-closed HTML tags
|
||||
* will be closed at the end of a document. Otherwise they will
|
||||
* be delivered non-closed {@link HtmlTag#isClosed()}
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@NonNull
|
||||
public Builder htmlIgnoreNonClosedTags(boolean htmlIgnoreNonClosedTags) {
|
||||
this.htmlIgnoreNonClosedTags = htmlIgnoreNonClosedTags;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SpannableConfiguration build() {
|
||||
|
||||
@ -290,16 +300,6 @@ public class SpannableConfiguration {
|
||||
htmlRenderer = MarkwonHtmlRenderer.create();
|
||||
}
|
||||
|
||||
// if (htmlParser == null) {
|
||||
// htmlParser = SpannableHtmlParser.create(
|
||||
// factory,
|
||||
// theme,
|
||||
// asyncDrawableLoader,
|
||||
// urlProcessor,
|
||||
// linkResolver,
|
||||
// imageSizeResolver);
|
||||
// }
|
||||
|
||||
return new SpannableConfiguration(this);
|
||||
}
|
||||
}
|
||||
|
@ -471,13 +471,22 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
||||
|
||||
final int length = builder.length();
|
||||
|
||||
if (span.getClass().isArray()) {
|
||||
for (Object o : ((Object[]) span)) {
|
||||
builder.setSpan(o, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
} else {
|
||||
builder.setSpan(span, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
SpannableBuilder.setSpans(
|
||||
builder,
|
||||
span,
|
||||
start,
|
||||
length
|
||||
);
|
||||
|
||||
// final int length = builder.length();
|
||||
//
|
||||
// if (span.getClass().isArray()) {
|
||||
// for (Object o : ((Object[]) span)) {
|
||||
// builder.setSpan(o, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// }
|
||||
// } else {
|
||||
// builder.setSpan(span, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,13 +26,26 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
@NonNull final SpannableBuilder builder,
|
||||
@NonNull MarkwonHtmlParser parser) {
|
||||
|
||||
final int length = builder.length();
|
||||
final int end;
|
||||
if (configuration.htmlIgnoreNonClosedTags()) {
|
||||
end = HtmlTag.NO_END;
|
||||
} else {
|
||||
end = builder.length();
|
||||
}
|
||||
|
||||
parser.flushInlineTags(length, new MarkwonHtmlParser.FlushAction<HtmlTag.Inline>() {
|
||||
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(configuration, builder, inline);
|
||||
@ -41,11 +54,18 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
}
|
||||
});
|
||||
|
||||
parser.flushBlockTags(length, new MarkwonHtmlParser.FlushAction<HtmlTag.Block>() {
|
||||
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(configuration, builder, block);
|
||||
|
Loading…
x
Reference in New Issue
Block a user