diff --git a/markwon-simple-ext/build.gradle b/markwon-simple-ext/build.gradle new file mode 100644 index 00000000..39dc63da --- /dev/null +++ b/markwon-simple-ext/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.library' + +android { + + compileSdkVersion config['compile-sdk'] + buildToolsVersion config['build-tools'] + + defaultConfig { + minSdkVersion config['min-sdk'] + targetSdkVersion config['target-sdk'] + versionCode 1 + versionName version + } +} + +dependencies { + + api project(':markwon-core') + + deps['test'].with { + testImplementation it['junit'] + testImplementation it['robolectric'] + testImplementation it['mockito'] + } +} + +registerArtifact(this) \ No newline at end of file diff --git a/markwon-simple-ext/gradle.properties b/markwon-simple-ext/gradle.properties new file mode 100644 index 00000000..2e2ad8d5 --- /dev/null +++ b/markwon-simple-ext/gradle.properties @@ -0,0 +1,4 @@ +POM_NAME=Simple Extension +POM_ARTIFACT_ID=simple-ext +POM_DESCRIPTION=Custom extension based on simple delimiter usage +POM_PACKAGING=aar \ No newline at end of file diff --git a/markwon-simple-ext/src/main/AndroidManifest.xml b/markwon-simple-ext/src/main/AndroidManifest.xml new file mode 100644 index 00000000..26f87af2 --- /dev/null +++ b/markwon-simple-ext/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtBuilder.java b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtBuilder.java new file mode 100644 index 00000000..10d0ed95 --- /dev/null +++ b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtBuilder.java @@ -0,0 +1,64 @@ +package io.noties.markwon.simple.ext; + +import androidx.annotation.NonNull; + +import org.commonmark.parser.delimiter.DelimiterProcessor; + +import java.util.ArrayList; +import java.util.List; + +import io.noties.markwon.SpanFactory; + +// @since 4.0.0-SNAPSHOT +class SimpleExtBuilder { + + private final List extensions = new ArrayList<>(2); + + private boolean isBuilt; + + void addExtension( + int length, + char character, + @NonNull SpanFactory spanFactory) { + + checkState(); + + extensions.add(new SimpleExtDelimiterProcessor( + character, + character, + length, + spanFactory)); + } + + void addExtension( + int length, + char openingCharacter, + char closingCharacter, + @NonNull SpanFactory spanFactory) { + + checkState(); + + extensions.add(new SimpleExtDelimiterProcessor( + openingCharacter, + closingCharacter, + length, + spanFactory)); + } + + @NonNull + List build() { + + checkState(); + + isBuilt = true; + + return extensions; + } + + private void checkState() { + if (isBuilt) { + throw new IllegalStateException("SimpleExtBuilder is already built, " + + "do not mutate SimpleExtPlugin after configuration is finished"); + } + } +} diff --git a/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtDelimiterProcessor.java b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtDelimiterProcessor.java new file mode 100644 index 00000000..5cde82fe --- /dev/null +++ b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtDelimiterProcessor.java @@ -0,0 +1,70 @@ +package io.noties.markwon.simple.ext; + +import androidx.annotation.NonNull; + +import org.commonmark.node.Node; +import org.commonmark.node.Text; +import org.commonmark.parser.delimiter.DelimiterProcessor; +import org.commonmark.parser.delimiter.DelimiterRun; + +import io.noties.markwon.SpanFactory; + +// @since 4.0.0-SNAPSHOT +class SimpleExtDelimiterProcessor implements DelimiterProcessor { + + private final char open; + private final char close; + private final int length; + private final SpanFactory spanFactory; + + SimpleExtDelimiterProcessor( + char open, + char close, + int length, + @NonNull SpanFactory spanFactory) { + this.open = open; + this.close = close; + this.length = length; + this.spanFactory = spanFactory; + } + + @Override + public char getOpeningCharacter() { + return open; + } + + @Override + public char getClosingCharacter() { + return close; + } + + @Override + public int getMinLength() { + return length; + } + + @Override + public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) { + if (opener.length() >= length && closer.length() >= length) { + return length; + } + return 0; + } + + @Override + public void process(Text opener, Text closer, int delimiterUse) { + + final Node node = new SimpleExtNode(spanFactory); + + Node tmp = opener.getNext(); + Node next; + + while (tmp != null && tmp != closer) { + next = tmp.getNext(); + node.appendChild(tmp); + tmp = next; + } + + opener.insertAfter(node); + } +} diff --git a/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtNode.java b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtNode.java new file mode 100644 index 00000000..d43855e2 --- /dev/null +++ b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtNode.java @@ -0,0 +1,28 @@ +package io.noties.markwon.simple.ext; + +import androidx.annotation.NonNull; + +import org.commonmark.node.CustomNode; +import org.commonmark.node.Visitor; + +import io.noties.markwon.SpanFactory; + +// @since 4.0.0-SNAPSHOT +class SimpleExtNode extends CustomNode { + + private final SpanFactory spanFactory; + + SimpleExtNode(@NonNull SpanFactory spanFactory) { + this.spanFactory = spanFactory; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @NonNull + SpanFactory spanFactory() { + return spanFactory; + } +} diff --git a/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtPlugin.java b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtPlugin.java new file mode 100644 index 00000000..48e90359 --- /dev/null +++ b/markwon-simple-ext/src/main/java/io/noties/markwon/simple/ext/SimpleExtPlugin.java @@ -0,0 +1,85 @@ +package io.noties.markwon.simple.ext; + +import androidx.annotation.NonNull; + +import org.commonmark.parser.Parser; +import org.commonmark.parser.delimiter.DelimiterProcessor; + +import io.noties.markwon.AbstractMarkwonPlugin; +import io.noties.markwon.MarkwonVisitor; +import io.noties.markwon.SpanFactory; +import io.noties.markwon.SpannableBuilder; + +/** + * @since 4.0.0-SNAPSHOT + */ +public class SimpleExtPlugin extends AbstractMarkwonPlugin { + + public interface SimpleExtConfigure { + void configure(@NonNull SimpleExtPlugin plugin); + } + + @NonNull + public static SimpleExtPlugin create() { + return new SimpleExtPlugin(); + } + + @NonNull + public static SimpleExtPlugin create(@NonNull SimpleExtConfigure configure) { + final SimpleExtPlugin plugin = new SimpleExtPlugin(); + configure.configure(plugin); + return plugin; + } + + private final SimpleExtBuilder builder = new SimpleExtBuilder(); + + @SuppressWarnings("WeakerAccess") + SimpleExtPlugin() { + } + + @NonNull + public SimpleExtPlugin addExtension( + int length, + char character, + @NonNull SpanFactory spanFactory) { + builder.addExtension(length, character, spanFactory); + return this; + } + + @NonNull + public SimpleExtPlugin addExtension( + int length, + char openingCharacter, + char closingCharacter, + @NonNull SpanFactory spanFactory) { + builder.addExtension(length, openingCharacter, closingCharacter, spanFactory); + return this; + } + + @Override + public void configureParser(@NonNull Parser.Builder builder) { + for (DelimiterProcessor processor : this.builder.build()) { + builder.customDelimiterProcessor(processor); + } + } + + @Override + public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { + builder.on(SimpleExtNode.class, new MarkwonVisitor.NodeVisitor() { + @Override + public void visit(@NonNull MarkwonVisitor visitor, @NonNull SimpleExtNode simpleExtNode) { + + final int length = visitor.length(); + + visitor.visitChildren(simpleExtNode); + + SpannableBuilder.setSpans( + visitor.builder(), + simpleExtNode.spanFactory().getSpans(visitor.configuration(), visitor.renderProps()), + length, + visitor.length() + ); + } + }); + } +} diff --git a/sample/build.gradle b/sample/build.gradle index f624824b..c3ff21dd 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -43,6 +43,7 @@ dependencies { implementation project(':markwon-syntax-highlight') implementation project(':markwon-recycler') implementation project(':markwon-recycler-table') + implementation project(':markwon-simple-ext') implementation project(':markwon-image-picasso') diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 22ebc86b..7018e3fc 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -25,6 +25,7 @@ + diff --git a/sample/src/main/java/io/noties/markwon/sample/MainActivity.java b/sample/src/main/java/io/noties/markwon/sample/MainActivity.java index 8c197ea7..a27d37a9 100644 --- a/sample/src/main/java/io/noties/markwon/sample/MainActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/MainActivity.java @@ -24,6 +24,7 @@ import io.noties.markwon.sample.customextension.CustomExtensionActivity; import io.noties.markwon.sample.html.HtmlActivity; import io.noties.markwon.sample.latex.LatexActivity; import io.noties.markwon.sample.recycler.RecyclerActivity; +import io.noties.markwon.sample.simpleext.SimpleExtActivity; public class MainActivity extends Activity { @@ -102,6 +103,10 @@ public class MainActivity extends Activity { activity = HtmlActivity.class; break; + case SIMPLE_EXT: + activity = SimpleExtActivity.class; + break; + default: throw new IllegalStateException("No Activity is associated with sample-item: " + item); } diff --git a/sample/src/main/java/io/noties/markwon/sample/Sample.java b/sample/src/main/java/io/noties/markwon/sample/Sample.java index 03160a35..dbe9bc8a 100644 --- a/sample/src/main/java/io/noties/markwon/sample/Sample.java +++ b/sample/src/main/java/io/noties/markwon/sample/Sample.java @@ -15,7 +15,9 @@ public enum Sample { RECYCLER(R.string.sample_recycler), - HTML(R.string.sample_html); + HTML(R.string.sample_html), + + SIMPLE_EXT(R.string.sample_simple_ext); private final int textResId; diff --git a/sample/src/main/java/io/noties/markwon/sample/simpleext/SimpleExtActivity.java b/sample/src/main/java/io/noties/markwon/sample/simpleext/SimpleExtActivity.java new file mode 100644 index 00000000..77902e16 --- /dev/null +++ b/sample/src/main/java/io/noties/markwon/sample/simpleext/SimpleExtActivity.java @@ -0,0 +1,43 @@ +package io.noties.markwon.sample.simpleext; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.text.style.ForegroundColorSpan; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import io.noties.markwon.Markwon; +import io.noties.markwon.core.spans.EmphasisSpan; +import io.noties.markwon.core.spans.StrongEmphasisSpan; +import io.noties.markwon.sample.R; +import io.noties.markwon.simple.ext.SimpleExtPlugin; + +public class SimpleExtActivity extends Activity { + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_text_view); + + final TextView textView = findViewById(R.id.text_view); + + final Markwon markwon = Markwon.builder(this) + .usePlugin(SimpleExtPlugin.create(plugin -> plugin + // +sometext+ + .addExtension(1, '+', (_1, _2) -> new EmphasisSpan()) + // ??sometext?? + .addExtension(2, '?', (_1, _2) -> new StrongEmphasisSpan()) + // @@sometext$$ + .addExtension(2, '@', '$', (_1, _2) -> new ForegroundColorSpan(Color.RED)))) + .build(); + + final String markdown = "# SimpleExt\n" + + "\n" + + "+let's start with `+`, ??then we can use this, and finally @@this$$??+"; + + markwon.setMarkdown(textView, markdown); + } +} diff --git a/sample/src/main/res/values/strings-samples.xml b/sample/src/main/res/values/strings-samples.xml index 24cab950..a624f921 100644 --- a/sample/src/main/res/values/strings-samples.xml +++ b/sample/src/main/res/values/strings-samples.xml @@ -17,4 +17,7 @@ # \# Html\n\nShows how to define own tag handlers + # \# SimpleExt\n\nShows how to use SimpleExtPlugin module + to create own delimited parser extensions + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 38372eba..45a92d52 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,5 +12,6 @@ include ':app', ':sample', ':markwon-linkify', ':markwon-recycler', ':markwon-recycler-table', + ':markwon-simple-ext', ':markwon-syntax-highlight', ':markwon-test-span'