Adding javadoc documentation (work in progress)
This commit is contained in:
		
							parent
							
								
									ef15ee4bd2
								
							
						
					
					
						commit
						286dd5410a
					
				| @ -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'] | ||||
|     } | ||||
| } | ||||
| @ -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() { | ||||
|     } | ||||
| } | ||||
| @ -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'] | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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) { | ||||
| 
 | ||||
|  | ||||
| @ -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 <code>org.commonmark.node.Node</code> | ||||
|      * @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}. | ||||
|      * <p> | ||||
|      * 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 { | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 <code>org.commonmark.parser.Parser</code> (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). | ||||
|      * <p> | ||||
|      * This method will be called before <em>each</em> rendering step (after rendering {@link RenderProps} | ||||
|      * will be cleared. This method <strong>won\'t</strong> 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 <code>pre-processing</code> of markdown String. | ||||
|      * | ||||
|      * @param markdown String to process | ||||
|      * @return processed markdown String | ||||
|      */ | ||||
|     @NonNull | ||||
|     String processMarkdown(@NonNull String markdown); | ||||
| 
 | ||||
|     /** | ||||
|      * This method will be called <strong>before</strong> rendering will occur thus making possible | ||||
|      * to <code>post-process</code> parsed node (make changes for example). | ||||
|      * | ||||
|      * @param node root parsed org.commonmark.node.Node | ||||
|      */ | ||||
|     void beforeRender(@NonNull Node node); | ||||
| 
 | ||||
|     /** | ||||
|      * This method will be called <strong>after</strong> 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 <code>node</code> won\'t | ||||
|      * have any effect as it has been already <i>visited</i> 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 <strong>before</strong> calling <code>TextView#setText</code>. | ||||
|      * <p> | ||||
|      * 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 <code>markdown</code> 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 <strong>after</strong> markdown was applied. | ||||
|      * <p> | ||||
|      * 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. | ||||
|      * <p> | ||||
|      * Unlike {@link #beforeSetText(TextView, Spanned)} this method does not receive parsed markdown | ||||
|      * as at this point spans must be queried by calling <code>TextView#getText#getSpans</code>. | ||||
|      * | ||||
|      * @param textView TextView to which markdown was applied | ||||
|      */ | ||||
|     void afterSetText(@NonNull TextView textView); | ||||
| } | ||||
|  | ||||
| @ -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 <code>null</code> 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 <code>non-null</code> | ||||
|      * | ||||
|      * @param node type of the node | ||||
|      * @return registered {@link SpanFactory} or null if it\'s not registered | ||||
|      * @see #require(Class) | ||||
|      */ | ||||
|     @Nullable | ||||
|     <N extends Node, F extends SpanFactory> F get(@NonNull Class<N> node); | ||||
|     <N extends Node> SpanFactory get(@NonNull Class<N> node); | ||||
| 
 | ||||
|     /** | ||||
|      * @see #get(Class) | ||||
|      * @see #require(Node) | ||||
|      */ | ||||
|     @Nullable | ||||
|     <N extends Node, F extends SpanFactory> F get(@NonNull N node); | ||||
|     <N extends Node> SpanFactory get(@NonNull N node); | ||||
| 
 | ||||
|     @NonNull | ||||
|     <N extends Node, F extends SpanFactory> F require(@NonNull Class<N> node); | ||||
|     <N extends Node> SpanFactory require(@NonNull Class<N> node); | ||||
| 
 | ||||
|     /** | ||||
|      * @see #require(Class) | ||||
|      */ | ||||
|     @NonNull | ||||
|     <N extends Node, F extends SpanFactory> F require(@NonNull N node); | ||||
|     <N extends Node> SpanFactory require(@NonNull N node); | ||||
| 
 | ||||
| 
 | ||||
|     interface Builder { | ||||
| 
 | ||||
|         @NonNull | ||||
|         <N extends Node, F extends SpanFactory> Builder setFactory(@NonNull Class<N> node, @NonNull F factory); | ||||
|         <N extends Node> Builder setFactory(@NonNull Class<N> node, @NonNull SpanFactory factory); | ||||
| 
 | ||||
|         @NonNull | ||||
|         MarkwonSpansFactory build(); | ||||
|  | ||||
| @ -22,21 +22,20 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory { | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public <N extends Node, F extends SpanFactory> F get(@NonNull Class<N> node) { | ||||
|         //noinspection unchecked | ||||
|         return (F) factories.get(node); | ||||
|     public <N extends Node> SpanFactory get(@NonNull Class<N> node) { | ||||
|         return factories.get(node); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public <N extends Node, F extends SpanFactory> F get(@NonNull N node) { | ||||
|     public <N extends Node> SpanFactory get(@NonNull N node) { | ||||
|         return get(node.getClass()); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public <N extends Node, F extends SpanFactory> F require(@NonNull Class<N> node) { | ||||
|         final F f = get(node); | ||||
|     public <N extends Node> SpanFactory require(@NonNull Class<N> node) { | ||||
|         final SpanFactory f = get(node); | ||||
|         if (f == null) { | ||||
|             throw new NullPointerException(); | ||||
|         } | ||||
| @ -45,8 +44,8 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory { | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public <N extends Node, F extends SpanFactory> F require(@NonNull N node) { | ||||
|         final F f = get(node); | ||||
|     public <N extends Node> 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 <N extends Node, F extends SpanFactory> Builder setFactory(@NonNull Class<N> node, @NonNull F factory) { | ||||
|         public <N extends Node> Builder setFactory(@NonNull Class<N> node, @NonNull SpanFactory factory) { | ||||
|             factories.put(node, factory); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
| @ -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<N extends Node> { | ||||
|         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 | ||||
|         <N extends Node> Builder on(@NonNull Class<N> node, @Nullable NodeVisitor<? super N> 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 <strong>ensures</strong> 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 <code>builder().length()</code> | ||||
|      * | ||||
|      * @return current length of underlying {@link SpannableBuilder} | ||||
|      */ | ||||
|     int length(); | ||||
| 
 | ||||
|     /** | ||||
|      * Sets <code>spans</code> to underlying {@link SpannableBuilder} from <em>start</em> | ||||
|      * to <em>{@link SpannableBuilder#length()}</em>. | ||||
|      * | ||||
|      * @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) | ||||
|      */ | ||||
|     <N extends Node> 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) | ||||
|      */ | ||||
|     <N extends Node> void setSpansForNode(@NonNull Class<N> node, int start); | ||||
| 
 | ||||
|     // does not throw if there is no SpanFactory registered for this node | ||||
| 
 | ||||
|     /** | ||||
|      * Helper method to apply spans from a {@link SpanFactory} <b>if</b> 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 <code>node</code> 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) | ||||
|      */ | ||||
|     <N extends Node> 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") | ||||
|     <N extends Node> void setSpansForNodeOptional(@NonNull Class<N> node, int start); | ||||
| } | ||||
|  | ||||
| @ -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 <em>property</em>. | ||||
|  * | ||||
|  * @param <T> represents the type that this instance holds | ||||
|  * @see #of(String) | ||||
|  * @see #of(Class, String) | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| public final class Prop<T> { | ||||
|  | ||||
| @ -17,4 +17,6 @@ public interface RenderProps { | ||||
|     <T> void set(@NonNull Prop<T> prop, @Nullable T value); | ||||
| 
 | ||||
|     <T> void clear(@NonNull Prop<T> prop); | ||||
| 
 | ||||
|     void clearAll(); | ||||
| } | ||||
|  | ||||
| @ -45,6 +45,7 @@ public class RenderPropsImpl implements RenderProps { | ||||
|         values.remove(prop); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clearAll() { | ||||
|         values.clear(); | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -16,6 +16,22 @@ import java.util.Locale; | ||||
| import ru.noties.markwon.utils.ColorUtils; | ||||
| import ru.noties.markwon.utils.Dip; | ||||
| 
 | ||||
| /** | ||||
|  * Class to hold <i>theming</i> information for rending of markdown. | ||||
|  * <p> | ||||
|  * Since version 3.0.0 this class should be considered as <em>CoreTheme</em> as it\'s | ||||
|  * information holds data for core features only. But based on this other components can still use it | ||||
|  * to display markdown consistently. | ||||
|  * <p> | ||||
|  * 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)} | ||||
|  * <p> | ||||
|  * Since version 3.0.0 properties related to <em>strike-through</em>, <em>tables</em> and <em>HTML</em> | ||||
|  * 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 <strong>empty</strong> instance of {@link Builder} with no default values applied | ||||
|      * <p> | ||||
|      * 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; | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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<String, Object> extractArgs(@NonNull RenderProps props) { | ||||
|       return Collections.emptyMap(); | ||||
|       return span(name); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -11,6 +11,5 @@ include ':app', | ||||
|         ':markwon-html', | ||||
|         ':markwon-view', | ||||
|         ':markwon-test-span', | ||||
|         ':markwon-test-util', | ||||
|         ':sample-custom-extension', | ||||
|         ':sample-latex-math' | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov