MarkwonInlineParserPlugin
This commit is contained in:
parent
a80ff09e15
commit
74682ae605
@ -14,6 +14,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api project(':markwon-core')
|
||||||
api deps['x-annotations']
|
api deps['x-annotations']
|
||||||
api deps['commonmark']
|
api deps['commonmark']
|
||||||
|
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package io.noties.markwon.inlineparser;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.commonmark.parser.Parser;
|
||||||
|
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3.0-SNAPSHOT
|
||||||
|
*/
|
||||||
|
public class MarkwonInlineParserPlugin extends AbstractMarkwonPlugin {
|
||||||
|
|
||||||
|
public interface BuilderConfigure<B extends MarkwonInlineParser.FactoryBuilder> {
|
||||||
|
void configureBuilder(@NonNull B factoryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonInlineParserPlugin create() {
|
||||||
|
return create(MarkwonInlineParser.factoryBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonInlineParserPlugin create(@NonNull BuilderConfigure<MarkwonInlineParser.FactoryBuilder> configure) {
|
||||||
|
final MarkwonInlineParser.FactoryBuilder factoryBuilder = MarkwonInlineParser.factoryBuilder();
|
||||||
|
configure.configureBuilder(factoryBuilder);
|
||||||
|
return new MarkwonInlineParserPlugin(factoryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonInlineParserPlugin create(@NonNull MarkwonInlineParser.FactoryBuilder factoryBuilder) {
|
||||||
|
return new MarkwonInlineParserPlugin(factoryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <B extends MarkwonInlineParser.FactoryBuilder> MarkwonInlineParserPlugin create(
|
||||||
|
@NonNull B factoryBuilder,
|
||||||
|
@NonNull BuilderConfigure<B> configure) {
|
||||||
|
configure.configureBuilder(factoryBuilder);
|
||||||
|
return new MarkwonInlineParserPlugin(factoryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final MarkwonInlineParser.FactoryBuilder factoryBuilder;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
MarkwonInlineParserPlugin(@NonNull MarkwonInlineParser.FactoryBuilder factoryBuilder) {
|
||||||
|
this.factoryBuilder = factoryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureParser(@NonNull Parser.Builder builder) {
|
||||||
|
builder.inlineParserFactory(factoryBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public MarkwonInlineParser.FactoryBuilder factoryBuilder() {
|
||||||
|
return factoryBuilder;
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,14 @@ public abstract class ActivityWithMenuOptions extends Activity {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public abstract MenuOptions menuOptions();
|
public abstract MenuOptions menuOptions();
|
||||||
|
|
||||||
|
protected void beforeOptionSelected(@NonNull String option) {
|
||||||
|
// no op, override to customize
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void afterOptionSelected(@NonNull String option) {
|
||||||
|
// no op, override to customize
|
||||||
|
}
|
||||||
|
|
||||||
private MenuOptions menuOptions;
|
private MenuOptions menuOptions;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -29,6 +37,13 @@ public abstract class ActivityWithMenuOptions extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
return menuOptions.onOptionsItemSelected(item);
|
final MenuOptions.Option option = menuOptions.onOptionsItemSelected(item);
|
||||||
|
if (option != null) {
|
||||||
|
beforeOptionSelected(option.title);
|
||||||
|
option.action.run();
|
||||||
|
afterOptionSelected(option.title);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -15,6 +16,16 @@ public class MenuOptions {
|
|||||||
return new MenuOptions();
|
return new MenuOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class Option {
|
||||||
|
final String title;
|
||||||
|
final Runnable action;
|
||||||
|
|
||||||
|
Option(@NonNull String title, @NonNull Runnable action) {
|
||||||
|
this.title = title;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// to preserve order use LinkedHashMap
|
// to preserve order use LinkedHashMap
|
||||||
private final Map<String, Runnable> actions = new LinkedHashMap<>();
|
private final Map<String, Runnable> actions = new LinkedHashMap<>();
|
||||||
|
|
||||||
@ -34,13 +45,13 @@ public class MenuOptions {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean onOptionsItemSelected(MenuItem item) {
|
@Nullable
|
||||||
|
Option onOptionsItemSelected(MenuItem item) {
|
||||||
final String title = String.valueOf(item.getTitle());
|
final String title = String.valueOf(item.getTitle());
|
||||||
final Runnable action = actions.get(title);
|
final Runnable action = actions.get(title);
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
action.run();
|
return new Option(title, action);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.text.Editable;
|
|||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.MetricAffectingSpan;
|
import android.text.style.MetricAffectingSpan;
|
||||||
@ -40,6 +41,7 @@ import io.noties.markwon.inlineparser.BangInlineProcessor;
|
|||||||
import io.noties.markwon.inlineparser.EntityInlineProcessor;
|
import io.noties.markwon.inlineparser.EntityInlineProcessor;
|
||||||
import io.noties.markwon.inlineparser.HtmlInlineProcessor;
|
import io.noties.markwon.inlineparser.HtmlInlineProcessor;
|
||||||
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
import io.noties.markwon.inlineparser.MarkwonInlineParser;
|
||||||
|
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
|
||||||
import io.noties.markwon.linkify.LinkifyPlugin;
|
import io.noties.markwon.linkify.LinkifyPlugin;
|
||||||
import io.noties.markwon.sample.ActivityWithMenuOptions;
|
import io.noties.markwon.sample.ActivityWithMenuOptions;
|
||||||
import io.noties.markwon.sample.MenuOptions;
|
import io.noties.markwon.sample.MenuOptions;
|
||||||
@ -48,6 +50,7 @@ import io.noties.markwon.sample.R;
|
|||||||
public class EditorActivity extends ActivityWithMenuOptions {
|
public class EditorActivity extends ActivityWithMenuOptions {
|
||||||
|
|
||||||
private EditText editText;
|
private EditText editText;
|
||||||
|
private String pendingInput;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@ -58,16 +61,41 @@ public class EditorActivity extends ActivityWithMenuOptions {
|
|||||||
.add("customPunctuationSpan", this::custom_punctuation_span)
|
.add("customPunctuationSpan", this::custom_punctuation_span)
|
||||||
.add("additionalEditSpan", this::additional_edit_span)
|
.add("additionalEditSpan", this::additional_edit_span)
|
||||||
.add("additionalPlugins", this::additional_plugins)
|
.add("additionalPlugins", this::additional_plugins)
|
||||||
.add("multipleEditSpans", this::multiple_edit_spans);
|
.add("multipleEditSpans", this::multiple_edit_spans)
|
||||||
|
.add("multipleEditSpansPlugin", this::multiple_edit_spans_plugin)
|
||||||
|
.add("pluginRequire", this::plugin_require)
|
||||||
|
.add("pluginNoDefaults", this::plugin_no_defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void beforeOptionSelected(@NonNull String option) {
|
||||||
|
// we cannot _clear_ editText of text-watchers without keeping a reference to them...
|
||||||
|
pendingInput = editText != null
|
||||||
|
? editText.getText().toString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
createView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterOptionSelected(@NonNull String option) {
|
||||||
|
if (!TextUtils.isEmpty(pendingInput)) {
|
||||||
|
editText.setText(pendingInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createView() {
|
||||||
|
setContentView(R.layout.activity_editor);
|
||||||
|
|
||||||
|
this.editText = findViewById(R.id.edit_text);
|
||||||
|
|
||||||
|
initBottomBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_editor);
|
createView();
|
||||||
|
|
||||||
this.editText = findViewById(R.id.edit_text);
|
|
||||||
initBottomBar();
|
|
||||||
|
|
||||||
multiple_edit_spans();
|
multiple_edit_spans();
|
||||||
}
|
}
|
||||||
@ -219,6 +247,76 @@ public class EditorActivity extends ActivityWithMenuOptions {
|
|||||||
editor, Executors.newSingleThreadExecutor(), editText));
|
editor, Executors.newSingleThreadExecutor(), editText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void multiple_edit_spans_plugin() {
|
||||||
|
// inline parsing is configured via MarkwonInlineParserPlugin
|
||||||
|
|
||||||
|
// for links to be clickable
|
||||||
|
editText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(StrikethroughPlugin.create())
|
||||||
|
.usePlugin(LinkifyPlugin.create())
|
||||||
|
.usePlugin(MarkwonInlineParserPlugin.create(builder -> {
|
||||||
|
builder
|
||||||
|
.excludeInlineProcessor(BangInlineProcessor.class)
|
||||||
|
.excludeInlineProcessor(HtmlInlineProcessor.class)
|
||||||
|
.excludeInlineProcessor(EntityInlineProcessor.class);
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final LinkEditHandler.OnClick onClick = (widget, link) -> markwon.configuration().linkResolver().resolve(widget, link);
|
||||||
|
|
||||||
|
final MarkwonEditor editor = MarkwonEditor.builder(markwon)
|
||||||
|
.useEditHandler(new EmphasisEditHandler())
|
||||||
|
.useEditHandler(new StrongEmphasisEditHandler())
|
||||||
|
.useEditHandler(new StrikethroughEditHandler())
|
||||||
|
.useEditHandler(new CodeEditHandler())
|
||||||
|
.useEditHandler(new BlockQuoteEditHandler())
|
||||||
|
.useEditHandler(new LinkEditHandler(onClick))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(
|
||||||
|
editor, Executors.newSingleThreadExecutor(), editText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugin_require() {
|
||||||
|
// usage of plugin from other plugins
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(MarkwonInlineParserPlugin.create())
|
||||||
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
|
@Override
|
||||||
|
public void configure(@NonNull Registry registry) {
|
||||||
|
registry.require(MarkwonInlineParserPlugin.class)
|
||||||
|
.factoryBuilder()
|
||||||
|
.excludeInlineProcessor(HtmlInlineProcessor.class);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final MarkwonEditor editor = MarkwonEditor.create(markwon);
|
||||||
|
|
||||||
|
editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(
|
||||||
|
editor, Executors.newSingleThreadExecutor(), editText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugin_no_defaults() {
|
||||||
|
// a plugin with no defaults registered
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(MarkwonInlineParserPlugin.create(MarkwonInlineParser.factoryBuilderNoDefaults()))
|
||||||
|
// .usePlugin(MarkwonInlineParserPlugin.create(MarkwonInlineParser.factoryBuilderNoDefaults(), factoryBuilder -> {
|
||||||
|
// // if anything, they can be included here
|
||||||
|
//// factoryBuilder.includeDefaults()
|
||||||
|
// }))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final MarkwonEditor editor = MarkwonEditor.create(markwon);
|
||||||
|
|
||||||
|
editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(
|
||||||
|
editor, Executors.newSingleThreadExecutor(), editText));
|
||||||
|
}
|
||||||
|
|
||||||
private void initBottomBar() {
|
private void initBottomBar() {
|
||||||
// all except block-quote wraps if have selection, or inserts at current cursor position
|
// all except block-quote wraps if have selection, or inserts at current cursor position
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<string name="sample_inline_parser"># \# Inline Parser\n\nUsage of custom inline parser</string>
|
<string name="sample_inline_parser"># \# Inline Parser\n\nUsage of custom inline parser</string>
|
||||||
|
|
||||||
<string name="sample_html_details"># \# HTML <details> tag\n\n<details> tag parsed and rendered</string>
|
<string name="sample_html_details"># \# HTML\n\n`details` tag parsed and rendered</string>
|
||||||
|
|
||||||
<string name="sample_task_list"># \# TaskList\n\nUsage of TaskListPlugin</string>
|
<string name="sample_task_list"># \# TaskList\n\nUsage of TaskListPlugin</string>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user