Adding javadoc documentation (work in progress)

This commit is contained in:
Dimitry Ivanov 2018-12-23 16:36:01 +03:00
parent ef15ee4bd2
commit 286dd5410a
20 changed files with 375 additions and 104 deletions

View File

@ -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']
}
}

View File

@ -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() {
}
}

View File

@ -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']
}
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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(

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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> {

View File

@ -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();
}

View File

@ -45,6 +45,7 @@ public class RenderPropsImpl implements RenderProps {
values.remove(prop);
}
@Override
public void clearAll() {
values.clear();
}

View File

@ -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);
}
});
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -11,6 +11,5 @@ include ':app',
':markwon-html',
':markwon-view',
':markwon-test-span',
':markwon-test-util',
':sample-custom-extension',
':sample-latex-math'