Add reducer and nodeRenderer
This commit is contained in:
parent
12ef0b5703
commit
128612d5b2
@ -19,7 +19,7 @@
|
||||
@click="selectAll"
|
||||
>
|
||||
<div class="selected-artifact-script">
|
||||
<span class="token keyword">final def</span> markwon_version = <span class="token string">'latest_version'</span>
|
||||
<span class="token keyword">final def</span> markwon_version = <span class="token string">'{{latestVersion}}'</span>
|
||||
</div>
|
||||
<br>
|
||||
<div class="selected-artifact-script" v-for="artifact in selectedArtifacts">
|
||||
@ -42,7 +42,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
artifacts,
|
||||
selected: ['core']
|
||||
selected: ['core'],
|
||||
latestVersion: 'latest_version'
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -15,8 +15,8 @@ Markwon.builder(context)
|
||||
|
||||
:::danger
|
||||
Customizing `MarkwonHtmlRenderer` is not enough to include HTML content in your application.
|
||||
You must explicitly include [markwon-html](/docs/v3/html/) artifact (include HtmlParser)
|
||||
to you project and register `HtmlPlugin`:
|
||||
You must explicitly include [markwon-html](/docs/v3/html/) artifact (includes HtmlParser)
|
||||
to your project and register `HtmlPlugin`:
|
||||
|
||||
```java
|
||||
Markwon.builder(context)
|
||||
|
@ -0,0 +1,184 @@
|
||||
package ru.noties.markwon;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public abstract class MarkwonNodeRenderer {
|
||||
|
||||
public interface ViewProvider<N extends Node> {
|
||||
|
||||
/**
|
||||
* Please note that you should not attach created View to specified group. It will be done
|
||||
* automatically.
|
||||
*/
|
||||
@NonNull
|
||||
View provide(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@NonNull ViewGroup group,
|
||||
@NonNull Markwon markwon,
|
||||
@NonNull N n);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Builder builder(@NonNull ViewProvider<Node> defaultViewProvider) {
|
||||
return new Builder(defaultViewProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultViewProviderLayoutResId layout resource id to be used in default view provider
|
||||
* @param defaultViewProviderTextViewId id of a TextView in specified layout
|
||||
* @return Builder
|
||||
* @see SimpleTextViewProvider
|
||||
*/
|
||||
@NonNull
|
||||
public static Builder builder(
|
||||
@LayoutRes int defaultViewProviderLayoutResId,
|
||||
@IdRes int defaultViewProviderTextViewId) {
|
||||
return new Builder(new SimpleTextViewProvider(
|
||||
defaultViewProviderLayoutResId,
|
||||
defaultViewProviderTextViewId));
|
||||
}
|
||||
|
||||
public abstract void render(@NonNull ViewGroup group, @NonNull Markwon markwon, @NonNull String markdown);
|
||||
|
||||
public abstract void render(@NonNull ViewGroup group, @NonNull Markwon markwon, @NonNull Node root);
|
||||
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final ViewProvider<Node> defaultViewProvider;
|
||||
|
||||
private MarkwonReducer reducer;
|
||||
private Map<Class<? extends Node>, ViewProvider<Node>> viewProviders;
|
||||
private LayoutInflater inflater;
|
||||
|
||||
public Builder(@NonNull ViewProvider<Node> defaultViewProvider) {
|
||||
this.defaultViewProvider = defaultViewProvider;
|
||||
this.viewProviders = new HashMap<>(3);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder reducer(@NonNull MarkwonReducer reducer) {
|
||||
this.reducer = reducer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public <N extends Node> Builder viewProvider(
|
||||
@NonNull Class<N> type,
|
||||
@NonNull ViewProvider<? super N> viewProvider) {
|
||||
//noinspection unchecked
|
||||
viewProviders.put(type, (ViewProvider<Node>) viewProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder inflater(@NonNull LayoutInflater inflater) {
|
||||
this.inflater = inflater;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MarkwonNodeRenderer build() {
|
||||
if (reducer == null) {
|
||||
reducer = MarkwonReducer.directChildren();
|
||||
}
|
||||
return new Impl(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleTextViewProvider implements ViewProvider<Node> {
|
||||
|
||||
private final int layoutResId;
|
||||
private final int textViewId;
|
||||
|
||||
public SimpleTextViewProvider(@LayoutRes int layoutResId, @IdRes int textViewId) {
|
||||
this.layoutResId = layoutResId;
|
||||
this.textViewId = textViewId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View provide(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@NonNull ViewGroup group,
|
||||
@NonNull Markwon markwon,
|
||||
@NonNull Node node) {
|
||||
final View view = inflater.inflate(layoutResId, group, false);
|
||||
final TextView textView = view.findViewById(textViewId);
|
||||
markwon.setParsedMarkdown(textView, markwon.render(node));
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
static class Impl extends MarkwonNodeRenderer {
|
||||
|
||||
private final MarkwonReducer reducer;
|
||||
private final Map<Class<? extends Node>, ViewProvider<Node>> viewProviders;
|
||||
private final ViewProvider<Node> defaultViewProvider;
|
||||
|
||||
private LayoutInflater inflater;
|
||||
|
||||
Impl(@NonNull Builder builder) {
|
||||
this.reducer = builder.reducer;
|
||||
this.viewProviders = builder.viewProviders;
|
||||
this.defaultViewProvider = builder.defaultViewProvider;
|
||||
this.inflater = builder.inflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NonNull ViewGroup group, @NonNull Markwon markwon, @NonNull String markdown) {
|
||||
render(group, markwon, markwon.parse(markdown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NonNull ViewGroup group, @NonNull Markwon markwon, @NonNull Node root) {
|
||||
|
||||
final LayoutInflater inflater = ensureLayoutInflater(group.getContext());
|
||||
|
||||
ViewProvider<Node> viewProvider;
|
||||
|
||||
for (Node node : reducer.reduce(root)) {
|
||||
viewProvider = viewProvider(node);
|
||||
group.addView(viewProvider.provide(inflater, group, markwon, node));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private LayoutInflater ensureLayoutInflater(@NonNull Context context) {
|
||||
LayoutInflater inflater = this.inflater;
|
||||
if (inflater == null) {
|
||||
inflater = this.inflater = LayoutInflater.from(context);
|
||||
}
|
||||
return inflater;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ViewProvider<Node> viewProvider(@NonNull Node node) {
|
||||
|
||||
// check for specific node view provider
|
||||
final ViewProvider<Node> provider = viewProviders.get(node.getClass());
|
||||
if (provider != null) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
// if it's not present, then we can return a default one
|
||||
return defaultViewProvider;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package ru.noties.markwon;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public abstract class MarkwonReducer {
|
||||
|
||||
@NonNull
|
||||
public static MarkwonReducer directChildren() {
|
||||
return new DirectChildren();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract List<Node> reduce(@NonNull Node node);
|
||||
|
||||
|
||||
static class DirectChildren extends MarkwonReducer {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Node> reduce(@NonNull Node root) {
|
||||
|
||||
final List<Node> list;
|
||||
|
||||
// we will extract all blocks that are direct children of Document
|
||||
Node node = root.getFirstChild();
|
||||
|
||||
// please note, that if there are no children -> we will return a list with
|
||||
// single element (which was supplied)
|
||||
if (node == null) {
|
||||
list = Collections.singletonList(root);
|
||||
} else {
|
||||
|
||||
list = new ArrayList<>();
|
||||
|
||||
Node temp;
|
||||
|
||||
while (node != null) {
|
||||
list.add(node);
|
||||
temp = node.getNext();
|
||||
node.unlink();
|
||||
node = temp;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,16 +42,10 @@ public abstract class MarkwonHtmlRenderer {
|
||||
Builder allowNonClosedTags(boolean allowNonClosedTags);
|
||||
|
||||
@NonNull
|
||||
Builder setHandler(@NonNull String tagName, @NonNull TagHandler tagHandler);
|
||||
Builder setHandler(@NonNull String tagName, @Nullable TagHandler tagHandler);
|
||||
|
||||
@NonNull
|
||||
Builder setHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler);
|
||||
|
||||
@NonNull
|
||||
Builder removeHandler(@NonNull String tagName);
|
||||
|
||||
@NonNull
|
||||
Builder removeHandlers(@NonNull String... tagNames);
|
||||
Builder setHandler(@NonNull Collection<String> tagNames, @Nullable TagHandler tagHandler);
|
||||
|
||||
@NonNull
|
||||
MarkwonHtmlRenderer build();
|
||||
|
@ -100,36 +100,26 @@ class MarkwonHtmlRendererImpl extends MarkwonHtmlRenderer {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder setHandler(@NonNull String tagName, @NonNull TagHandler tagHandler) {
|
||||
tagHandlers.put(tagName, tagHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder setHandler(@NonNull Collection<String> tagNames, @NonNull TagHandler tagHandler) {
|
||||
for (String tagName : tagNames) {
|
||||
if (tagName != null) {
|
||||
public Builder setHandler(@NonNull String tagName, @Nullable TagHandler tagHandler) {
|
||||
if (tagHandler == null) {
|
||||
tagHandlers.remove(tagName);
|
||||
} else {
|
||||
tagHandlers.put(tagName, tagHandler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder removeHandler(@NonNull String tagName) {
|
||||
tagHandlers.remove(tagName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder removeHandlers(@NonNull String... tagNames) {
|
||||
public Builder setHandler(@NonNull Collection<String> tagNames, @Nullable TagHandler tagHandler) {
|
||||
if (tagHandler == null) {
|
||||
for (String tagName : tagNames) {
|
||||
if (tagName != null) {
|
||||
tagHandlers.remove(tagName);
|
||||
}
|
||||
} else {
|
||||
for (String tagName : tagNames) {
|
||||
tagHandlers.put(tagName, tagHandler);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||
import ru.noties.markwon.MarkwonSpansFactory;
|
||||
import ru.noties.markwon.MarkwonVisitor;
|
||||
import ru.noties.markwon.RenderProps;
|
||||
import ru.noties.markwon.core.SimpleBlockNodeVisitor;
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
@ -75,20 +76,7 @@ public class TaskListPlugin extends AbstractMarkwonPlugin {
|
||||
@Override
|
||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||
builder
|
||||
.on(TaskListBlock.class, new MarkwonVisitor.NodeVisitor<TaskListBlock>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull TaskListBlock taskListBlock) {
|
||||
|
||||
visitor.ensureNewLine();
|
||||
|
||||
visitor.visitChildren(taskListBlock);
|
||||
|
||||
if (visitor.hasNext(taskListBlock)) {
|
||||
visitor.ensureNewLine();
|
||||
visitor.forceNewLine();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on(TaskListBlock.class, new SimpleBlockNodeVisitor())
|
||||
.on(TaskListItem.class, new MarkwonVisitor.NodeVisitor<TaskListItem>() {
|
||||
@Override
|
||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull TaskListItem taskListItem) {
|
||||
|
@ -14,6 +14,7 @@ import org.commonmark.node.Node;
|
||||
import java.util.List;
|
||||
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.MarkwonReducer;
|
||||
|
||||
/**
|
||||
* Adapter to display markdown in a RecyclerView. It is done by extracting root blocks from a
|
||||
@ -92,7 +93,7 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter
|
||||
* @see #include(Class, Entry)
|
||||
* @see #defaultEntry(int)
|
||||
* @see #defaultEntry(Entry)
|
||||
* @see #reducer(Reducer)
|
||||
* @see #reducer(MarkwonReducer)
|
||||
*/
|
||||
public interface Builder {
|
||||
|
||||
@ -136,15 +137,15 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter
|
||||
|
||||
/**
|
||||
* Specify how root Node will be <em>reduced</em> to a list of nodes. There is a default
|
||||
* {@link Reducer} that will be used if not provided explicitly (there is no need to
|
||||
* {@link MarkwonReducer} that will be used if not provided explicitly (there is no need to
|
||||
* register your own unless you require it).
|
||||
*
|
||||
* @param reducer {@link Reducer}
|
||||
* @param reducer {@link MarkwonReducer}
|
||||
* @return self
|
||||
* @see Reducer
|
||||
* @see MarkwonReducer
|
||||
*/
|
||||
@NonNull
|
||||
Builder reducer(@NonNull Reducer reducer);
|
||||
Builder reducer(@NonNull MarkwonReducer reducer);
|
||||
|
||||
/**
|
||||
* @return {@link MarkwonAdapter}
|
||||
@ -169,12 +170,6 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter
|
||||
void clear();
|
||||
}
|
||||
|
||||
public interface Reducer {
|
||||
|
||||
@NonNull
|
||||
List<Node> reduce(@NonNull Node root);
|
||||
}
|
||||
|
||||
public abstract void setMarkdown(@NonNull Markwon markwon, @NonNull String markdown);
|
||||
|
||||
public abstract void setParsedMarkdown(@NonNull Markwon markwon, @NonNull Node document);
|
||||
|
@ -7,17 +7,17 @@ import android.view.ViewGroup;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.MarkwonReducer;
|
||||
|
||||
class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||
|
||||
private final SparseArray<Entry<Node, Holder>> entries;
|
||||
private final Entry<Node, Holder> defaultEntry;
|
||||
private final Reducer reducer;
|
||||
private final MarkwonReducer reducer;
|
||||
|
||||
private LayoutInflater layoutInflater;
|
||||
|
||||
@ -28,7 +28,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||
MarkwonAdapterImpl(
|
||||
@NonNull SparseArray<Entry<Node, Holder>> entries,
|
||||
@NonNull Entry<Node, Holder> defaultEntry,
|
||||
@NonNull Reducer reducer) {
|
||||
@NonNull MarkwonReducer reducer) {
|
||||
this.entries = entries;
|
||||
this.defaultEntry = defaultEntry;
|
||||
this.reducer = reducer;
|
||||
@ -133,7 +133,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||
private final SparseArray<Entry<Node, Holder>> entries = new SparseArray<>(3);
|
||||
|
||||
private Entry<Node, Holder> defaultEntry;
|
||||
private Reducer reducer;
|
||||
private MarkwonReducer reducer;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@ -163,7 +163,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Builder reducer(@NonNull Reducer reducer) {
|
||||
public Builder reducer(@NonNull MarkwonReducer reducer) {
|
||||
this.reducer = reducer;
|
||||
return this;
|
||||
}
|
||||
@ -178,33 +178,10 @@ class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||
}
|
||||
|
||||
if (reducer == null) {
|
||||
reducer = new ReducerImpl();
|
||||
reducer = MarkwonReducer.directChildren();
|
||||
}
|
||||
|
||||
return new MarkwonAdapterImpl(entries, defaultEntry, reducer);
|
||||
}
|
||||
}
|
||||
|
||||
static class ReducerImpl implements Reducer {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Node> reduce(@NonNull Node root) {
|
||||
|
||||
final List<Node> list = new ArrayList<>();
|
||||
|
||||
// we will extract all blocks that are direct children of Document
|
||||
Node node = root.getFirstChild();
|
||||
Node temp;
|
||||
|
||||
while (node != null) {
|
||||
list.add(node);
|
||||
temp = node.getNext();
|
||||
node.unlink();
|
||||
node = temp;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,13 @@
|
||||
|
||||
<string name="app_name">MarkwonSample</string>
|
||||
|
||||
<!--do not indent (will be treated as code block otherwise)-->
|
||||
<string name="input"><![CDATA[
|
||||
# Hello! @ic-android-black-24\n\n
|
||||
Home 36 black: @ic-home-black-36\n\n
|
||||
Memory 48 black: @ic-memory-black-48\n\n
|
||||
### I AM ANOTHER HEADER\n\n
|
||||
Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64
|
||||
# Hello! @ic-android-black-24\n\n
|
||||
Home 36 black: @ic-home-black-36\n\n
|
||||
Memory 48 black: @ic-memory-black-48\n\n
|
||||
### I AM ANOTHER HEADER\n\n
|
||||
Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64
|
||||
]]>
|
||||
</string>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user