From dda5b0cd083a77019f66bfc97c10195f8d62b01e Mon Sep 17 00:00:00 2001 From: Cyrus Bakhtiari-Haftlang Date: Fri, 24 Aug 2018 08:52:49 +0200 Subject: [PATCH] Replaced the implementation of SpannableBuilder SpannableBuilder will now extend SpannableStringBuilder. The major diff- erence will be that the span order will be reverted upon a call of `getSpans()`. Another addition will be support for setting multiple multiple spans by array. The reverse order implementation is credited Uncodin/bypass --- .../ru/noties/markwon/SpannableBuilder.java | 344 ------------------ .../markwon/SpannableStringBuilderImpl.java | 13 - .../ru/noties/markwon/SpannedReversed.java | 9 - .../markwon/renderer/SpannableBuilder.java | 63 ++++ .../renderer/SpannableMarkdownVisitor.java | 52 ++- .../markwon/renderer/SpannableRenderer.java | 3 +- .../renderer/html2/MarkwonHtmlRenderer.java | 4 +- .../html2/MarkwonHtmlRendererImpl.java | 10 +- .../renderer/html2/tag/BlockquoteHandler.java | 15 +- .../renderer/html2/tag/ListHandler.java | 11 +- .../renderer/html2/tag/SimpleTagHandler.java | 9 +- .../renderer/html2/tag/StrikeHandler.java | 15 +- .../renderer/html2/tag/TagHandler.java | 10 +- .../renderer/html2/tag/UnderlineHandler.java | 15 +- .../markwon/sample/extension/IconVisitor.java | 6 +- .../sample/extension/MainActivity.java | 4 +- 16 files changed, 146 insertions(+), 437 deletions(-) delete mode 100644 markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java delete mode 100644 markwon/src/main/java/ru/noties/markwon/SpannableStringBuilderImpl.java delete mode 100644 markwon/src/main/java/ru/noties/markwon/SpannedReversed.java create mode 100644 markwon/src/main/java/ru/noties/markwon/renderer/SpannableBuilder.java diff --git a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java b/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java deleted file mode 100644 index a02b53de..00000000 --- a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java +++ /dev/null @@ -1,344 +0,0 @@ -package ru.noties.markwon; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Iterator; - -/** - * This class is used to _revert_ order of applied spans. Original SpannableStringBuilder - * is using an array to store all the information about spans. So, a span that is added first - * will be drawn first, which leads to subtle bugs (spans receive wrong `x` values when - * requested to draw itself) - *

- * since 2.0.0 implements Appendable and CharSequence - * - * @since 1.0.1 - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -public class SpannableBuilder implements Appendable, CharSequence { - - /** - * @since 2.0.0 - */ - public static void setSpans(@NonNull SpannableBuilder builder, @Nullable Object spans, int start, int end) { - if (spans != null) { - - // 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); - } - } else { - builder.setSpan(spans, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } - - private static boolean isPositionValid(int length, int start, int end) { - return !(end <= start - || start < 0 - || end > length); - } - - - private final StringBuilder builder; - - // actually we might be just using ArrayList - private final Deque spans = new ArrayDeque<>(8); - - public SpannableBuilder() { - this(""); - } - - public SpannableBuilder(@NonNull CharSequence cs) { -// this.builder = new SpannableStringBuilderImpl(cs.toString()); - this.builder = new StringBuilder(cs); - copySpans(0, cs); - } - - /** - * Additional method that takes a String, which is proven to NOT contain any spans - * - * @param text String to append - * @return this instance - */ - @NonNull - public SpannableBuilder append(@NonNull String text) { - builder.append(text); - return this; - } - - @NonNull - @Override - public SpannableBuilder append(char c) { - builder.append(c); - return this; - } - - @NonNull - @Override - public SpannableBuilder append(@NonNull CharSequence cs) { - - copySpans(length(), cs); - - builder.append(cs); - - return this; - } - - /** - * @since 2.0.0 to follow Appendable interface - */ - @NonNull - @Override - public SpannableBuilder append(CharSequence csq, int start, int end) { - - final CharSequence cs = csq.subSequence(start, end); - copySpans(length(), cs); - - builder.append(cs); - - return this; - } - - @NonNull - public SpannableBuilder append(@NonNull CharSequence cs, @NonNull Object span) { - final int length = length(); - append(cs); - setSpan(span, length); - return this; - } - - @NonNull - public SpannableBuilder append(@NonNull CharSequence cs, @NonNull Object span, int flags) { - final int length = length(); - append(cs); - setSpan(span, length, length(), flags); - return this; - } - - @NonNull - public SpannableBuilder setSpan(@NonNull Object span, int start) { - return setSpan(span, start, length()); - } - - @NonNull - public SpannableBuilder setSpan(@NonNull Object span, int start, int end) { - return setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @NonNull - public SpannableBuilder setSpan(@NonNull Object span, int start, int end, int flags) { - spans.push(new Span(span, start, end, flags)); - return this; - } - - @Override - public int length() { - return builder.length(); - } - - @Override - public char charAt(int index) { - return builder.charAt(index); - } - - /** - * @since 2.0.0 to follow CharSequence interface - */ - @Override - public CharSequence subSequence(int start, int end) { - return builder.subSequence(start, end); - } - - public char lastChar() { - return builder.charAt(length() - 1); - } - - @NonNull - public CharSequence removeFromEnd(int start) { - - // this method is not intended to be used by clients - // it's a workaround to support tables - - final int end = length(); - - // as we do not expose builder and do no apply spans to it, we are safe to NOT to convert to String - final SpannableStringBuilderImpl impl = new SpannableStringBuilderImpl(builder.subSequence(start, end)); - - final Iterator iterator = spans.iterator(); - - Span span; - - while (iterator.hasNext() && ((span = iterator.next())) != null) { - if (span.start >= start && span.end <= end) { - impl.setSpan(span.what, span.start - start, span.end - start, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - iterator.remove(); - } - } - - builder.replace(start, end, ""); - - return impl; - } - - @Override - @NonNull - public String toString() { - return builder.toString(); - } - - /** - * Moved from {@link #text()} method - * - * @since 2.0.0 - */ - public void trimWhiteSpaceEnd() { - - // now, let's remove trailing & leading newLines (so small amounts of text are displayed correctly) - // @since 1.0.2 - - int length = builder.length(); - - if (length > 0) { - - length = builder.length(); - int amount = 0; - for (int i = length - 1; i >= 0; i--) { - if (Character.isWhitespace(builder.charAt(i))) { - amount += 1; - } else { - break; - } - } - - if (amount > 0) { - - final int newLength = length - amount; - builder.replace(newLength, length, ""); - - // additionally we should apply new length to the spans (otherwise - // sometimes system cannot handle spans with length greater than total length - // which causes some internal failure (no exceptions, no logs) in Layout class - // and no markdown is displayed at all - if (spans.size() > 0) { - Span span; - final Iterator iterator = spans.iterator(); - while (iterator.hasNext()) { - span = iterator.next(); - // if span start is greater than newLength, then remove it... one should - // not use white space for spanning resulting text - if (span.start > newLength) { - iterator.remove(); - } else if (span.end > newLength) { - span.end = newLength; - } - } - } - } - - } - } - - @NonNull - public CharSequence text() { - // @since 2.0.0 redirects this call to `#spannableStringBuilder()` - return spannableStringBuilder(); - } - - /** - * Simple method to create a SpannableStringBuilder, which is created anyway. Unlike {@link #text()} - * method which returns the same SpannableStringBuilder there is no need to cast the resulting - * CharSequence - * - * @since 2.0.0 - */ - @NonNull - public SpannableStringBuilder spannableStringBuilder() { - - // okay, in order to not allow external modification and keep our spans order - // we should not return our builder - // - // plus, if this method was called -> all spans would be applied, which potentially - // breaks the order that we intend to use - // so, we will defensively copy builder - - // as we do not expose builder and do no apply spans to it, we are safe to NOT to convert to String - - final SpannableStringBuilderImpl impl = new SpannableStringBuilderImpl(builder); - - for (Span span : spans) { - impl.setSpan(span.what, span.start, span.end, span.flags); - } - - return impl; - } - - private void copySpans(final int index, @Nullable CharSequence cs) { - - // we must identify already reversed Spanned... - // and (!) iterate backwards when adding (to preserve order) - - if (cs instanceof Spanned) { - - final Spanned spanned = (Spanned) cs; - final boolean reverse = spanned instanceof SpannedReversed; - - final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class); - final int length = spans != null - ? spans.length - : 0; - - if (length > 0) { - if (reverse) { - Object o; - for (int i = length - 1; i >= 0; i--) { - o = spans[i]; - setSpan( - o, - index + spanned.getSpanStart(o), - index + spanned.getSpanEnd(o), - spanned.getSpanFlags(o) - ); - } - } else { - Object o; - for (int i = 0; i < length; i++) { - o = spans[i]; - setSpan( - o, - index + spanned.getSpanStart(o), - index + spanned.getSpanEnd(o), - spanned.getSpanFlags(o) - ); - } - } - } - } - } - - static class Span { - - final Object what; - int start; - int end; - final int flags; - - Span(@NonNull Object what, int start, int end, int flags) { - this.what = what; - this.start = start; - this.end = end; - this.flags = flags; - } - } -} diff --git a/markwon/src/main/java/ru/noties/markwon/SpannableStringBuilderImpl.java b/markwon/src/main/java/ru/noties/markwon/SpannableStringBuilderImpl.java deleted file mode 100644 index 7b29440b..00000000 --- a/markwon/src/main/java/ru/noties/markwon/SpannableStringBuilderImpl.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.noties.markwon; - -import android.text.SpannableStringBuilder; - -/** - * @since 1.0.1 - */ -class SpannableStringBuilderImpl extends SpannableStringBuilder implements SpannedReversed { - - SpannableStringBuilderImpl(CharSequence text) { - super(text); - } -} diff --git a/markwon/src/main/java/ru/noties/markwon/SpannedReversed.java b/markwon/src/main/java/ru/noties/markwon/SpannedReversed.java deleted file mode 100644 index 3fd7f566..00000000 --- a/markwon/src/main/java/ru/noties/markwon/SpannedReversed.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.noties.markwon; - -import android.text.Spanned; - -/** - * @since 1.0.1 - */ -interface SpannedReversed extends Spanned { -} diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableBuilder.java b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableBuilder.java new file mode 100644 index 00000000..5f8b6118 --- /dev/null +++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableBuilder.java @@ -0,0 +1,63 @@ +package ru.noties.markwon.renderer; + +import android.support.annotation.Nullable; +import android.text.SpannableStringBuilder; + +/** + * Copied with modification and renaming from @see Uncodin/bypass + */ +public class SpannableBuilder extends SpannableStringBuilder { + + public SpannableBuilder() { + super(); + } + + SpannableBuilder(CharSequence text, int start, int end) { + super(text, start, end); + } + + @Override + public T[] getSpans(int queryStart, int queryEnd, Class kind) { + T[] ret = super.getSpans(queryStart, queryEnd, kind); + reverse(ret); + return ret; + } + + @Override + public void setSpan(@Nullable Object what, int start, int end, int flags) { + if (what != null && isRangeValid(start, end)) { + if (what.getClass().isArray()) { + for (final Object span : (Object[]) what) { + setSpan(span, start, end, flags); + } + } else { + super.setSpan(what, start, end, flags); + } + } + } + + private boolean isRangeValid(int start, int end) { + final int len; + return end >= start && + (start <= (len = length()) && end <= len) && + start >= 0 && end >= 0; + } + + private static void reverse(Object[] arr) { + if (arr == null) { + return; + } + + int i = 0; + int j = arr.length - 1; + Object tmp; + while (j > i) { + tmp = arr[j]; + arr[j] = arr[i]; + arr[i] = tmp; + j--; + i++; + } + } +} 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 38d283ac..19866a49 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java @@ -2,6 +2,8 @@ package ru.noties.markwon.renderer; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.Editable; +import android.text.Spanned; import org.commonmark.ext.gfm.strikethrough.Strikethrough; import org.commonmark.ext.gfm.tables.TableBody; @@ -36,7 +38,6 @@ import org.commonmark.node.ThematicBreak; import java.util.ArrayList; import java.util.List; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableFactory; import ru.noties.markwon.html.api.MarkwonHtmlParser; @@ -49,8 +50,8 @@ import ru.noties.markwon.tasklist.TaskListItem; public class SpannableMarkdownVisitor extends AbstractVisitor { private final SpannableConfiguration configuration; - private final SpannableBuilder builder; private final MarkwonHtmlParser htmlParser; + private final SpannableBuilder builder; private final SpannableTheme theme; private final SpannableFactory factory; @@ -81,7 +82,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { configuration.htmlRenderer().render(configuration, builder, htmlParser); if (configuration.trimWhiteSpaceEnd()) { - builder.trimWhiteSpaceEnd(); + trimWhiteSpaceEnd(); } } @@ -371,10 +372,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { if (pendingTableRow == null) { pendingTableRow = new ArrayList<>(2); } - pendingTableRow.add(new TableRowSpan.Cell( tableCellAlignment(cell.getAlignment()), - builder.removeFromEnd(length) + removeFromEnd(length) )); tableRowIsHeader = cell.isHeader(); @@ -386,6 +386,22 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { return handled; } + @NonNull + private CharSequence removeFromEnd(int start) { + + // this method is not intended to be used by clients + // it's a workaround to support tables + + final int end = builder.length(); + + // as we do not expose builder and do no apply spans to it, we are safe to NOT to convert to String + final Editable impl = new SpannableBuilder(builder, start, end); + builder.delete(start, end); + + return impl; + } + + @Override public void visit(Paragraph paragraph) { final boolean inTightList = isInTightList(paragraph); @@ -465,23 +481,13 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { setSpan(length, factory.link(theme, destination, configuration.linkResolver())); } - private void setSpan(int start, @Nullable Object span) { - if (span != null) { - - final int length = builder.length(); - - SpannableBuilder.setSpans( - builder, - span, - start, - length - ); - } + private void setSpan(int start, @Nullable Object spans) { + builder.setSpan(spans, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } private void newLine() { if (builder.length() > 0 - && '\n' != builder.lastChar()) { + && '\n' != builder.charAt(builder.length() - 1)) { builder.append('\n'); } } @@ -498,6 +504,16 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { return false; } + private void trimWhiteSpaceEnd() { + final int oldEnd = builder.length(); + int end = oldEnd; + while (end > 0 && builder.charAt(end - 1) <= ' ') { + end--; + } + + builder.delete(end, oldEnd); + } + @TableRowSpan.Alignment private static int tableCellAlignment(TableCell.Alignment alignment) { final int out; diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java index 32d620dd..890c7e72 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java @@ -4,7 +4,6 @@ import android.support.annotation.NonNull; import org.commonmark.node.Node; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; public class SpannableRenderer { @@ -13,6 +12,6 @@ public class SpannableRenderer { public CharSequence render(@NonNull SpannableConfiguration configuration, @NonNull Node node) { final SpannableBuilder builder = new SpannableBuilder(); node.accept(new SpannableMarkdownVisitor(configuration, builder)); - return builder.text(); + return builder; } } 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 338ab211..bdbf9889 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 @@ -2,13 +2,13 @@ package ru.noties.markwon.renderer.html2; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.Spannable; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.MarkwonHtmlParser; import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler; @@ -30,7 +30,7 @@ public abstract class MarkwonHtmlRenderer { public abstract void render( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull MarkwonHtmlParser parser ); diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRendererImpl.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRendererImpl.java index 9cb67baa..e0248485 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRendererImpl.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/MarkwonHtmlRendererImpl.java @@ -2,11 +2,11 @@ package ru.noties.markwon.renderer.html2; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.Spannable; import java.util.List; import java.util.Map; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; import ru.noties.markwon.html.api.MarkwonHtmlParser; @@ -23,14 +23,14 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer { @Override public void render( @NonNull final SpannableConfiguration configuration, - @NonNull final SpannableBuilder builder, + @NonNull final Spannable spannable, @NonNull MarkwonHtmlParser parser) { final int end; if (configuration.htmlIgnoreNonClosedTags()) { end = HtmlTag.NO_END; } else { - end = builder.length(); + end = spannable.length(); } parser.flushInlineTags(end, new MarkwonHtmlParser.FlushAction() { @@ -48,7 +48,7 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer { handler = tagHandler(inline.name()); if (handler != null) { - handler.handle(configuration, builder, inline); + handler.handle(configuration, spannable, inline); } } } @@ -68,7 +68,7 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer { handler = tagHandler(block.name()); if (handler != null) { - handler.handle(configuration, builder, block); + handler.handle(configuration, spannable, block); } else { // see if any of children can be handled apply(block.children()); diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/BlockquoteHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/BlockquoteHandler.java index 99ddf153..75c2f80e 100644 --- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/BlockquoteHandler.java +++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/BlockquoteHandler.java @@ -1,8 +1,9 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.Spanned; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -11,18 +12,14 @@ public class BlockquoteHandler extends TagHandler { @Override public void handle( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull HtmlTag tag) { if (tag.isBlock()) { - visitChildren(configuration, builder, tag.getAsBlock()); + visitChildren(configuration, spannable, tag.getAsBlock()); } - SpannableBuilder.setSpans( - builder, - configuration.factory().blockQuote(configuration.theme()), - tag.start(), - tag.end() - ); + spannable.setSpan(configuration.factory().blockQuote(configuration.theme()), tag.start(), + tag.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } 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 fca098e7..f2a502c3 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 @@ -1,8 +1,9 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.Spanned; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -11,7 +12,7 @@ public class ListHandler extends TagHandler { @Override public void handle( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull HtmlTag tag) { if (!tag.isBlock()) { @@ -33,7 +34,7 @@ public class ListHandler extends TagHandler { for (HtmlTag.Block child : block.children()) { - visitChildren(configuration, builder, child); + visitChildren(configuration, spannable, child); if ("li".equals(child.name())) { // insert list item here @@ -48,7 +49,9 @@ public class ListHandler extends TagHandler { bulletLevel ); } - SpannableBuilder.setSpans(builder, spans, child.start(), child.end()); + + spannable.setSpan(spans, child.start(), child.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } 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 e5940cc7..2a636f5a 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 @@ -2,8 +2,9 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.Spannable; +import android.text.Spanned; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -13,10 +14,12 @@ public abstract class SimpleTagHandler extends TagHandler { public abstract Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag); @Override - public void handle(@NonNull SpannableConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull HtmlTag tag) { + public void handle(@NonNull SpannableConfiguration configuration, + @NonNull Spannable spannable, + @NonNull HtmlTag tag) { final Object spans = getSpans(configuration, tag); if (spans != null) { - SpannableBuilder.setSpans(builder, spans, tag.start(), tag.end()); + spannable.setSpan(spans, tag.start(), tag.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } 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 965ddfea..da91c5f9 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,8 +1,9 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.Spanned; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -11,18 +12,14 @@ public class StrikeHandler extends TagHandler { @Override public void handle( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull HtmlTag tag) { if (tag.isBlock()) { - visitChildren(configuration, builder, tag.getAsBlock()); + visitChildren(configuration, spannable, tag.getAsBlock()); } - SpannableBuilder.setSpans( - builder, - configuration.factory().strikethrough(), - tag.start(), - tag.end() - ); + spannable.setSpan(configuration.factory().strikethrough(), tag.start(), tag.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } 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 818c4a98..bd2d976b 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 @@ -1,8 +1,8 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; +import android.text.Spannable; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -10,13 +10,13 @@ public abstract class TagHandler { public abstract void handle( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull HtmlTag tag ); protected static void visitChildren( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @NonNull HtmlTag.Block block) { TagHandler handler; @@ -29,9 +29,9 @@ public abstract class TagHandler { handler = configuration.htmlRenderer().tagHandler(child.name()); if (handler != null) { - handler.handle(configuration, builder, child); + handler.handle(configuration, spannable, child); } else { - visitChildren(configuration, builder, child); + visitChildren(configuration, spannable, 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 ff870ef6..27a9ea23 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,8 +1,9 @@ package ru.noties.markwon.renderer.html2.tag; import android.support.annotation.NonNull; +import android.text.Spannable; +import android.text.Spanned; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.html.api.HtmlTag; @@ -11,21 +12,17 @@ public class UnderlineHandler extends TagHandler { @Override public void handle( @NonNull SpannableConfiguration configuration, - @NonNull SpannableBuilder builder, + @NonNull Spannable spannable, @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()); + visitChildren(configuration, spannable, tag.getAsBlock()); } - SpannableBuilder.setSpans( - builder, - configuration.factory().underline(), - tag.start(), - tag.end() - ); + spannable.setSpan(configuration.factory().underline(), + tag.start(), tag.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java index d373ff75..72a56245 100644 --- a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java @@ -1,13 +1,13 @@ package ru.noties.markwon.sample.extension; import android.support.annotation.NonNull; +import android.text.Spanned; import android.text.TextUtils; -import android.widget.TextView; import org.commonmark.node.CustomNode; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.renderer.SpannableBuilder; import ru.noties.markwon.renderer.SpannableMarkdownVisitor; @SuppressWarnings("WeakerAccess") @@ -51,7 +51,7 @@ public class IconVisitor extends SpannableMarkdownVisitor { final int length = builder.length(); builder.append(name); - builder.setSpan(iconSpanProvider.provide(name, color, size), length); + builder.setSpan(iconSpanProvider.provide(name, color, size), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); builder.append(' '); return true; diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java index 19a69704..891640b2 100644 --- a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java @@ -12,8 +12,8 @@ import org.commonmark.parser.Parser; import java.util.Arrays; -import ru.noties.markwon.SpannableBuilder; import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.renderer.SpannableBuilder; import ru.noties.markwon.spans.SpannableTheme; import ru.noties.markwon.tasklist.TaskListExtension; @@ -70,6 +70,6 @@ public class MainActivity extends Activity { node.accept(visitor); // apply - textView.setText(builder.text()); + textView.setText(builder); } }