From 74682ae605e5fbf470fad74206e8b18a2acbe3bf Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Wed, 26 Feb 2020 15:54:08 +0300 Subject: [PATCH] MarkwonInlineParserPlugin --- markwon-inline-parser/build.gradle | 1 + .../MarkwonInlineParserPlugin.java | 59 ++++++++++ .../sample/ActivityWithMenuOptions.java | 17 ++- .../io/noties/markwon/sample/MenuOptions.java | 19 ++- .../markwon/sample/editor/EditorActivity.java | 108 +++++++++++++++++- .../src/main/res/values/strings-samples.xml | 2 +- 6 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserPlugin.java diff --git a/markwon-inline-parser/build.gradle b/markwon-inline-parser/build.gradle index 703a18ff..32a45d7c 100644 --- a/markwon-inline-parser/build.gradle +++ b/markwon-inline-parser/build.gradle @@ -14,6 +14,7 @@ android { } dependencies { + api project(':markwon-core') api deps['x-annotations'] api deps['commonmark'] diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserPlugin.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserPlugin.java new file mode 100644 index 00000000..ce80501b --- /dev/null +++ b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserPlugin.java @@ -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 { + void configureBuilder(@NonNull B factoryBuilder); + } + + @NonNull + public static MarkwonInlineParserPlugin create() { + return create(MarkwonInlineParser.factoryBuilder()); + } + + @NonNull + public static MarkwonInlineParserPlugin create(@NonNull BuilderConfigure 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 MarkwonInlineParserPlugin create( + @NonNull B factoryBuilder, + @NonNull BuilderConfigure 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; + } +} diff --git a/sample/src/main/java/io/noties/markwon/sample/ActivityWithMenuOptions.java b/sample/src/main/java/io/noties/markwon/sample/ActivityWithMenuOptions.java index 66690f7b..54f5342f 100644 --- a/sample/src/main/java/io/noties/markwon/sample/ActivityWithMenuOptions.java +++ b/sample/src/main/java/io/noties/markwon/sample/ActivityWithMenuOptions.java @@ -13,6 +13,14 @@ public abstract class ActivityWithMenuOptions extends Activity { @NonNull 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; @Override @@ -29,6 +37,13 @@ public abstract class ActivityWithMenuOptions extends Activity { @Override 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; } } diff --git a/sample/src/main/java/io/noties/markwon/sample/MenuOptions.java b/sample/src/main/java/io/noties/markwon/sample/MenuOptions.java index 1c349bd8..6fb5b310 100644 --- a/sample/src/main/java/io/noties/markwon/sample/MenuOptions.java +++ b/sample/src/main/java/io/noties/markwon/sample/MenuOptions.java @@ -4,6 +4,7 @@ import android.view.Menu; import android.view.MenuItem; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.LinkedHashMap; import java.util.Map; @@ -15,6 +16,16 @@ public class 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 private final Map actions = new LinkedHashMap<>(); @@ -34,13 +45,13 @@ public class MenuOptions { return false; } - boolean onOptionsItemSelected(MenuItem item) { + @Nullable + Option onOptionsItemSelected(MenuItem item) { final String title = String.valueOf(item.getTitle()); final Runnable action = actions.get(title); if (action != null) { - action.run(); - return true; + return new Option(title, action); } - return false; + return null; } } diff --git a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java index 0dc05ef9..e1181a7f 100644 --- a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java @@ -5,6 +5,7 @@ import android.text.Editable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; +import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.text.style.ForegroundColorSpan; 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.HtmlInlineProcessor; import io.noties.markwon.inlineparser.MarkwonInlineParser; +import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.linkify.LinkifyPlugin; import io.noties.markwon.sample.ActivityWithMenuOptions; import io.noties.markwon.sample.MenuOptions; @@ -48,6 +50,7 @@ import io.noties.markwon.sample.R; public class EditorActivity extends ActivityWithMenuOptions { private EditText editText; + private String pendingInput; @NonNull @Override @@ -58,16 +61,41 @@ public class EditorActivity extends ActivityWithMenuOptions { .add("customPunctuationSpan", this::custom_punctuation_span) .add("additionalEditSpan", this::additional_edit_span) .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 public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_editor); - - this.editText = findViewById(R.id.edit_text); - initBottomBar(); + createView(); multiple_edit_spans(); } @@ -219,6 +247,76 @@ public class EditorActivity extends ActivityWithMenuOptions { 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() { // all except block-quote wraps if have selection, or inserts at current cursor position diff --git a/sample/src/main/res/values/strings-samples.xml b/sample/src/main/res/values/strings-samples.xml index f5a97644..d7f11e1a 100644 --- a/sample/src/main/res/values/strings-samples.xml +++ b/sample/src/main/res/values/strings-samples.xml @@ -29,7 +29,7 @@ # \# Inline Parser\n\nUsage of custom inline parser - # \# HTML <details> tag\n\n<details> tag parsed and rendered + # \# HTML\n\n`details` tag parsed and rendered # \# TaskList\n\nUsage of TaskListPlugin