diff --git a/README.md b/README.md
index 45228c28..7545d2b4 100644
--- a/README.md
+++ b/README.md
@@ -158,8 +158,6 @@ Or leave it empty and use the [link text itself].
Inline `code` has `back-ticks around` it.
-**Please note, that syntax highlighting is supported but library provides no means to do it automatically*
-
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
diff --git a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
index a868bbcd..23ff268b 100644
--- a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
+++ b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
@@ -96,7 +96,6 @@ public class MarkdownRenderer {
.codeTextColor(prism4jTheme.textColor())
.build())
.factory(new GifAwareSpannableFactory(gifPlaceholder))
- .trimWhiteSpaceEnd(false)
.build();
final long start = SystemClock.uptimeMillis();
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b24d050e..97c5fb1b 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -8,13 +8,16 @@
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:padding="16dip"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:scrollbarStyle="outsideOverlay"
android:layout_marginTop="?android:attr/actionBarSize">
-## Trim white space from end
-
-`trimWhiteSpaceEnd` option controls whether or not to trim white spaces from the
-end of a document.
-
-```java
-SpannableConfiguration.builder(context)
- .trimWhiteSpaceEnd(boolean)
- .build();
-```
-
-If not provided explicitly, default `true` value will be used.
-
-:::tip Before
-Before `2.0.0` version this functionality was _implicitly_ included in
-`SpannableBuilder#text` method. This is no longer true and now `SpannableBuilder`
-does not trim white spaces (which was by default and non-configurable)
-:::
-
## HTML
### Parser
diff --git a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java b/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
index a02b53de..cba31c27 100644
--- a/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
+++ b/markwon/src/main/java/ru/noties/markwon/SpannableBuilder.java
@@ -61,7 +61,6 @@ public class SpannableBuilder implements Appendable, CharSequence {
}
public SpannableBuilder(@NonNull CharSequence cs) {
-// this.builder = new SpannableStringBuilderImpl(cs.toString());
this.builder = new StringBuilder(cs);
copySpans(0, cs);
}
@@ -198,58 +197,6 @@ public class SpannableBuilder implements Appendable, CharSequence {
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()`
diff --git a/markwon/src/main/java/ru/noties/markwon/SpannableConfiguration.java b/markwon/src/main/java/ru/noties/markwon/SpannableConfiguration.java
index 319698ea..cb2a34bc 100644
--- a/markwon/src/main/java/ru/noties/markwon/SpannableConfiguration.java
+++ b/markwon/src/main/java/ru/noties/markwon/SpannableConfiguration.java
@@ -3,7 +3,6 @@ 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;
@@ -34,7 +33,6 @@ public class SpannableConfiguration {
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 htmlAllowNonClosedTags; // @since 2.0.0
@@ -48,7 +46,6 @@ public class SpannableConfiguration {
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.htmlAllowNonClosedTags = builder.htmlAllowNonClosedTags;
@@ -98,13 +95,6 @@ public class SpannableConfiguration {
return softBreakAddsNewLine;
}
- /**
- * @since 2.0.0
- */
- public boolean trimWhiteSpaceEnd() {
- return trimWhiteSpaceEnd;
- }
-
/**
* @since 2.0.0
*/
@@ -140,7 +130,6 @@ public class SpannableConfiguration {
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 htmlAllowNonClosedTags; // @since 2.0.0
@@ -210,18 +199,6 @@ public class SpannableConfiguration {
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;
- }
-
/**
* @since 2.0.0
*/
@@ -242,9 +219,9 @@ public class SpannableConfiguration {
/**
* @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()}
+ * 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
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 3ce3db87..f3342203 100644
--- a/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
+++ b/markwon/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
@@ -6,6 +6,7 @@ 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;
@@ -79,10 +80,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
super.visit(document);
configuration.htmlRenderer().render(configuration, builder, htmlParser);
-
- if (configuration.trimWhiteSpaceEnd()) {
- builder.trimWhiteSpaceEnd();
- }
}
@Override
@@ -122,9 +119,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
blockQuoteIndent -= 1;
- newLine();
- if (blockQuoteIndent == 0) {
- builder.append('\n');
+ if (hasNext(blockQuote)) {
+ newLine();
+ if (blockQuoteIndent == 0) {
+ builder.append('\n');
+ }
}
}
@@ -145,7 +144,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
@Override
public void visit(FencedCodeBlock fencedCodeBlock) {
// @since 1.0.4
- visitCodeBlock(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral());
+ visitCodeBlock(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral(), fencedCodeBlock);
}
/**
@@ -153,7 +152,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
*/
@Override
public void visit(IndentedCodeBlock indentedCodeBlock) {
- visitCodeBlock(null, indentedCodeBlock.getLiteral());
+ visitCodeBlock(null, indentedCodeBlock.getLiteral(), indentedCodeBlock);
}
/**
@@ -161,7 +160,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
* @param code content of a code block
* @since 1.0.4
*/
- private void visitCodeBlock(@Nullable String info, @NonNull String code) {
+ private void visitCodeBlock(@Nullable String info, @NonNull String code, @NonNull Node node) {
+
newLine();
final int length = builder.length();
@@ -172,12 +172,16 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
configuration.syntaxHighlight()
.highlight(info, code)
);
- builder.append('\u00a0').append('\n');
+
+ newLine();
+ builder.append('\u00a0');
setSpan(length, factory.code(theme, true));
- newLine();
- builder.append('\n');
+ if (hasNext(node)) {
+ newLine();
+ builder.append('\n');
+ }
}
@Override
@@ -191,11 +195,16 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
}
private void visitList(Node node) {
+
newLine();
+
visitChildren(node);
- newLine();
- if (listLevel == 0 && blockQuoteIndent == 0) {
- builder.append('\n');
+
+ if (hasNext(node)) {
+ newLine();
+ if (listLevel == 0 && blockQuoteIndent == 0) {
+ builder.append('\n');
+ }
}
}
@@ -230,7 +239,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
blockQuoteIndent -= 1;
listLevel -= 1;
- newLine();
+ if (hasNext(listItem)) {
+ newLine();
+ }
}
@Override
@@ -243,8 +254,10 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
setSpan(length, factory.thematicBreak(theme));
- newLine();
- builder.append('\n');
+ if (hasNext(thematicBreak)) {
+ newLine();
+ builder.append('\n');
+ }
}
@Override
@@ -256,10 +269,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
visitChildren(heading);
setSpan(length, factory.heading(theme, heading.getLevel()));
- newLine();
-
- // after heading we add another line anyway (no additional checks)
- builder.append('\n');
+ if (hasNext(heading)) {
+ newLine();
+ // after heading we add another line anyway (no additional checks)
+ builder.append('\n');
+ }
}
@Override
@@ -282,12 +296,17 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
*/
@Override
public void visit(CustomBlock customBlock) {
+
if (customBlock instanceof TaskListBlock) {
blockQuoteIndent += 1;
visitChildren(customBlock);
blockQuoteIndent -= 1;
- newLine();
- builder.append('\n');
+
+ if (hasNext(customBlock)) {
+ newLine();
+ builder.append('\n');
+ }
+
} else {
super.visit(customBlock);
}
@@ -316,7 +335,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
setSpan(length, factory.taskListItem(theme, blockQuoteIndent, listItem.done()));
- newLine();
+ if (hasNext(customNode)) {
+ newLine();
+ }
blockQuoteIndent -= listItem.indent();
@@ -330,18 +351,37 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
final boolean handled;
if (node instanceof TableBody) {
+
visitChildren(node);
tableRows = 0;
handled = true;
- newLine();
- builder.append('\n');
- } else if (node instanceof TableRow) {
+
+ if (hasNext(node)) {
+ newLine();
+ builder.append('\n');
+ }
+
+ } else if (node instanceof TableRow || node instanceof TableHead) {
final int length = builder.length();
+
visitChildren(node);
if (pendingTableRow != null) {
+ // @since 2.0.0
+ // we cannot rely on hasNext(TableHead) as it's not reliable
+ // we must apply new line manually and then exclude it from tableRow span
+ final boolean addNewLine;
+ {
+ final int builderLength = builder.length();
+ addNewLine = builderLength > 0
+ && '\n' != builder.charAt(builderLength - 1);
+ }
+ if (addNewLine) {
+ builder.append('\n');
+ }
+
// @since 1.0.4 Replace table char with non-breakable space
// we need this because if table is at the end of the text, then it will be
// trimmed from the final result
@@ -357,12 +397,13 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
? 0
: tableRows + 1;
- setSpan(length, span);
- newLine();
+ setSpan(addNewLine ? length + 1 : length, span);
+
pendingTableRow = null;
}
handled = true;
+
} else if (node instanceof TableCell) {
final TableCell cell = (TableCell) node;
@@ -383,11 +424,13 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
} else {
handled = false;
}
+
return handled;
}
@Override
public void visit(Paragraph paragraph) {
+
final boolean inTightList = isInTightList(paragraph);
if (!inTightList) {
@@ -400,9 +443,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
// @since 1.1.1 apply paragraph span
setSpan(length, factory.paragraph(inTightList));
- if (!inTightList) {
+ if (hasNext(paragraph) && !inTightList) {
newLine();
-
if (blockQuoteIndent == 0) {
builder.append('\n');
}
@@ -518,4 +560,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
}
return out;
}
+
+ /**
+ * @since 2.0.0
+ */
+ protected static boolean hasNext(@NonNull Node node) {
+ return node.getNext() != null;
+ }
}
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 9f123307..047a0584 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
@@ -61,7 +61,7 @@ public class SpannableMarkdownVisitorTest {
}
// assert that the whole thing is processed
- assertEquals(stringBuilder.length(), index);
+ assertEquals("`" + stringBuilder + "`", stringBuilder.length(), index);
final Object[] spans = stringBuilder.getSpans(0, stringBuilder.length(), Object.class);
final int length = spans != null
diff --git a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java
index f6ad246e..59c000fe 100644
--- a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java
+++ b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java
@@ -5,7 +5,6 @@ import android.support.annotation.Nullable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
-import java.util.Arrays;
import java.util.Map;
import ix.Ix;
@@ -68,7 +67,10 @@ abstract class TestValidator {
}
}
- assertEquals(text, builder.subSequence(index, index + text.length()).toString());
+ assertEquals(
+ String.format("text: %s, position: {%d-%d}", text, index, index + text.length()),
+ text,
+ builder.subSequence(index, index + text.length()).toString());
return index + text.length();
}
@@ -106,6 +108,7 @@ abstract class TestValidator {
.filter(new IxPredicate() {
@Override
public boolean test(TestSpan testSpan) {
+
// in case of nested spans with the same name (lists)
// we also must validate attributes
// and thus we are moving most of assertions to this filter method
@@ -170,16 +173,21 @@ abstract class TestValidator {
spansText = "[]";
} else {
final StringBuilder builder = new StringBuilder();
- for (Object o: spans) {
+ for (Object o : spans) {
final TestSpan testSpan = (TestSpan) o;
if (builder.length() > 0) {
builder.append(", ");
}
+
builder
.append("{name: '").append(testSpan.name()).append('\'')
- .append(", position{").append(start).append(", ").append(end).append('}')
- .append(", attributes: ").append(testSpan.attributes())
- .append('}');
+ .append(", position{").append(start).append(", ").append(end).append('}');
+
+ if (testSpan.attributes().size() > 0) {
+ builder.append(", attributes: ").append(testSpan.attributes());
+ }
+
+ builder.append('}');
}
spansText = builder.toString();
}
diff --git a/markwon/src/test/resources/tests/code-blocks.yaml b/markwon/src/test/resources/tests/code-blocks.yaml
new file mode 100644
index 00000000..53b9bd50
--- /dev/null
+++ b/markwon/src/test/resources/tests/code-blocks.yaml
@@ -0,0 +1,17 @@
+input: |-
+ ```java
+ final String s = null;
+ ```
+ ```html
+
+ ```
+ ```
+ nothing here
+ ```
+
+output:
+ - code-block: "final String s = null;"
+ - "\n\n"
+ - code-block: ""
+ - "\n\n"
+ - code-block: "nothing here"
\ No newline at end of file
diff --git a/markwon/src/test/resources/tests/second.yaml b/markwon/src/test/resources/tests/second.yaml
index 747a645e..bd088dc2 100644
--- a/markwon/src/test/resources/tests/second.yaml
+++ b/markwon/src/test/resources/tests/second.yaml
@@ -21,8 +21,8 @@ output:
- text: " "
- s: "strike"
- text: " down\n\n"
- - blockquote: "Some quote here!\n"
- - text: "\n"
+ - blockquote: "Some quote here!"
+ - text: "\n\n"
- h1: "Header 1"
- text: "\n\n"
- h2: "Header 2"
diff --git a/markwon/src/test/resources/tests/ul-levels.yaml b/markwon/src/test/resources/tests/ul-levels.yaml
index 00708e17..a7a06c96 100644
--- a/markwon/src/test/resources/tests/ul-levels.yaml
+++ b/markwon/src/test/resources/tests/ul-levels.yaml
@@ -10,8 +10,8 @@ output:
- ul:
- ul: "Second"
level: 1
- - text: "\n"
level: 0
+ - text: "\n"
- ul:
- ul:
- ul: "Third"