From 286dd5410aa8185d073687de6224be622b187c58 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Sun, 23 Dec 2018 16:36:01 +0300 Subject: [PATCH] Adding javadoc documentation (work in progress) --- markwon-test-util/build.gradle | 13 --- .../java/ru/noties/markwon/test/TestUtil.java | 32 ------ markwon/build.gradle | 5 - .../noties/markwon/AbstractMarkwonPlugin.java | 47 +++++++- .../main/java/ru/noties/markwon/Markwon.java | 26 ++++- .../ru/noties/markwon/MarkwonBuilderImpl.java | 1 - .../java/ru/noties/markwon/MarkwonImpl.java | 16 ++- .../java/ru/noties/markwon/MarkwonPlugin.java | 108 +++++++++++++++++- .../noties/markwon/MarkwonSpansFactory.java | 29 ++++- .../markwon/MarkwonSpansFactoryImpl.java | 17 ++- .../ru/noties/markwon/MarkwonVisitor.java | 77 ++++++++++++- .../src/main/java/ru/noties/markwon/Prop.java | 4 +- .../java/ru/noties/markwon/RenderProps.java | 2 + .../ru/noties/markwon/RenderPropsImpl.java | 1 + .../ru/noties/markwon/core/CorePlugin.java | 21 ++-- .../ru/noties/markwon/core/MarkwonTheme.java | 33 ++++++ .../ru/noties/markwon/image/ImagesPlugin.java | 21 +++- .../markwon/core/suite/BaseSuiteTest.java | 18 +-- .../markwon/syntax/SyntaxHighlightTest.java | 7 +- settings.gradle | 1 - 20 files changed, 375 insertions(+), 104 deletions(-) delete mode 100644 markwon-test-util/build.gradle delete mode 100644 markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java diff --git a/markwon-test-util/build.gradle b/markwon-test-util/build.gradle deleted file mode 100644 index 3909ab27..00000000 --- a/markwon-test-util/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -apply plugin: 'java-library' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 - -dependencies { - - api deps['support-annotations'] - - deps['test'].with { - implementation it['commons-io'] - } -} \ No newline at end of file diff --git a/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java b/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java deleted file mode 100644 index 2ce00e1f..00000000 --- a/markwon-test-util/src/main/java/ru/noties/markwon/test/TestUtil.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.noties.markwon.test; - -import android.support.annotation.NonNull; - -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -public abstract class TestUtil { - - @NonNull - public static String read(@NonNull String path) { - try { - return IOUtils.resourceToString(path, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @NonNull - public static String read(@NonNull Object who, @NonNull String path) { - try { - return IOUtils.resourceToString(path, StandardCharsets.UTF_8, who.getClass().getClassLoader()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private TestUtil() { - } -} diff --git a/markwon/build.gradle b/markwon/build.gradle index 604519a2..8682d261 100644 --- a/markwon/build.gradle +++ b/markwon/build.gradle @@ -23,17 +23,12 @@ dependencies { deps['test'].with { testImplementation project(':markwon-test-span') - testImplementation project(':markwon-test-util') testImplementation it['junit'] testImplementation it['robolectric'] testImplementation it['mockito'] - // to remove after migration testImplementation it['ix-java'] - testImplementation it['jackson-yaml'] - testImplementation it['jackson-databind'] - testImplementation it['gson'] testImplementation it['commons-io'] } } diff --git a/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java b/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java index 28767521..d9849e5c 100644 --- a/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java +++ b/markwon/src/main/java/ru/noties/markwon/AbstractMarkwonPlugin.java @@ -1,6 +1,7 @@ package ru.noties.markwon; import android.support.annotation.NonNull; +import android.text.Spanned; import android.widget.TextView; import org.commonmark.node.Node; @@ -9,63 +10,107 @@ import org.commonmark.parser.Parser; import ru.noties.markwon.core.MarkwonTheme; import ru.noties.markwon.image.AsyncDrawableLoader; +/** + * Class that extends {@link MarkwonPlugin} with all methods implemented (empty body) + * for easier plugin implementation. Only required methods can be overriden + * + * @see MarkwonPlugin + * @since 3.0.0 + */ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { + + /** + * @inheritDoc + */ @Override public void configureParser(@NonNull Parser.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureTheme(@NonNull MarkwonTheme.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { } + /** + * @inheritDoc + */ @Override public void configureRenderProps(@NonNull RenderProps renderProps) { } + /** + * @inheritDoc + */ @NonNull @Override public String processMarkdown(@NonNull String markdown) { return markdown; } + /** + * @inheritDoc + */ @Override public void beforeRender(@NonNull Node node) { } + /** + * @inheritDoc + */ @Override public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) { } + /** + * @inheritDoc + */ @Override - public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { + public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { } + /** + * @inheritDoc + */ @Override public void afterSetText(@NonNull TextView textView) { diff --git a/markwon/src/main/java/ru/noties/markwon/Markwon.java b/markwon/src/main/java/ru/noties/markwon/Markwon.java index 6c4e4bea..c5a9ea8c 100644 --- a/markwon/src/main/java/ru/noties/markwon/Markwon.java +++ b/markwon/src/main/java/ru/noties/markwon/Markwon.java @@ -7,9 +7,20 @@ import android.widget.TextView; import org.commonmark.node.Node; +/** + * Class to parse and render markdown. Since version 3.0.0 instance specific (previously consisted + * of static stateless methods). An instance of builder can be obtained via {@link #builder(Context)} + * method. + * + * @see #builder(Context) + * @see Builder + */ public abstract class Markwon { /** + * Factory method to obtain an instance of {@link Builder} + * + * @see Builder * @since 3.0.0 */ @NonNull @@ -17,6 +28,14 @@ public abstract class Markwon { return new MarkwonBuilderImpl(context); } + /** + * Method to simply parse markdown (without rendering) + * + * @param input markdown input to parse + * @return parsed via commonmark-java org.commonmark.node.Node + * @see #render(Node) + * @since 3.0.0 + */ @NonNull public abstract Node parse(@NonNull String input); @@ -29,9 +48,14 @@ public abstract class Markwon { public abstract void setMarkdown(@NonNull TextView textView, @NonNull String markdown); - public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown); + public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull Spanned markdown); /** + * Builder for {@link Markwon}. + *

+ * Please note that the order in which plugins are supplied is important as this order will be + * used through the whole usage of built Markwon instance + * * @since 3.0.0 */ public interface Builder { diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java index 6098155a..1cf15c2c 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonBuilderImpl.java @@ -80,7 +80,6 @@ class MarkwonBuilderImpl implements Markwon.Builder { plugin.configureConfiguration(configurationBuilder); plugin.configureVisitor(visitorBuilder); plugin.configureSpansFactory(spanFactoryBuilder); - plugin.configureRenderProps(renderProps); } final MarkwonConfiguration configuration = configurationBuilder.build( diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java index 2114f050..8e2d8791 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonImpl.java @@ -9,6 +9,9 @@ import org.commonmark.parser.Parser; import java.util.List; +/** + * @since 3.0.0 + */ class MarkwonImpl extends Markwon { private final TextView.BufferType bufferType; @@ -31,6 +34,7 @@ class MarkwonImpl extends Markwon { @Override public Node parse(@NonNull String input) { + // make sure that all plugins are called `processMarkdown` before parsing for (MarkwonPlugin plugin : plugins) { input = plugin.processMarkdown(input); } @@ -42,7 +46,14 @@ class MarkwonImpl extends Markwon { @Override public Spanned render(@NonNull Node node) { + final RenderProps renderProps = visitor.renderProps(); + for (MarkwonPlugin plugin : plugins) { + + // let plugins apply render properties before rendering (as we will clear + // renderProps after rendering) + plugin.configureRenderProps(renderProps); + plugin.beforeRender(node); } @@ -52,6 +63,9 @@ class MarkwonImpl extends Markwon { plugin.afterRender(node, visitor); } + // clear render props after rending + renderProps.clearAll(); + return visitor.builder().spannableStringBuilder(); } @@ -67,7 +81,7 @@ class MarkwonImpl extends Markwon { } @Override - public void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown) { + public void setParsedMarkdown(@NonNull TextView textView, @NonNull Spanned markdown) { for (MarkwonPlugin plugin : plugins) { plugin.beforeSetText(textView, markdown); diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java b/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java index 910b044e..fd99b4dd 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonPlugin.java @@ -1,6 +1,7 @@ package ru.noties.markwon; import android.support.annotation.NonNull; +import android.text.Spanned; import android.widget.TextView; import org.commonmark.node.Node; @@ -8,38 +9,137 @@ import org.commonmark.parser.Parser; import ru.noties.markwon.core.MarkwonTheme; import ru.noties.markwon.image.AsyncDrawableLoader; +import ru.noties.markwon.image.MediaDecoder; +import ru.noties.markwon.image.SchemeHandler; /** + * Class represents a plugin (extension) to Markwon to configure how parsing and rendering + * of markdown is carried on. + * + * @see AbstractMarkwonPlugin + * @see ru.noties.markwon.core.CorePlugin + * @see ru.noties.markwon.image.ImagesPlugin * @since 3.0.0 */ public interface MarkwonPlugin { + /** + * Method to configure org.commonmark.parser.Parser (for example register custom + * extension, etc). + */ void configureParser(@NonNull Parser.Builder builder); + /** + * Modify {@link MarkwonTheme} that is used for rendering of markdown. + * + * @see MarkwonTheme + * @see MarkwonTheme.Builder + */ void configureTheme(@NonNull MarkwonTheme.Builder builder); + /** + * Configure image loading functionality. For example add new content-types + * {@link AsyncDrawableLoader.Builder#addMediaDecoder(String, MediaDecoder)}, a transport + * layer (network, file, etc) {@link AsyncDrawableLoader.Builder#addSchemeHandler(String, SchemeHandler)} + * or modify existing properties. + * + * @see AsyncDrawableLoader + * @see AsyncDrawableLoader.Builder + */ void configureImages(@NonNull AsyncDrawableLoader.Builder builder); + /** + * Configure {@link MarkwonConfiguration} + * + * @see MarkwonConfiguration + * @see MarkwonConfiguration.Builder + */ void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder); + /** + * Configure {@link MarkwonVisitor} to accept new node types or override already registered nodes. + * + * @see MarkwonVisitor + * @see MarkwonVisitor.Builder + */ void configureVisitor(@NonNull MarkwonVisitor.Builder builder); + /** + * Configure {@link MarkwonSpansFactory} to change what spans are used for certain node types. + * + * @see MarkwonSpansFactory + * @see MarkwonSpansFactory.Builder + */ void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder); // can be used to configure own properties and use between plugins + + /** + * A method to store some arbitrary data in {@link RenderProps}. Although it won\'t make + * much sense to use existing {@link Prop} keys for {@link SpanFactory}, it can be helpful + * to establish a communication channel between multiple plugins in decoupled way (provide + * some initial properties for example or indicate that certain plugin is registered). + *

+ * This method will be called before each rendering step (after rendering {@link RenderProps} + * will be cleared. This method won\'t be called during initialization stage. + * + * @see RenderProps + */ void configureRenderProps(@NonNull RenderProps renderProps); + /** + * Process input markdown and return new string to be used in parsing stage further. + * Can be described as pre-processing of markdown String. + * + * @param markdown String to process + * @return processed markdown String + */ @NonNull String processMarkdown(@NonNull String markdown); + /** + * This method will be called before rendering will occur thus making possible + * to post-process parsed node (make changes for example). + * + * @param node root parsed org.commonmark.node.Node + */ void beforeRender(@NonNull Node node); + /** + * This method will be called after rendering (but before applying markdown to a + * TextView, if such action will happen). It can be used to clean some + * internal state, or trigger certain action. Please note that modifying node won\'t + * have any effect as it has been already visited at this stage. + * + * @param node root parsed org.commonmark.node.Node + * @param visitor {@link MarkwonVisitor} instance used to render markdown + */ void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor); - void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown); + /** + * This method will be called before calling TextView#setText. + *

+ * It can be useful to prepare a TextView for markdown. For example {@link ru.noties.markwon.image.ImagesPlugin} + * uses this method to unregister previously registered {@link ru.noties.markwon.image.AsyncDrawableSpan} + * (if there are such spans in this TextView at this point). Or {@link ru.noties.markwon.core.CorePlugin} + * which measures ordered list numbers + * + * @param textView TextView to which markdown will be applied + * @param markdown Parsed markdown + */ + void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown); - // this method do not receive markdown like `beforeSetText` does because at this - // point TextView already has markdown set and to manipulate spans one must - // request them from TextView (getText()) + /** + * This method will be called after markdown was applied. + *

+ * It can be useful to trigger certain action on spans/textView. For example {@link ru.noties.markwon.image.ImagesPlugin} + * uses this method to register {@link ru.noties.markwon.image.AsyncDrawableSpan} and start + * asynchronously loading images. + *

+ * Unlike {@link #beforeSetText(TextView, Spanned)} this method does not receive parsed markdown + * as at this point spans must be queried by calling TextView#getText#getSpans. + * + * @param textView TextView to which markdown was applied + */ void afterSetText(@NonNull TextView textView); } diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java index 748ebd57..70545066 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactory.java @@ -6,27 +6,46 @@ import android.support.annotation.Nullable; import org.commonmark.node.Node; /** + * Class that controls what spans are used for certain Nodes. + * + * @see SpanFactory * @since 3.0.0 */ public interface MarkwonSpansFactory { + /** + * Returns registered {@link SpanFactory} or null if a factory for this node type + * is not registered. There is {@link #require(Class)} method that will throw an exception + * if required {@link SpanFactory} is not registered, thus making return type non-null + * + * @param node type of the node + * @return registered {@link SpanFactory} or null if it\'s not registered + * @see #require(Class) + */ @Nullable - F get(@NonNull Class node); + SpanFactory get(@NonNull Class node); + /** + * @see #get(Class) + * @see #require(Node) + */ @Nullable - F get(@NonNull N node); + SpanFactory get(@NonNull N node); @NonNull - F require(@NonNull Class node); + SpanFactory require(@NonNull Class node); + /** + * @see #require(Class) + */ @NonNull - F require(@NonNull N node); + SpanFactory require(@NonNull N node); interface Builder { @NonNull - Builder setFactory(@NonNull Class node, @NonNull F factory); + Builder setFactory(@NonNull Class node, @NonNull SpanFactory factory); @NonNull MarkwonSpansFactory build(); diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java index 7bbc8ca8..81b5a0c3 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonSpansFactoryImpl.java @@ -22,21 +22,20 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory { @Nullable @Override - public F get(@NonNull Class node) { - //noinspection unchecked - return (F) factories.get(node); + public SpanFactory get(@NonNull Class node) { + return factories.get(node); } @Nullable @Override - public F get(@NonNull N node) { + public SpanFactory get(@NonNull N node) { return get(node.getClass()); } @NonNull @Override - public F require(@NonNull Class node) { - final F f = get(node); + public SpanFactory require(@NonNull Class node) { + final SpanFactory f = get(node); if (f == null) { throw new NullPointerException(); } @@ -45,8 +44,8 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory { @NonNull @Override - public F require(@NonNull N node) { - final F f = get(node); + public SpanFactory require(@NonNull N node) { + final SpanFactory f = get(node); if (f == null) { throw new NullPointerException(); } @@ -60,7 +59,7 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory { @NonNull @Override - public Builder setFactory(@NonNull Class node, @NonNull F factory) { + public Builder setFactory(@NonNull Class node, @NonNull SpanFactory factory) { factories.put(node, factory); return this; } diff --git a/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java b/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java index 81d3138f..aec63444 100644 --- a/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java +++ b/markwon/src/main/java/ru/noties/markwon/MarkwonVisitor.java @@ -7,16 +7,29 @@ import org.commonmark.node.Node; import org.commonmark.node.Visitor; /** + * Configurable visitor of parsed markdown. Allows visiting certain (registered) nodes without + * need to create own instance of this class. + * + * @see Builder#on(Class, NodeVisitor) + * @see MarkwonPlugin#configureVisitor(Builder) * @since 3.0.0 */ public interface MarkwonVisitor extends Visitor { + /** + * @see Builder#on(Class, NodeVisitor) + */ interface NodeVisitor { void visit(@NonNull MarkwonVisitor visitor, @NonNull N n); } interface Builder { + /** + * @param node to register + * @param nodeVisitor {@link NodeVisitor} to be used or null to ignore previously registered + * visitor for this node + */ @NonNull Builder on(@NonNull Class node, @Nullable NodeVisitor nodeVisitor); @@ -33,26 +46,86 @@ public interface MarkwonVisitor extends Visitor { @NonNull SpannableBuilder builder(); + /** + * Visits all children of supplied node. + * + * @param node to visit + */ void visitChildren(@NonNull Node node); + /** + * Executes a check if there is further content available. + * + * @param node to check + * @return boolean indicating if there are more nodes after supplied one + */ boolean hasNext(@NonNull Node node); + /** + * This method ensures that further content will start at a new line. If current + * last character is already a new line, then it won\'t do anything. + */ void ensureNewLine(); + /** + * This method inserts a new line without any condition checking (unlike {@link #ensureNewLine()}). + */ void forceNewLine(); + /** + * Helper method to call builder().length() + * + * @return current length of underlying {@link SpannableBuilder} + */ int length(); + /** + * Sets spans to underlying {@link SpannableBuilder} from start + * to {@link SpannableBuilder#length()}. + * + * @param start start position of spans + * @param spans to apply + */ void setSpans(int start, @Nullable Object spans); - // will automatically obtain SpanFactory instance and use it, it no SpanFactory is registered, - // will throw, if not desired use setSpansForNodeOptional + /** + * Helper method to obtain and apply spans for supplied Node. Internally queries {@link SpanFactory} + * for the node (via {@link MarkwonSpansFactory#require(Node)} thus throwing an exception + * if there is no {@link SpanFactory} registered for the node). + * + * @param node to retrieve {@link SpanFactory} for + * @param start start position for further {@link #setSpans(int, Object)} call + * @see #setSpansForNodeOptional(Node, int) + */ void setSpansForNode(@NonNull N node, int start); + /** + * The same as {@link #setSpansForNode(Node, int)} but can be used in situations when there is + * no access to a Node instance (for example in HTML rendering which doesn\'t have markdown Nodes). + * + * @see #setSpansForNode(Node, int) + */ void setSpansForNode(@NonNull Class node, int start); // does not throw if there is no SpanFactory registered for this node + + /** + * Helper method to apply spans from a {@link SpanFactory} if it\'s registered in + * {@link MarkwonSpansFactory} instance. Otherwise ignores this call (no spans will be applied). + * If there is a need to ensure that specified node has a {@link SpanFactory} registered, + * then {@link #setSpansForNode(Node, int)} can be used. {@link #setSpansForNode(Node, int)} internally + * uses {@link MarkwonSpansFactory#require(Node)}. This method uses {@link MarkwonSpansFactory#get(Node)}. + * + * @see #setSpansForNode(Node, int) + */ void setSpansForNodeOptional(@NonNull N node, int start); + /** + * The same as {@link #setSpansForNodeOptional(Node, int)} but can be used in situations when + * there is no access to a Node instance (for example in HTML rendering). + * + * @see #setSpansForNodeOptional(Node, int) + */ + @SuppressWarnings("unused") void setSpansForNodeOptional(@NonNull Class node, int start); } diff --git a/markwon/src/main/java/ru/noties/markwon/Prop.java b/markwon/src/main/java/ru/noties/markwon/Prop.java index a930288f..caacbfab 100644 --- a/markwon/src/main/java/ru/noties/markwon/Prop.java +++ b/markwon/src/main/java/ru/noties/markwon/Prop.java @@ -4,9 +4,11 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; /** - * Class to hold data in {@link RenderProps} + * Class to hold data in {@link RenderProps}. Represents a certain property. * * @param represents the type that this instance holds + * @see #of(String) + * @see #of(Class, String) * @since 3.0.0 */ public final class Prop { diff --git a/markwon/src/main/java/ru/noties/markwon/RenderProps.java b/markwon/src/main/java/ru/noties/markwon/RenderProps.java index b939acc4..e28edbaf 100644 --- a/markwon/src/main/java/ru/noties/markwon/RenderProps.java +++ b/markwon/src/main/java/ru/noties/markwon/RenderProps.java @@ -17,4 +17,6 @@ public interface RenderProps { void set(@NonNull Prop prop, @Nullable T value); void clear(@NonNull Prop prop); + + void clearAll(); } diff --git a/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java b/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java index 81605ee9..75b6ac4b 100644 --- a/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java +++ b/markwon/src/main/java/ru/noties/markwon/RenderPropsImpl.java @@ -45,6 +45,7 @@ public class RenderPropsImpl implements RenderProps { values.remove(prop); } + @Override public void clearAll() { values.clear(); } diff --git a/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java b/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java index 47024866..187f0173 100644 --- a/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java +++ b/markwon/src/main/java/ru/noties/markwon/core/CorePlugin.java @@ -3,6 +3,7 @@ package ru.noties.markwon.core; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; +import android.text.Spanned; import android.widget.TextView; import org.commonmark.node.BlockQuote; @@ -104,7 +105,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { } @Override - public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { + public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { OrderedListItemSpan.measure(textView, markdown); } @@ -123,7 +124,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) { final int length = visitor.length(); visitor.visitChildren(strongEmphasis); - visitor.setSpansForNode(strongEmphasis, length); + visitor.setSpansForNodeOptional(strongEmphasis, length); } }); } @@ -134,7 +135,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) { final int length = visitor.length(); visitor.visitChildren(emphasis); - visitor.setSpansForNode(emphasis, length); + visitor.setSpansForNodeOptional(emphasis, length); } }); } @@ -149,7 +150,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { final int length = visitor.length(); visitor.visitChildren(blockQuote); - visitor.setSpansForNode(blockQuote, length); + visitor.setSpansForNodeOptional(blockQuote, length); if (visitor.hasNext(blockQuote)) { visitor.ensureNewLine(); @@ -173,7 +174,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { .append(code.getLiteral()) .append('\u00a0'); - visitor.setSpansForNode(code, length); + visitor.setSpansForNodeOptional(code, length); } }); } @@ -215,7 +216,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { visitor.builder().append('\u00a0'); - visitor.setSpansForNode(node, length); + visitor.setSpansForNodeOptional(node, length); if (visitor.hasNext(node)) { visitor.ensureNewLine(); @@ -260,7 +261,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { CoreProps.BULLET_LIST_ITEM_LEVEL.set(visitor.renderProps(), listLevel(listItem)); } - visitor.setSpansForNode(listItem, length); + visitor.setSpansForNodeOptional(listItem, length); if (visitor.hasNext(listItem)) { visitor.ensureNewLine(); @@ -293,7 +294,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { // without space it won't render visitor.builder().append('\u00a0'); - visitor.setSpansForNode(thematicBreak, length); + visitor.setSpansForNodeOptional(thematicBreak, length); if (visitor.hasNext(thematicBreak)) { visitor.ensureNewLine(); @@ -315,7 +316,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { CoreProps.HEADING_LEVEL.set(visitor.renderProps(), heading.getLevel()); - visitor.setSpansForNode(heading, length); + visitor.setSpansForNodeOptional(heading, length); if (visitor.hasNext(heading)) { visitor.ensureNewLine(); @@ -399,7 +400,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination); - visitor.setSpansForNode(link, length); + visitor.setSpansForNodeOptional(link, length); } }); } diff --git a/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java b/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java index 347a2321..908e200f 100644 --- a/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java +++ b/markwon/src/main/java/ru/noties/markwon/core/MarkwonTheme.java @@ -16,6 +16,22 @@ import java.util.Locale; import ru.noties.markwon.utils.ColorUtils; import ru.noties.markwon.utils.Dip; +/** + * Class to hold theming information for rending of markdown. + *

+ * Since version 3.0.0 this class should be considered as CoreTheme as it\'s + * information holds data for core features only. But based on this other components can still use it + * to display markdown consistently. + *

+ * Since version 3.0.0 this class should not be instantiated manually. Instead a {@link ru.noties.markwon.MarkwonPlugin} + * should be used: {@link ru.noties.markwon.MarkwonPlugin#configureTheme(Builder)} + *

+ * Since version 3.0.0 properties related to strike-through, tables and HTML + * are moved to specific plugins in independent artifacts + * + * @see CorePlugin + * @see ru.noties.markwon.MarkwonPlugin#configureTheme(Builder) + */ @SuppressWarnings("WeakerAccess") public class MarkwonTheme { @@ -41,12 +57,28 @@ public class MarkwonTheme { * @see #builderWithDefaults(Context) * @see #builder(MarkwonTheme) * @since 1.0.0 + * @deprecated 3.0.0 */ @NonNull + @Deprecated public static Builder builder() { return new Builder(); } + /** + * Create an empty instance of {@link Builder} with no default values applied + *

+ * Since version 3.0.0 manual construction of {@link MarkwonTheme} is not required, instead a + * {@link ru.noties.markwon.MarkwonPlugin#configureTheme(Builder)} should be used in order + * to change certain theme properties + * + * @since 3.0.0 + */ + @NonNull + public static Builder builderNoDefaults() { + return new Builder(); + } + /** * Factory method to create a {@link Builder} instance and initialize it with values * from supplied {@link MarkwonTheme} @@ -549,6 +581,7 @@ public class MarkwonTheme { * @return self * @since 1.1.0 */ + @SuppressWarnings("UnusedReturnValue") @NonNull public Builder headingTextSizeMultipliers(@Size(6) @NonNull float[] headingTextSizeMultipliers) { this.headingTextSizeMultipliers = headingTextSizeMultipliers; diff --git a/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java b/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java index 7e91f288..dc51504f 100644 --- a/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java +++ b/markwon/src/main/java/ru/noties/markwon/image/ImagesPlugin.java @@ -2,6 +2,7 @@ package ru.noties.markwon.image; import android.content.Context; import android.support.annotation.NonNull; +import android.text.Spanned; import android.widget.TextView; import org.commonmark.node.Image; @@ -15,6 +16,7 @@ import ru.noties.markwon.MarkwonConfiguration; import ru.noties.markwon.MarkwonSpansFactory; import ru.noties.markwon.MarkwonVisitor; import ru.noties.markwon.RenderProps; +import ru.noties.markwon.SpanFactory; import ru.noties.markwon.image.data.DataUriSchemeHandler; import ru.noties.markwon.image.file.FileSchemeHandler; import ru.noties.markwon.image.network.NetworkSchemeHandler; @@ -68,6 +70,13 @@ public class ImagesPlugin extends AbstractMarkwonPlugin { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) { + // if there is no image spanFactory, ignore + final SpanFactory spanFactory = visitor.configuration().spansFactory().get(image); + if (spanFactory == null) { + visitor.visitChildren(image); + return; + } + final int length = visitor.length(); visitor.visitChildren(image); @@ -86,22 +95,22 @@ public class ImagesPlugin extends AbstractMarkwonPlugin { .urlProcessor() .process(image.getDestination()); - final RenderProps context = visitor.renderProps(); + final RenderProps props = visitor.renderProps(); // apply image properties // Please note that we explicitly set IMAGE_SIZE to null as we do not clear // properties after we applied span (we could though) - ImageProps.DESTINATION.set(context, destination); - ImageProps.REPLACEMENT_TEXT_IS_LINK.set(context, link); - ImageProps.IMAGE_SIZE.set(context, null); + ImageProps.DESTINATION.set(props, destination); + ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link); + ImageProps.IMAGE_SIZE.set(props, null); - visitor.setSpansForNode(image, length); + visitor.setSpans(length, spanFactory.getSpans(configuration, props)); } }); } @Override - public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { + public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { AsyncDrawableScheduler.unschedule(textView); } diff --git a/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java b/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java index f91f2f13..98734db7 100644 --- a/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java +++ b/markwon/src/test/java/ru/noties/markwon/core/suite/BaseSuiteTest.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.Spanned; +import org.apache.commons.io.IOUtils; import org.commonmark.node.BlockQuote; import org.commonmark.node.Code; import org.commonmark.node.Emphasis; @@ -18,7 +19,8 @@ import org.commonmark.node.StrongEmphasis; import org.commonmark.node.ThematicBreak; import org.robolectric.RuntimeEnvironment; -import java.util.Collections; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -32,7 +34,6 @@ import ru.noties.markwon.core.CorePlugin; import ru.noties.markwon.core.CoreProps; import ru.noties.markwon.test.TestSpan; import ru.noties.markwon.test.TestSpanMatcher; -import ru.noties.markwon.test.TestUtil; import static ru.noties.markwon.test.TestSpan.args; import static ru.noties.markwon.test.TestSpan.span; @@ -62,7 +63,11 @@ abstract class BaseSuiteTest { @NonNull private String read(@NonNull String name) { - return TestUtil.read(this, "tests/" + name); + try { + return IOUtils.resourceToString("tests/" + name, StandardCharsets.UTF_8, getClass().getClassLoader()); + } catch (IOException e) { + throw new RuntimeException(e); + } } @NonNull @@ -160,12 +165,7 @@ abstract class BaseSuiteTest { @Nullable @Override public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { - return span(name, extractArgs(props)); - } - - @NonNull - Map extractArgs(@NonNull RenderProps props) { - return Collections.emptyMap(); + return span(name); } } } diff --git a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java index 732a182f..7e0a2af2 100644 --- a/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java +++ b/markwon/src/test/java/ru/noties/markwon/syntax/SyntaxHighlightTest.java @@ -13,6 +13,7 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -74,7 +75,7 @@ public class SyntaxHighlightTest { }; final MarkwonSpansFactory spansFactory = mock(MarkwonSpansFactory.class); - when(spansFactory.require(any(FencedCodeBlock.class))).thenReturn(new SpanFactory() { + when(spansFactory.get(any(FencedCodeBlock.class))).thenReturn(new SpanFactory() { @Override public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { return codeSpan; @@ -103,7 +104,7 @@ public class SyntaxHighlightTest { final FencedCodeBlock fencedCodeBlock = new FencedCodeBlock(); fencedCodeBlock.setLiteral("{code}"); - CorePluginBridge.visitCodeBlock(visitor, null, "{code}", fencedCodeBlock); + CorePluginBridge.visitCodeBlock(visitor, null, fencedCodeBlock.getLiteral(), fencedCodeBlock); final int end = builder.length(); @@ -115,7 +116,7 @@ public class SyntaxHighlightTest { // each character + code span final int length = fencedCodeBlock.getLiteral().length() + 1; - assertEquals(length, spans.length); + assertEquals(Arrays.toString(spans), length, spans.length); assertEquals(codeSpan, spans[0]); // each character diff --git a/settings.gradle b/settings.gradle index ede0753a..d5f56c45 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,6 +11,5 @@ include ':app', ':markwon-html', ':markwon-view', ':markwon-test-span', - ':markwon-test-util', ':sample-custom-extension', ':sample-latex-math'