Add trimWhiteSpaceEnd configuration option
This commit is contained in:
parent
359fd8699d
commit
4a6e5002e8
@ -96,6 +96,7 @@ public class MarkdownRenderer {
|
|||||||
.codeTextColor(prism4jTheme.textColor())
|
.codeTextColor(prism4jTheme.textColor())
|
||||||
.build())
|
.build())
|
||||||
.factory(new GifAwareSpannableFactory(gifPlaceholder))
|
.factory(new GifAwareSpannableFactory(gifPlaceholder))
|
||||||
|
.trimWhiteSpaceEnd(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final long start = SystemClock.uptimeMillis();
|
final long start = SystemClock.uptimeMillis();
|
||||||
|
@ -26,7 +26,8 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
|
|
||||||
// we will be using SpannableStringBuilder anyway as a backing store
|
// we will be using SpannableStringBuilder anyway as a backing store
|
||||||
// as it has tight connection with system (implements some hidden methods, etc)
|
// as it has tight connection with system (implements some hidden methods, etc)
|
||||||
private final SpannableStringBuilder builder;
|
// private final SpannableStringBuilder builder;
|
||||||
|
private final StringBuilder builder;
|
||||||
|
|
||||||
// actually we might be just using ArrayList
|
// actually we might be just using ArrayList
|
||||||
private final Deque<Span> spans = new ArrayDeque<>(8);
|
private final Deque<Span> spans = new ArrayDeque<>(8);
|
||||||
@ -36,7 +37,8 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SpannableBuilder(@NonNull CharSequence cs) {
|
public SpannableBuilder(@NonNull CharSequence cs) {
|
||||||
this.builder = new SpannableStringBuilderImpl(cs.toString());
|
// this.builder = new SpannableStringBuilderImpl(cs.toString());
|
||||||
|
this.builder = new StringBuilder(cs);
|
||||||
copySpans(0, cs);
|
copySpans(0, cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
|
|
||||||
copySpans(length(), cs);
|
copySpans(length(), cs);
|
||||||
|
|
||||||
builder.append(cs.toString());
|
builder.append(cs);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -80,7 +82,7 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
final CharSequence cs = csq.subSequence(start, end);
|
final CharSequence cs = csq.subSequence(start, end);
|
||||||
copySpans(length(), cs);
|
copySpans(length(), cs);
|
||||||
|
|
||||||
builder.append(cs.toString());
|
builder.append(cs);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -172,8 +174,73 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
return builder.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<Span> 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
|
@NonNull
|
||||||
public CharSequence text() {
|
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
|
// okay, in order to not allow external modification and keep our spans order
|
||||||
// we should not return our builder
|
// we should not return our builder
|
||||||
@ -183,30 +250,13 @@ public class SpannableBuilder implements Appendable, CharSequence {
|
|||||||
// so, we will defensively copy builder
|
// 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
|
// 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);
|
final SpannableStringBuilderImpl impl = new SpannableStringBuilderImpl(builder);
|
||||||
|
|
||||||
for (Span span : spans) {
|
for (Span span : spans) {
|
||||||
impl.setSpan(span.what, span.start, span.end, span.flags);
|
impl.setSpan(span.what, span.start, span.end, span.flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, let's remove trailing newLines (so small amounts of text are displayed correctly)
|
|
||||||
// @since 1.0.2
|
|
||||||
|
|
||||||
final int length = impl.length();
|
|
||||||
if (length > 0) {
|
|
||||||
int amount = 0;
|
|
||||||
for (int i = length - 1; i >= 0; i--) {
|
|
||||||
if (Character.isWhitespace(impl.charAt(i))) {
|
|
||||||
amount += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (amount > 0) {
|
|
||||||
impl.replace(length - amount, length, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return impl;
|
return impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ public class SpannableConfiguration {
|
|||||||
private final SpannableHtmlParser htmlParser;
|
private final SpannableHtmlParser htmlParser;
|
||||||
private final ImageSizeResolver imageSizeResolver;
|
private final ImageSizeResolver imageSizeResolver;
|
||||||
private final SpannableFactory factory; // @since 1.1.0
|
private final SpannableFactory factory; // @since 1.1.0
|
||||||
|
private final boolean trimWhiteSpaceEnd; // @since 2.0.0
|
||||||
|
|
||||||
private SpannableConfiguration(@NonNull Builder builder) {
|
private SpannableConfiguration(@NonNull Builder builder) {
|
||||||
this.theme = builder.theme;
|
this.theme = builder.theme;
|
||||||
@ -42,6 +43,7 @@ public class SpannableConfiguration {
|
|||||||
this.htmlParser = builder.htmlParser;
|
this.htmlParser = builder.htmlParser;
|
||||||
this.imageSizeResolver = builder.imageSizeResolver;
|
this.imageSizeResolver = builder.imageSizeResolver;
|
||||||
this.factory = builder.factory;
|
this.factory = builder.factory;
|
||||||
|
this.trimWhiteSpaceEnd = builder.trimWhiteSpaceEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -84,6 +86,13 @@ public class SpannableConfiguration {
|
|||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public boolean trimWhiteSpaceEnd() {
|
||||||
|
return trimWhiteSpaceEnd;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
@ -96,6 +105,7 @@ public class SpannableConfiguration {
|
|||||||
private SpannableHtmlParser htmlParser;
|
private SpannableHtmlParser htmlParser;
|
||||||
private ImageSizeResolver imageSizeResolver;
|
private ImageSizeResolver imageSizeResolver;
|
||||||
private SpannableFactory factory;
|
private SpannableFactory factory;
|
||||||
|
private boolean trimWhiteSpaceEnd = true;
|
||||||
|
|
||||||
Builder(@NonNull Context context) {
|
Builder(@NonNull Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -155,6 +165,18 @@ public class SpannableConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will trim white space(s) from the end from resulting text.
|
||||||
|
* By default `true`
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder trimWhiteSpaceEnd(boolean trimWhiteSpaceEnd) {
|
||||||
|
this.trimWhiteSpaceEnd = trimWhiteSpaceEnd;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public SpannableConfiguration build() {
|
public SpannableConfiguration build() {
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import org.commonmark.node.BulletList;
|
|||||||
import org.commonmark.node.Code;
|
import org.commonmark.node.Code;
|
||||||
import org.commonmark.node.CustomBlock;
|
import org.commonmark.node.CustomBlock;
|
||||||
import org.commonmark.node.CustomNode;
|
import org.commonmark.node.CustomNode;
|
||||||
|
import org.commonmark.node.Document;
|
||||||
import org.commonmark.node.Emphasis;
|
import org.commonmark.node.Emphasis;
|
||||||
import org.commonmark.node.FencedCodeBlock;
|
import org.commonmark.node.FencedCodeBlock;
|
||||||
import org.commonmark.node.HardLineBreak;
|
import org.commonmark.node.HardLineBreak;
|
||||||
@ -77,6 +78,15 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
|
|||||||
this.factory = configuration.factory();
|
this.factory = configuration.factory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Document document) {
|
||||||
|
super.visit(document);
|
||||||
|
|
||||||
|
if (configuration.trimWhiteSpaceEnd()) {
|
||||||
|
builder.trimWhiteSpaceEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Text text) {
|
public void visit(Text text) {
|
||||||
builder.append(text.getLiteral());
|
builder.append(text.getLiteral());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user