MarkwonInlineParserPlugin

This commit is contained in:
Dimitry Ivanov 2020-02-26 15:54:08 +03:00
parent a80ff09e15
commit 74682ae605
6 changed files with 195 additions and 11 deletions

View File

@ -14,6 +14,7 @@ android {
}
dependencies {
api project(':markwon-core')
api deps['x-annotations']
api deps['commonmark']

View File

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

View File

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

View File

@ -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<String, Runnable> 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;
}
}

View File

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

View File

@ -29,7 +29,7 @@
<string name="sample_inline_parser"># \# Inline Parser\n\nUsage of custom inline parser</string>
<string name="sample_html_details"># \# HTML &lt;details> tag\n\n&lt;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>