diff --git a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java b/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
index 9e3ec713..20992e7f 100644
--- a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
+++ b/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
@@ -5,41 +5,52 @@ 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 {
+public class SpannableBuilder extends SpannableStringBuilder {
+
+ public SpannableBuilder() {
+ super();
+ }
+
+ public 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;
+ }
/**
- * @since 2.0.0
+ * Convenience for allowing {@link Nullable} and {@link NonNull} spans, as well as
+ * {@link NonNull} array of spans. {@link Spanned#SPAN_EXCLUSIVE_EXCLUSIVE} will be applied as
+ * flag.
+ *
+ * @param spans A span object, an array of span objects or null
+ * @param start Start index (inclusive)
+ * @param end End index (exclusive)
*/
- public static void setSpans(@NonNull SpannableBuilder builder, @Nullable Object spans, int start, int end) {
+ public void setSpans(@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)) {
+ if (!isPositionValid(length(), start, end)) {
return;
}
if (spans.getClass().isArray()) {
for (Object o : ((Object[]) spans)) {
- builder.setSpan(o, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ setSpan(o, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
- builder.setSpan(spans, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ setSpan(spans, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
@@ -50,242 +61,20 @@ public class SpannableBuilder implements Appendable, CharSequence {
&& 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 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();
- }
+ private static void reverse(Object[] arr) {
+ if (arr == null) {
+ return;
}
- builder.replace(start, end, "");
-
- return impl;
- }
-
- @Override
- @NonNull
- public String toString() {
- return builder.toString();
- }
-
- @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)
- );
- }
- }
- }
+ 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++;
}
}
-
- 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;
- }
- }
-}
+}
\ No newline at end of file
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/SpannableMarkdownVisitor.java b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
index 967784e5..81596d01 100644
--- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
+++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
@@ -415,7 +415,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
pendingTableRow.add(new TableRowSpan.Cell(
tableCellAlignment(cell.getAlignment()),
- builder.removeFromEnd(length)
+ removeFromEnd(length)
));
tableRowIsHeader = cell.isHeader();
@@ -508,12 +508,13 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
}
private void setSpan(int start, @Nullable Object span) {
- SpannableBuilder.setSpans(builder, span, start, builder.length());
+ builder.setSpans(span, start, builder.length());
}
private void newLine() {
- if (builder.length() > 0
- && '\n' != builder.lastChar()) {
+ final int length = builder.length();
+
+ if (length > 0 && '\n' != builder.charAt(length - 1)) {
builder.append('\n');
}
}
@@ -530,6 +531,21 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
return false;
}
+ @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 = 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 SpannableBuilder impl = new SpannableBuilder(builder, start, end);
+ builder.delete(start, end);
+
+ return impl;
+ }
+
+
@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..ad5441bf 100644
--- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java
+++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java
@@ -10,9 +10,9 @@ import ru.noties.markwon.SpannableConfiguration;
public class SpannableRenderer {
@NonNull
- public CharSequence render(@NonNull SpannableConfiguration configuration, @NonNull Node node) {
+ public SpannableBuilder 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/tag/BlockquoteHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/BlockquoteHandler.java
index 99ddf153..4af87f67 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
@@ -18,8 +18,7 @@ public class BlockquoteHandler extends TagHandler {
visitChildren(configuration, builder, tag.getAsBlock());
}
- SpannableBuilder.setSpans(
- builder,
+ builder.setSpans(
configuration.factory().blockQuote(configuration.theme()),
tag.start(),
tag.end()
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..35d61800 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
@@ -48,7 +48,7 @@ public class ListHandler extends TagHandler {
bulletLevel
);
}
- SpannableBuilder.setSpans(builder, spans, child.start(), child.end());
+ builder.setSpans(spans, child.start(), child.end());
}
}
}
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..32dfce08 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
@@ -14,9 +14,6 @@ public abstract class SimpleTagHandler extends TagHandler {
@Override
public void handle(@NonNull SpannableConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull HtmlTag tag) {
- final Object spans = getSpans(configuration, tag);
- if (spans != null) {
- SpannableBuilder.setSpans(builder, spans, tag.start(), tag.end());
- }
+ builder.setSpans(getSpans(configuration, tag), tag.start(), tag.end());
}
}
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..cf9a1b4b 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
@@ -18,8 +18,7 @@ public class StrikeHandler extends TagHandler {
visitChildren(configuration, builder, tag.getAsBlock());
}
- SpannableBuilder.setSpans(
- builder,
+ builder.setSpans(
configuration.factory().strikethrough(),
tag.start(),
tag.end()
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..3bd637a1 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
@@ -21,8 +21,7 @@ public class UnderlineHandler extends TagHandler {
visitChildren(configuration, builder, tag.getAsBlock());
}
- SpannableBuilder.setSpans(
- builder,
+ builder.setSpans(
configuration.factory().underline(),
tag.start(),
tag.end()
diff --git a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java
index 047a0584..b6a2c485 100644
--- a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java
+++ b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java
@@ -1,7 +1,6 @@
package ru.noties.markwon.renderer.visitor;
import android.support.annotation.NonNull;
-import android.text.SpannableStringBuilder;
import org.commonmark.node.Node;
import org.junit.Test;
@@ -50,20 +49,19 @@ public class SpannableMarkdownVisitorTest {
final Node node = Markwon.createParser().parse(data.input());
node.accept(visitor);
- final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
final TestValidator validator = TestValidator.create(file);
int index = 0;
for (TestNode testNode : data.output()) {
- index = validator.validate(stringBuilder, index, testNode);
+ index = validator.validate(builder, index, testNode);
}
// assert that the whole thing is processed
- assertEquals("`" + stringBuilder + "`", stringBuilder.length(), index);
+ assertEquals("`" + builder + "`", builder.length(), index);
- final Object[] spans = stringBuilder.getSpans(0, stringBuilder.length(), Object.class);
+ final Object[] spans = builder.getSpans(0, builder.length(), Object.class);
final int length = spans != null
? spans.length
: 0;
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..06bf7df2 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,6 +1,7 @@
package ru.noties.markwon.sample.extension;
import android.support.annotation.NonNull;
+import android.text.Spanned;
import android.text.TextUtils;
import android.widget.TextView;
@@ -51,9 +52,14 @@ 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,
+ 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..49c72ca8 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
@@ -70,6 +70,6 @@ public class MainActivity extends Activity {
node.accept(visitor);
// apply
- textView.setText(builder.text());
+ textView.setText(builder);
}
}
diff --git a/sample-latex-math/src/main/java/ru/noties/markwon/sample/jlatexmath/MainActivity.java b/sample-latex-math/src/main/java/ru/noties/markwon/sample/jlatexmath/MainActivity.java
index dee84ee4..776ee850 100644
--- a/sample-latex-math/src/main/java/ru/noties/markwon/sample/jlatexmath/MainActivity.java
+++ b/sample-latex-math/src/main/java/ru/noties/markwon/sample/jlatexmath/MainActivity.java
@@ -8,7 +8,6 @@ import org.commonmark.node.CustomBlock;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
-import ru.noties.jlatexmath.JLatexMathAndroid;
import ru.noties.markwon.Markwon;
import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration;
@@ -83,8 +82,7 @@ public class MainActivity extends Activity {
final int length = builder.length();
builder.append(latex);
- SpannableBuilder.setSpans(
- builder,
+ builder.setSpans(
configuration.factory().image(
configuration.theme(),
JLatexMathMedia.makeDestination(latex),
@@ -100,6 +98,6 @@ public class MainActivity extends Activity {
};
node.accept(visitor);
- Markwon.setText(textView, builder.text());
+ Markwon.setText(textView, builder);
}
}