diff --git a/markwon-html-parser-impl/src/main/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImpl.java b/markwon-html-parser-impl/src/main/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImpl.java
index 791147d2..86f985a2 100644
--- a/markwon-html-parser-impl/src/main/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImpl.java
+++ b/markwon-html-parser-impl/src/main/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImpl.java
@@ -119,9 +119,9 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
private boolean isInsidePreTag;
- private Tokeniser tokeniser;
-
- private CharacterReader reader;
+ // the thing is: we ensure a new line BEFORE block tag
+ // but not after, so another tag will be placed on the same line (which is wrong)
+ private boolean previousIsBlock;
MarkwonHtmlParserImpl(
@@ -242,6 +242,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag));
+ ensureNewLineIfPreviousWasBlock(output);
+
if (isVoidTag(name)
|| startTag.selfClosing) {
@@ -305,6 +307,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
if (isBlockTag(name)) {
isInsidePreTag = "pre".equals(name);
ensureNewLine(output);
+ } else {
+ ensureNewLineIfPreviousWasBlock(output);
}
final int start = output.length();
@@ -350,6 +354,11 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
block.closeAt(output.length());
+ // if it's empty -> we do no care about if it's block or not
+ if (!block.isEmpty()) {
+ previousIsBlock = isBlockTag(block.name);
+ }
+
if (TAG_PARAGRAPH.equals(name)) {
appendQuietly(output, '\n');
}
@@ -368,6 +377,7 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
if (isInsidePreTag) {
appendQuietly(output, character.getData());
} else {
+ ensureNewLineIfPreviousWasBlock(output);
trimmingAppender.append(output, character.getData());
}
}
@@ -410,6 +420,13 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
return blockTag;
}
+ protected void ensureNewLineIfPreviousWasBlock(@NonNull T output) {
+ if (previousIsBlock) {
+ ensureNewLine(output);
+ previousIsBlock = false;
+ }
+ }
+
// name here must lower case
protected static boolean isInlineTag(@NonNull String name) {
return INLINE_TAGS.contains(name);
diff --git a/markwon-html-parser-impl/src/test/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImplTest.java b/markwon-html-parser-impl/src/test/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImplTest.java
index 3edcfeb1..ad0669b3 100644
--- a/markwon-html-parser-impl/src/test/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImplTest.java
+++ b/markwon-html-parser-impl/src/test/java/ru/noties/markwon/html/impl/MarkwonHtmlParserImplTest.java
@@ -833,6 +833,30 @@ public class MarkwonHtmlParserImplTest {
});
}
+ @Test
+ public void newLineAfterBlockTag() {
+
+ final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
+ final StringBuilder output = new StringBuilder();
+
+ final String[] fragments = {
+ "head #1
just text",
+ "head #2
in span tag",
+ "head #3
in custom-tag"
+ };
+
+ for (String fragment: fragments) {
+ impl.processFragment(output, fragment);
+ }
+
+ final String expected = "" +
+ "head #1\njust text\n" +
+ "head #2\nin span tag\n" +
+ "head #3\nin custom-tag";
+
+ assertEquals(expected, output.toString());
+ }
+
private static class CaptureTagsAction implements MarkwonHtmlParser.FlushAction {
boolean called;
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..bd69445a 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
@@ -13,6 +13,7 @@ import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.MarkwonHtmlParser;
import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler;
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
+import ru.noties.markwon.renderer.html2.tag.HeadingHandler;
import ru.noties.markwon.renderer.html2.tag.ImageHandler;
import ru.noties.markwon.renderer.html2.tag.LinkHandler;
import ru.noties.markwon.renderer.html2.tag.ListHandler;
@@ -69,7 +70,13 @@ public abstract class MarkwonHtmlRenderer {
.handler("ul", listHandler)
.handler("ol", listHandler)
.handler("img", ImageHandler.create())
- .handler("blockquote", new BlockquoteHandler());
+ .handler("blockquote", new BlockquoteHandler())
+ .handler("h1", new HeadingHandler(1))
+ .handler("h2", new HeadingHandler(2))
+ .handler("h3", new HeadingHandler(3))
+ .handler("h4", new HeadingHandler(4))
+ .handler("h5", new HeadingHandler(5))
+ .handler("h6", new HeadingHandler(6));
}
@NonNull
diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/HeadingHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/HeadingHandler.java
new file mode 100644
index 00000000..e2138b05
--- /dev/null
+++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/HeadingHandler.java
@@ -0,0 +1,22 @@
+package ru.noties.markwon.renderer.html2.tag;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import ru.noties.markwon.SpannableConfiguration;
+import ru.noties.markwon.html.api.HtmlTag;
+
+public class HeadingHandler extends SimpleTagHandler {
+
+ private final int level;
+
+ public HeadingHandler(int level) {
+ this.level = level;
+ }
+
+ @Nullable
+ @Override
+ public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
+ return configuration.factory().heading(configuration.theme(), level);
+ }
+}
diff --git a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/LinkHandler.java b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/LinkHandler.java
index faa952bc..134874b9 100644
--- a/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/LinkHandler.java
+++ b/markwon/src/main/java/ru/noties/markwon/renderer/html2/tag/LinkHandler.java
@@ -11,7 +11,7 @@ public class LinkHandler extends SimpleTagHandler {
@Nullable
@Override
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
- final String destination = tag.attributes().get("src");
+ final String destination = tag.attributes().get("href");
if (!TextUtils.isEmpty(destination)) {
return configuration.factory().link(
configuration.theme(),
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 c47bccdf..ca9de641 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,15 +1,20 @@
package ru.noties.markwon.renderer.visitor;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.SpannableStringBuilder;
import org.commonmark.node.Node;
+import org.junit.ComparisonFailure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.annotation.Config;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
import ix.Ix;
import ix.IxPredicate;
@@ -55,13 +60,16 @@ public class SpannableMarkdownVisitorTest {
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
- System.out.printf("%n%s%n", stringBuilder);
+ System.out.printf("%s: %s%n", file, Arrays.toString(stringBuilder.getSpans(0, stringBuilder.length(), Object.class)));
int index = 0;
for (TestNode testNode : data.output()) {
index = validate(stringBuilder, index, testNode);
}
+
+ // assert that the whole thing is processed
+ assertEquals(stringBuilder.length(), index);
}
private int validate(@NonNull SpannableStringBuilder builder, int index, @NonNull TestNode node) {
@@ -103,6 +111,8 @@ public class SpannableMarkdownVisitorTest {
final String info = node.toString();
+ System.out.printf("%s: %s%n", file, builder.subSequence(index, out));
+
// we can possibly have parent spans here, should filter them
final Object[] spans = builder.getSpans(index, out, Object.class);
assertTrue(info, spans != null);
@@ -123,14 +133,18 @@ public class SpannableMarkdownVisitorTest {
})
.first(null);
- assertNotNull(info, testSpan);
+ assertNotNull(
+ format("info: %s, spans: %s", info, Arrays.toString(spans)),
+ testSpan
+ );
assertEquals(info, span.name(), testSpan.name());
- assertEquals(info, span.attributes(), testSpan.attributes());
+ assertMapEquals(info, span.attributes(), testSpan.attributes());
return out;
}
+ @SuppressWarnings("ConstantConditions")
@NonNull
private SpannableConfiguration configuration(@NonNull TestConfig config) {
@@ -147,4 +161,49 @@ public class SpannableMarkdownVisitorTest {
.factory(factory)
.build();
}
+
+ private static void assertMapEquals(
+ @NonNull String message,
+ @NonNull Map expected,
+ @NonNull Map actual) {
+ boolean result = true;
+ if (expected.size() == actual.size()) {
+ for (Map.Entry entry : expected.entrySet()) {
+ if (!actual.containsKey(entry.getKey())
+ || !equals(entry.getValue(), actual.get(entry.getKey()))) {
+ result = false;
+ break;
+ }
+ }
+ }
+ if (!result) {
+ final Comparator> comparator = new Comparator>() {
+ @Override
+ public int compare(Map.Entry o1, Map.Entry o2) {
+ return o1.getKey().compareTo(o2.getKey());
+ }
+ };
+ final String e = Ix.from(expected.entrySet())
+ .orderBy(comparator)
+ .toList()
+ .toString();
+ final String a = Ix.from(actual.entrySet())
+ .orderBy(comparator)
+ .toList()
+ .toString();
+ throw new ComparisonFailure(message, e, a);
+ }
+ }
+
+ private static boolean equals(@Nullable Object o1, @Nullable Object o2) {
+ return o1 != null
+ ? o1.equals(o2)
+ : o2 == null;
+
+ }
+
+ @NonNull
+ private static String format(@NonNull String message, Object... args) {
+ return String.format(message, args);
+ }
}
\ No newline at end of file
diff --git a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestFactory.java b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestFactory.java
index e87c6f34..89a0f646 100644
--- a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestFactory.java
+++ b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestFactory.java
@@ -186,7 +186,7 @@ class TestFactory implements SpannableFactory {
final int length = pairs.length;
final Map map = new HashMap<>(length);
for (Pair pair : pairs) {
- map.put(pair.key, String.valueOf(pair.value));
+ map.put(pair.key, pair.value == null ? null : String.valueOf(pair.value));
}
return map;
}
diff --git a/markwon/src/test/resources/tests/html.yaml b/markwon/src/test/resources/tests/html.yaml
new file mode 100644
index 00000000..d9f966f1
--- /dev/null
+++ b/markwon/src/test/resources/tests/html.yaml
@@ -0,0 +1,105 @@
+input: |-
+ html
+ emphasis
+ iemcitedfn
+ strong-emphasis
+ bstrong
+ super-script
+ sup
+ sub-script
+ sub
+ underline
+ uins
+ strike
+ sdel
+ link
+ a
+ unordered-list
+
+ ordered-list
+ - ol1
- ol2
+ image
+
+ blockquote
+ blockquote
+ 3
+ 4
+ 5
+ 6
+
+config:
+ use-html: true
+
+output:
+ - h1: "html"
+ - text: "\n"
+ - h2: "emphasis"
+ - text: "\n"
+ - i: "i"
+ - i: "em"
+ - i: "cite"
+ - i: "dfn"
+ - text: "\n"
+ - h2: "strong-emphasis"
+ - text: "\n"
+ - b: "b"
+ - b: "strong"
+ - text: "\n"
+ - h2: "super-script"
+ - text: "\n"
+ - sup: "sup"
+ - text: "\n"
+ - h2: "sub-script"
+ - text: "\n"
+ - sub: "sub"
+ - text: "\n"
+ - h2: "underline"
+ - text: "\n"
+ - u: "u"
+ - u: "ins"
+ - text: "\n"
+ - h2: "strike"
+ - text: "\n"
+ - s: "s"
+ - s: "del"
+ - text: "\n"
+ - h2: "link"
+ - text: "\n"
+ - a: "a"
+ attrs:
+ href: "a://href"
+ - text: "\n"
+ - h2: "unordered-list"
+ - text: "\n"
+ - ul: "ul1"
+ - text: "\n"
+ - ul: "ul2"
+ - text: "\n"
+ - h2: "ordered-list"
+ - text: "\n"
+ - ol: "ol1"
+ attrs:
+ start: 1
+ - text: "\n"
+ - ol: "ol2"
+ attrs:
+ start: 2
+ - text: "\n"
+ - h2: "image"
+ - text: "\n"
+ - img: "img"
+ attrs:
+ src: "img://src"
+ - text: "\n"
+ - h2: "blockquote"
+ - text: "\n"
+ - blockquote: "blockquote"
+ - text: "\n"
+ - h3: "3"
+ - text: "\n"
+ - h4: "4"
+ - text: "\n"
+ - h5: "5"
+ - text: "\n"
+ - h6: "6"
+
diff --git a/markwon/src/test/resources/tests/single-img.yaml b/markwon/src/test/resources/tests/single-img.yaml
index 4ff5a02c..fd5a1343 100644
--- a/markwon/src/test/resources/tests/single-img.yaml
+++ b/markwon/src/test/resources/tests/single-img.yaml
@@ -3,6 +3,6 @@ input: ""
output:
- img: "image"
attrs:
- scr: "#href"
+ src: "#href"
imageSize: null
replacementTextIsLink: false
\ No newline at end of file