diff --git a/app-sample/build.gradle b/app-sample/build.gradle index bfd97997..4d7acf82 100644 --- a/app-sample/build.gradle +++ b/app-sample/build.gradle @@ -44,12 +44,8 @@ android { } compileOptions { - targetCompatibility JavaVersion.VERSION_1_8 - sourceCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = "1.8" + targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_11 } sourceSets { @@ -58,48 +54,6 @@ android { } } - // do not sign in CI - if (!project.hasProperty('CI')) { - signingConfigs { - config { - - final def keystoreFile = project.file('keystore.jks') - final def keystoreFilePassword = 'MARKWON_KEYSTORE_FILE_PASSWORD' - final def keystoreAlias = 'MARKWON_KEY_ALIAS' - final def keystoreAliasPassword = 'MARKWON_KEY_ALIAS_PASSWORD' - - final def properties = [ - keystoreFilePassword, - keystoreAlias, - keystoreAliasPassword - ] - - if (!keystoreFile.exists()) { - throw new IllegalStateException("No '${keystoreFile.name}' file is found.") - } - - final def missingProperties = properties.findAll { !project.hasProperty(it) } - if (!missingProperties.isEmpty()) { - throw new IllegalStateException("Missing required signing properties: $missingProperties") - } - - storeFile keystoreFile - storePassword project[keystoreFilePassword] - - keyAlias project[keystoreAlias] - keyPassword project[keystoreAliasPassword] - } - } - - buildTypes { - debug { - signingConfig signingConfigs.config - } - release { - signingConfig signingConfigs.config - } - } - } } kapt { @@ -122,7 +76,7 @@ dependencies { kapt it['prism4j-bundler'] } - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(':markwon-core') implementation project(':markwon-editor') @@ -132,7 +86,6 @@ dependencies { implementation project(':markwon-ext-tasklist') implementation project(':markwon-html') implementation project(':markwon-image') - implementation project(':markwon-inline-parser') implementation project(':markwon-linkify') implementation project(':markwon-recycler') implementation project(':markwon-recycler-table') diff --git a/app-sample/samples.json b/app-sample/samples.json index 1b4c5275..9082b7e3 100644 --- a/app-sample/samples.json +++ b/app-sample/samples.json @@ -651,18 +651,6 @@ "parsing" ] }, - { - "javaClassName": "io.noties.markwon.app.samples.DelimiterProcessorSample", - "id": "20200630194017", - "title": "Custom delimiter processor", - "description": "Custom parsing delimiter processor with `?` character", - "artifacts": [ - "CORE" - ], - "tags": [ - "parsing" - ] - }, { "javaClassName": "io.noties.markwon.app.samples.html.HtmlDisableSanitizeSample", "id": "20200630171424", @@ -957,31 +945,6 @@ "editor" ] }, - { - "javaClassName": "io.noties.markwon.app.samples.NoParsingSample", - "id": "20200629171212", - "title": "No parsing", - "description": "All commonmark parsing is disabled (both inlines and blocks)", - "artifacts": [ - "CORE" - ], - "tags": [ - "parsing", - "rendering" - ] - }, - { - "javaClassName": "io.noties.markwon.app.samples.InlinePluginNoDefaultsSample", - "id": "20200629170857", - "title": "Inline parsing without defaults", - "description": "Configure inline parser plugin to **not** have any **inline** parsing", - "artifacts": [ - "INLINE_PARSER" - ], - "tags": [ - "parsing" - ] - }, { "javaClassName": "io.noties.markwon.app.samples.editor.EditorNewLineContinuationSample", "id": "20200629170348", @@ -1076,23 +1039,6 @@ "editor" ] }, - { - "javaClassName": "io.noties.markwon.app.samples.CustomExtensionSample", - "id": "20200629163248", - "title": "Custom extension", - "description": "Custom extension that adds an icon from resources and renders it as image with `@ic-name` syntax", - "artifacts": [ - "CORE" - ], - "tags": [ - "parsing", - "plugin", - "rendering", - "image", - "extension", - "span" - ] - }, { "javaClassName": "io.noties.markwon.app.samples.GithubUserIssueOnTextAddedSample", "id": "20200629162024", @@ -1107,21 +1053,6 @@ "textAddedListener" ] }, - { - "javaClassName": "io.noties.markwon.app.samples.GithubUserIssueInlineParsingSample", - "id": "20200629162023", - "title": "User mention and issue (via text)", - "description": "Github-like user mention and issue rendering via `CorePlugin.OnTextAddedListener`", - "artifacts": [ - "CORE", - "INLINE_PARSER" - ], - "tags": [ - "parsing", - "rendering", - "textAddedListener" - ] - }, { "javaClassName": "io.noties.markwon.app.samples.ReadMorePluginSample", "id": "20200629161505", @@ -1410,20 +1341,6 @@ "defaults" ] }, - { - "javaClassName": "io.noties.markwon.app.samples.EnabledBlockTypesSample", - "id": "20200627075012", - "title": "Enabled markdown blocks", - "description": "Modify/inspect enabled by `CorePlugin` block types. Disable quotes or other blocks from being parsed", - "artifacts": [ - "CORE" - ], - "tags": [ - "parsing", - "block", - "plugin" - ] - }, { "javaClassName": "io.noties.markwon.app.samples.ToastDynamicContentSample", "id": "20200627074017", diff --git a/app-sample/src/main/AndroidManifest.xml b/app-sample/src/main/AndroidManifest.xml index 0c265b10..70511bdd 100644 --- a/app-sample/src/main/AndroidManifest.xml +++ b/app-sample/src/main/AndroidManifest.xml @@ -15,7 +15,9 @@ android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> - + @@ -42,7 +44,7 @@ android:host="noties.io" android:scheme="https" /> - + diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/AdditionalSpacingSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/AdditionalSpacingSample.java index 3b263846..a4ab110b 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/AdditionalSpacingSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/AdditionalSpacingSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Heading; +import com.vladsch.flexmark.ast.Heading; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/AllBlocksNoForcedNewLineSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/AllBlocksNoForcedNewLineSample.java index cfa03316..b5427175 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/AllBlocksNoForcedNewLineSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/AllBlocksNoForcedNewLineSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.BlockHandlerDef; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/BlockHandlerSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/BlockHandlerSample.java index bfb066ac..a7df79c4 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/BlockHandlerSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/BlockHandlerSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ChangeBulletSpanSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/ChangeBulletSpanSample.java index 373053e8..294b7b3b 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ChangeBulletSpanSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ChangeBulletSpanSample.java @@ -4,7 +4,8 @@ import android.text.style.BulletSpan; import androidx.annotation.NonNull; -import org.commonmark.node.ListItem; +import com.vladsch.flexmark.ast.ListItem; + import io.noties.debug.Debug; import io.noties.markwon.AbstractMarkwonPlugin; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/CopyCodeBlockSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/CopyCodeBlockSample.kt index 9860111a..7b224e47 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/CopyCodeBlockSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/CopyCodeBlockSample.kt @@ -10,6 +10,7 @@ import android.text.style.ClickableSpan import android.text.style.LeadingMarginSpan import android.view.View import android.widget.TextView +import com.vladsch.flexmark.ast.FencedCodeBlock import io.noties.debug.Debug import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.Markwon @@ -20,19 +21,18 @@ import io.noties.markwon.sample.annotations.MarkwonArtifact import io.noties.markwon.sample.annotations.MarkwonSampleInfo import io.noties.markwon.sample.annotations.Tag import io.noties.markwon.utils.LeadingMarginUtils -import org.commonmark.node.FencedCodeBlock @MarkwonSampleInfo( - id = "20210315112847", - title = "Copy code block", - description = "Copy contents of fenced code blocks", - artifacts = [MarkwonArtifact.CORE], - tags = [Tag.rendering, Tag.block, Tag.spanFactory, Tag.span] + id = "20210315112847", + title = "Copy code block", + description = "Copy contents of fenced code blocks", + artifacts = [MarkwonArtifact.CORE], + tags = [Tag.rendering, Tag.block, Tag.spanFactory, Tag.span] ) class CopyCodeBlockSample : MarkwonTextViewSample() { - override fun render() { - val md = """ + override fun render() { + val md = """ # Hello code blocks! ```java final int i = 0; @@ -43,77 +43,77 @@ class CopyCodeBlockSample : MarkwonTextViewSample() { bye bye! """.trimIndent() - val markwon = Markwon.builder(context) - .usePlugin(object : AbstractMarkwonPlugin() { - override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) { - builder.appendFactory(FencedCodeBlock::class.java) { _, _ -> - CopyContentsSpan() - } - builder.appendFactory(FencedCodeBlock::class.java) { _, _ -> - CopyIconSpan(context.getDrawable(R.drawable.ic_code_white_24dp)!!) - } - } - }) - .build() + val markwon = Markwon.builder(context) + .usePlugin(object : AbstractMarkwonPlugin() { + override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) { + builder.appendFactory(FencedCodeBlock::class.java) { _, _ -> + CopyContentsSpan() + } + builder.appendFactory(FencedCodeBlock::class.java) { _, _ -> + CopyIconSpan(context.getDrawable(R.drawable.ic_code_white_24dp)!!) + } + } + }) + .build() - markwon.setMarkdown(textView, md) + markwon.setMarkdown(textView, md) + } + + class CopyContentsSpan : ClickableSpan() { + override fun onClick(widget: View) { + val spanned = (widget as? TextView)?.text as? Spanned ?: return + val start = spanned.getSpanStart(this) + val end = spanned.getSpanEnd(this) + // by default code blocks have new lines before and after content + val contents = spanned.subSequence(start, end).toString().trim() + // copy code here + Debug.i(contents) } - class CopyContentsSpan : ClickableSpan() { - override fun onClick(widget: View) { - val spanned = (widget as? TextView)?.text as? Spanned ?: return - val start = spanned.getSpanStart(this) - val end = spanned.getSpanEnd(this) - // by default code blocks have new lines before and after content - val contents = spanned.subSequence(start, end).toString().trim() - // copy code here - Debug.i(contents) - } + override fun updateDrawState(ds: TextPaint) { + // do not apply link styling + } + } - override fun updateDrawState(ds: TextPaint) { - // do not apply link styling - } + class CopyIconSpan(val icon: Drawable) : LeadingMarginSpan { + + init { + if (icon.bounds.isEmpty) { + icon.setBounds(0, 0, icon.intrinsicWidth, icon.intrinsicHeight) + } } - class CopyIconSpan(val icon: Drawable) : LeadingMarginSpan { + override fun getLeadingMargin(first: Boolean): Int = 0 - init { - if (icon.bounds.isEmpty) { - icon.setBounds(0, 0, icon.intrinsicWidth, icon.intrinsicHeight) - } - } + override fun drawLeadingMargin( + c: Canvas, + p: Paint, + x: Int, + dir: Int, + top: Int, + baseline: Int, + bottom: Int, + text: CharSequence, + start: Int, + end: Int, + first: Boolean, + layout: Layout + ) { - override fun getLeadingMargin(first: Boolean): Int = 0 + // called for each line of text, we are interested only in first one + if (!LeadingMarginUtils.selfStart(start, text, this)) return - override fun drawLeadingMargin( - c: Canvas, - p: Paint, - x: Int, - dir: Int, - top: Int, - baseline: Int, - bottom: Int, - text: CharSequence, - start: Int, - end: Int, - first: Boolean, - layout: Layout - ) { - - // called for each line of text, we are interested only in first one - if (!LeadingMarginUtils.selfStart(start, text, this)) return - - val save = c.save() - try { - // horizontal position for icon - val w = icon.bounds.width().toFloat() - // minus quarter width as padding - val left = layout.width - w - (w / 4F) - c.translate(left, top.toFloat()) - icon.draw(c) - } finally { - c.restoreToCount(save) - } - } + val save = c.save() + try { + // horizontal position for icon + val w = icon.bounds.width().toFloat() + // minus quarter width as padding + val left = layout.width - w - (w / 4F) + c.translate(left, top.toFloat()) + icon.draw(c) + } finally { + c.restoreToCount(save) + } } + } } \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/CustomExtensionSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/CustomExtensionSample.java deleted file mode 100644 index 484e7f92..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/CustomExtensionSample.java +++ /dev/null @@ -1,436 +0,0 @@ -package io.noties.markwon.app.samples; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.text.style.ReplacementSpan; - -import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.commonmark.node.CustomNode; -import org.commonmark.node.Delimited; -import org.commonmark.node.Node; -import org.commonmark.node.Text; -import org.commonmark.parser.Parser; -import org.commonmark.parser.delimiter.DelimiterProcessor; -import org.commonmark.parser.delimiter.DelimiterRun; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.noties.markwon.AbstractMarkwonPlugin; -import io.noties.markwon.Markwon; -import io.noties.markwon.MarkwonVisitor; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200629163248", - title = "Custom extension", - description = "Custom extension that adds an " + - "icon from resources and renders it as image with " + - "`@ic-name` syntax", - artifacts = MarkwonArtifact.CORE, - tags = {Tag.parsing, Tag.rendering, Tag.plugin, Tag.image, Tag.extension, Tag.span} -) -public class CustomExtensionSample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "# 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" + - ""; - - // note that we haven't registered CorePlugin, as it's the only one that can be - // implicitly deducted and added automatically. All other plugins require explicit - // `usePlugin` call - final Markwon markwon = Markwon.builder(context) - .usePlugin(IconPlugin.create(IconSpanProvider.create(context, 0))) - .build(); - - markwon.setMarkdown(textView, md); - } -} - -class IconPlugin extends AbstractMarkwonPlugin { - - @NonNull - public static IconPlugin create(@NonNull IconSpanProvider iconSpanProvider) { - return new IconPlugin(iconSpanProvider); - } - - private final IconSpanProvider iconSpanProvider; - - IconPlugin(@NonNull IconSpanProvider iconSpanProvider) { - this.iconSpanProvider = iconSpanProvider; - } - - @Override - public void configureParser(@NonNull Parser.Builder builder) { - builder.customDelimiterProcessor(IconProcessor.create()); - } - - @Override - public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { - builder.on(IconNode.class, (visitor, iconNode) -> { - - final String name = iconNode.name(); - final String color = iconNode.color(); - final String size = iconNode.size(); - - if (!TextUtils.isEmpty(name) - && !TextUtils.isEmpty(color) - && !TextUtils.isEmpty(size)) { - - final int length = visitor.length(); - - visitor.builder().append(name); - visitor.setSpans(length, iconSpanProvider.provide(name, color, size)); - visitor.builder().append(' '); - } - }); - } - - @NonNull - @Override - public String processMarkdown(@NonNull String markdown) { - return IconProcessor.prepare(markdown); - } -} - -abstract class IconSpanProvider { - - @SuppressWarnings("SameParameterValue") - @NonNull - public static IconSpanProvider create(@NonNull Context context, @DrawableRes int fallBack) { - return new Impl(context, fallBack); - } - - - @NonNull - public abstract IconSpan provide(@NonNull String name, @NonNull String color, @NonNull String size); - - - private static class Impl extends IconSpanProvider { - - private final Context context; - private final Resources resources; - private final int fallBack; - - Impl(@NonNull Context context, @DrawableRes int fallBack) { - this.context = context; - this.resources = context.getResources(); - this.fallBack = fallBack; - } - - @NonNull - @Override - public IconSpan provide(@NonNull String name, @NonNull String color, @NonNull String size) { - final String resName = iconName(name, color, size); - int resId = resources.getIdentifier(resName, "drawable", context.getPackageName()); - if (resId == 0) { - resId = fallBack; - } - return new IconSpan(getDrawable(resId), IconSpan.ALIGN_CENTER); - } - - - @NonNull - private static String iconName(@NonNull String name, @NonNull String color, @NonNull String size) { - return "ic_" + name + "_" + color + "_" + size + "dp"; - } - - @NonNull - private Drawable getDrawable(int resId) { - //noinspection ConstantConditions - return context.getDrawable(resId); - } - } -} - -class IconSpan extends ReplacementSpan { - - @IntDef({ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER}) - @Retention(RetentionPolicy.CLASS) - @interface Alignment { - } - - public static final int ALIGN_BOTTOM = 0; - public static final int ALIGN_BASELINE = 1; - public static final int ALIGN_CENTER = 2; // will only center if drawable height is less than text line height - - - private final Drawable drawable; - - private final int alignment; - - public IconSpan(@NonNull Drawable drawable, @Alignment int alignment) { - this.drawable = drawable; - this.alignment = alignment; - if (drawable.getBounds().isEmpty()) { - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - } - } - - @Override - public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { - - final Rect rect = drawable.getBounds(); - - if (fm != null) { - fm.ascent = -rect.bottom; - fm.descent = 0; - - fm.top = fm.ascent; - fm.bottom = 0; - } - - return rect.right; - } - - @Override - public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { - - final int b = bottom - drawable.getBounds().bottom; - - final int save = canvas.save(); - try { - final int translationY; - if (ALIGN_CENTER == alignment) { - translationY = b - ((bottom - top - drawable.getBounds().height()) / 2); - } else if (ALIGN_BASELINE == alignment) { - translationY = b - paint.getFontMetricsInt().descent; - } else { - translationY = b; - } - canvas.translate(x, translationY); - drawable.draw(canvas); - } finally { - canvas.restoreToCount(save); - } - } -} - -class IconProcessor implements DelimiterProcessor { - - @NonNull - public static IconProcessor create() { - return new IconProcessor(); - } - - // ic-home-black-24 - private static final Pattern PATTERN = Pattern.compile("ic-(\\w+)-(\\w+)-(\\d+)"); - - private static final String TO_FIND = IconNode.DELIMITER_STRING + "ic-"; - - /** - * Should be used when input string does not wrap icon definition with `@` from both ends. - * So, `@ic-home-white-24` would become `@ic-home-white-24@`. This way parsing is easier - * and more predictable (cannot specify multiple ending delimiters, as we would require them: - * space, newline, end of a document, and a lot of more) - * - * @param input to process - * @return processed string - * @see #prepare(StringBuilder) - */ - @NonNull - public static String prepare(@NonNull String input) { - final StringBuilder builder = new StringBuilder(input); - prepare(builder); - return builder.toString(); - } - - public static void prepare(@NonNull StringBuilder builder) { - - int start = builder.indexOf(TO_FIND); - int end; - - while (start > -1) { - - end = iconDefinitionEnd(start + TO_FIND.length(), builder); - - // if we match our pattern, append `@` else ignore - if (iconDefinitionValid(builder.subSequence(start + 1, end))) { - builder.insert(end, '@'); - } - - // move to next - start = builder.indexOf(TO_FIND, end); - } - } - - @Override - public char getOpeningCharacter() { - return IconNode.DELIMITER; - } - - @Override - public char getClosingCharacter() { - return IconNode.DELIMITER; - } - - @Override - public int getMinLength() { - return 1; - } - - @Override - public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) { - return opener.length() >= 1 && closer.length() >= 1 ? 1 : 0; - } - - @Override - public void process(Text opener, Text closer, int delimiterUse) { - - final IconGroupNode iconGroupNode = new IconGroupNode(); - - final Node next = opener.getNext(); - - boolean handled = false; - - // process only if we have exactly one Text node - if (next instanceof Text && next.getNext() == closer) { - - final String text = ((Text) next).getLiteral(); - - if (!TextUtils.isEmpty(text)) { - - // attempt to match - final Matcher matcher = PATTERN.matcher(text); - if (matcher.matches()) { - final IconNode iconNode = new IconNode( - matcher.group(1), - matcher.group(2), - matcher.group(3) - ); - iconGroupNode.appendChild(iconNode); - next.unlink(); - handled = true; - } - } - } - - if (!handled) { - - // restore delimiters if we didn't match - - iconGroupNode.appendChild(new Text(IconNode.DELIMITER_STRING)); - - Node node; - for (Node tmp = opener.getNext(); tmp != null && tmp != closer; tmp = node) { - node = tmp.getNext(); - // append a child anyway - iconGroupNode.appendChild(tmp); - } - - iconGroupNode.appendChild(new Text(IconNode.DELIMITER_STRING)); - } - - opener.insertBefore(iconGroupNode); - } - - private static int iconDefinitionEnd(int index, @NonNull StringBuilder builder) { - - // all spaces, new lines, non-words or digits, - - char c; - - int end = -1; - for (int i = index; i < builder.length(); i++) { - c = builder.charAt(i); - if (Character.isWhitespace(c) - || !(Character.isLetterOrDigit(c) || c == '-' || c == '_')) { - end = i; - break; - } - } - - if (end == -1) { - end = builder.length(); - } - - return end; - } - - private static boolean iconDefinitionValid(@NonNull CharSequence cs) { - final Matcher matcher = PATTERN.matcher(cs); - return matcher.matches(); - } -} - -class IconNode extends CustomNode implements Delimited { - - public static final char DELIMITER = '@'; - - public static final String DELIMITER_STRING = "" + DELIMITER; - - - private final String name; - - private final String color; - - private final String size; - - public IconNode(@NonNull String name, @NonNull String color, @NonNull String size) { - this.name = name; - this.color = color; - this.size = size; - } - - @NonNull - public String name() { - return name; - } - - @NonNull - public String color() { - return color; - } - - @NonNull - public String size() { - return size; - } - - @Override - public String getOpeningDelimiter() { - return DELIMITER_STRING; - } - - @Override - public String getClosingDelimiter() { - return DELIMITER_STRING; - } - - @Override - @NonNull - public String toString() { - return "IconNode{" + - "name='" + name + '\'' + - ", color='" + color + '\'' + - ", size='" + size + '\'' + - '}'; - } -} - -class IconGroupNode extends CustomNode { - -} diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/DelimiterProcessorSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/DelimiterProcessorSample.java deleted file mode 100644 index 9cb90984..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/DelimiterProcessorSample.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.noties.markwon.app.samples; - -import androidx.annotation.NonNull; - -import org.commonmark.node.Emphasis; -import org.commonmark.node.Node; -import org.commonmark.node.Text; -import org.commonmark.parser.Parser; -import org.commonmark.parser.delimiter.DelimiterProcessor; -import org.commonmark.parser.delimiter.DelimiterRun; - -import io.noties.markwon.AbstractMarkwonPlugin; -import io.noties.markwon.Markwon; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200630194017", - title = "Custom delimiter processor", - description = "Custom parsing delimiter processor with `?` character", - artifacts = MarkwonArtifact.CORE, - tags = Tag.parsing -) -public class DelimiterProcessorSample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "?hello? there!"; - - final Markwon markwon = Markwon.builder(context) - .usePlugin(new AbstractMarkwonPlugin() { - @Override - public void configureParser(@NonNull Parser.Builder builder) { - builder.customDelimiterProcessor(new QuestionDelimiterProcessor()); - } - }) - .build(); - - markwon.setMarkdown(textView, md); - } -} - -class QuestionDelimiterProcessor implements DelimiterProcessor { - - @Override - public char getOpeningCharacter() { - return '?'; - } - - @Override - public char getClosingCharacter() { - return '?'; - } - - @Override - public int getMinLength() { - return 1; - } - - @Override - public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) { - if (opener.length() >= 1 && closer.length() >= 1) { - return 1; - } - return 0; - } - - @Override - public void process(Text opener, Text closer, int delimiterUse) { - final Node node = new Emphasis(); - - Node tmp = opener.getNext(); - while (tmp != null && tmp != closer) { - Node next = tmp.getNext(); - node.appendChild(tmp); - tmp = next; - } - - opener.insertAfter(node); - } -} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/DisableNodeSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/DisableNodeSample.java index 398f9f78..8bfd7d17 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/DisableNodeSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/DisableNodeSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Heading; +import com.vladsch.flexmark.ast.Heading; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/EnabledBlockTypesSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/EnabledBlockTypesSample.kt deleted file mode 100644 index 9a0e7bcc..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/EnabledBlockTypesSample.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.noties.markwon.app.samples - -import io.noties.markwon.AbstractMarkwonPlugin -import io.noties.markwon.Markwon -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample -import io.noties.markwon.core.CorePlugin -import io.noties.markwon.sample.annotations.MarkwonArtifact -import io.noties.markwon.sample.annotations.MarkwonSampleInfo -import io.noties.markwon.sample.annotations.Tag -import org.commonmark.node.BlockQuote -import org.commonmark.parser.Parser - -@MarkwonSampleInfo( - id = "20200627075012", - title = "Enabled markdown blocks", - description = "Modify/inspect enabled by `CorePlugin` block types. " + - "Disable quotes or other blocks from being parsed", - artifacts = [MarkwonArtifact.CORE], - tags = [Tag.parsing, Tag.block, Tag.plugin] -) -class EnabledBlockTypesSample : MarkwonTextViewSample() { - override fun render() { - val md = """ - # Heading - ## Second level - > Quote is not handled - """.trimIndent() - - val markwon = Markwon.builder(context) - .usePlugin(object : AbstractMarkwonPlugin() { - override fun configureParser(builder: Parser.Builder) { - // obtain all enabled block types - val enabledBlockTypes = CorePlugin.enabledBlockTypes() - // it is safe to modify returned collection - // remove quotes - enabledBlockTypes.remove(BlockQuote::class.java) - - builder.enabledBlockTypes(enabledBlockTypes) - } - }) - .build() - - markwon.setMarkdown(textView, md) - } -} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ExcludeFromParsingSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/ExcludeFromParsingSample.kt index 95c34f62..dd3b44ae 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ExcludeFromParsingSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ExcludeFromParsingSample.kt @@ -16,7 +16,7 @@ import java.util.regex.Pattern artifacts = [MarkwonArtifact.CORE], tags = [Tag.parsing] ) -class ExcludeFromParsingSample : MarkwonTextViewSample() { +public class ExcludeFromParsingSample : MarkwonTextViewSample() { override fun render() { // cannot have continuous markdown between parts (so a node started in one part and ended in other) diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java deleted file mode 100644 index 884d4c5c..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java +++ /dev/null @@ -1,110 +0,0 @@ -package io.noties.markwon.app.samples; - -import androidx.annotation.NonNull; - -import org.commonmark.node.Link; -import org.commonmark.node.Node; -import org.commonmark.parser.InlineParserFactory; -import org.commonmark.parser.Parser; - -import java.util.regex.Pattern; - -import io.noties.markwon.AbstractMarkwonPlugin; -import io.noties.markwon.Markwon; -import io.noties.markwon.app.BuildConfig; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.inlineparser.InlineProcessor; -import io.noties.markwon.inlineparser.MarkwonInlineParser; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200629162023", - title = "User mention and issue (via text)", - description = "Github-like user mention and issue " + - "rendering via `CorePlugin.OnTextAddedListener`", - artifacts = {MarkwonArtifact.CORE, MarkwonArtifact.INLINE_PARSER}, - tags = {Tag.parsing, Tag.textAddedListener, Tag.rendering} -) -public class GithubUserIssueInlineParsingSample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "# Custom Extension 2\n" + - "\n" + - "This is an issue #1\n" + - "Done by @noties and other @dude"; - - final InlineParserFactory inlineParserFactory = MarkwonInlineParser.factoryBuilder() - // include all current defaults (otherwise will be empty - contain only our inline-processors) - // included by default, to create factory-builder without defaults call `factoryBuilderNoDefaults` -// .includeDefaults() - .addInlineProcessor(new IssueInlineProcessor()) - .addInlineProcessor(new UserInlineProcessor()) - .build(); - - final Markwon markwon = Markwon.builder(context) - .usePlugin(new AbstractMarkwonPlugin() { - @Override - public void configureParser(@NonNull Parser.Builder builder) { - builder.inlineParserFactory(inlineParserFactory); - } - }) - .build(); - - markwon.setMarkdown(textView, md); - } -} - -class IssueInlineProcessor extends InlineProcessor { - - private static final Pattern RE = Pattern.compile("\\d+"); - - @Override - public char specialCharacter() { - return '#'; - } - - @Override - protected Node parse() { - final String id = match(RE); - if (id != null) { - final Link link = new Link(createIssueOrPullRequestLinkDestination(id), null); - link.appendChild(text("#" + id)); - return link; - } - return null; - } - - @NonNull - private static String createIssueOrPullRequestLinkDestination(@NonNull String id) { - return BuildConfig.GIT_REPOSITORY + "/issues/" + id; - } -} - -class UserInlineProcessor extends InlineProcessor { - - private static final Pattern RE = Pattern.compile("\\w+"); - - @Override - public char specialCharacter() { - return '@'; - } - - @Override - protected Node parse() { - final String user = match(RE); - if (user != null) { - final Link link = new Link(createUserLinkDestination(user), null); - link.appendChild(text("@" + user)); - return link; - } - return null; - } - - @NonNull - private static String createUserLinkDestination(@NonNull String user) { - return "https://github.com/" + user; - } -} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java index 68f1e9c2..c9225018 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Link; +import com.vladsch.flexmark.ast.Link; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingColorSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingColorSample.java index a7ed4c57..4b6b16a0 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingColorSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingColorSample.java @@ -5,7 +5,7 @@ import android.text.style.ForegroundColorSpan; import androidx.annotation.NonNull; -import org.commonmark.node.Heading; +import com.vladsch.flexmark.ast.Heading; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceBlockHandlerSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceBlockHandlerSample.java index ae0f1083..733f41f0 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceBlockHandlerSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceBlockHandlerSample.java @@ -2,8 +2,8 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Heading; -import org.commonmark.node.Node; +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.BlockHandlerDef; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceSample.java index be3e3f54..99fb5853 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/HeadingNoSpaceSample.java @@ -2,7 +2,7 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Heading; +import com.vladsch.flexmark.ast.Heading; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/InlinePluginNoDefaultsSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/InlinePluginNoDefaultsSample.java deleted file mode 100644 index 1184acb2..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/InlinePluginNoDefaultsSample.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.noties.markwon.app.samples; - -import io.noties.markwon.Markwon; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.inlineparser.MarkwonInlineParser; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200629170857", - title = "Inline parsing without defaults", - description = "Configure inline parser plugin to **not** have any **inline** parsing", - artifacts = {MarkwonArtifact.INLINE_PARSER}, - tags = {Tag.parsing} -) -public class InlinePluginNoDefaultsSample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "# Heading\n" + - "`code` inlined and **bold** here"; - - final Markwon markwon = Markwon.builder(context) - .usePlugin(MarkwonInlineParserPlugin.create(MarkwonInlineParser.factoryBuilderNoDefaults())) -// .usePlugin(MarkwonInlineParserPlugin.create(MarkwonInlineParser.factoryBuilderNoDefaults(), factoryBuilder -> { -// // if anything, they can be included here -//// factoryBuilder.includeDefaults() -// })) - .build(); - - markwon.setMarkdown(textView, md); - } -} diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/LetterOrderedListSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/LetterOrderedListSample.java index f8b99f88..be38c0ed 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/LetterOrderedListSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/LetterOrderedListSample.java @@ -5,10 +5,10 @@ import android.util.SparseIntArray; import androidx.annotation.NonNull; -import org.commonmark.node.BulletList; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.OrderedList; +import com.vladsch.flexmark.ast.BulletList; +import com.vladsch.flexmark.ast.ListItem; +import com.vladsch.flexmark.ast.OrderedList; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/LinkRemoveUnderlineSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/LinkRemoveUnderlineSample.java index a39a833d..da805644 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/LinkRemoveUnderlineSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/LinkRemoveUnderlineSample.java @@ -6,7 +6,7 @@ import android.text.style.UpdateAppearance; import androidx.annotation.NonNull; -import org.commonmark.node.Link; +import com.vladsch.flexmark.ast.Link; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/LinkTitleSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/LinkTitleSample.java index 0bc93e6e..6edba2a0 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/LinkTitleSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/LinkTitleSample.java @@ -8,7 +8,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Link; +import com.vladsch.flexmark.ast.Link; import java.util.Locale; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/NoParsingSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/NoParsingSample.java deleted file mode 100644 index 55ed7fe9..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/NoParsingSample.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.noties.markwon.app.samples; - -import androidx.annotation.NonNull; - -import org.commonmark.parser.Parser; - -import java.util.Collections; - -import io.noties.markwon.AbstractMarkwonPlugin; -import io.noties.markwon.Markwon; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.inlineparser.MarkwonInlineParser; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200629171212", - title = "No parsing", - description = "All commonmark parsing is disabled (both inlines and blocks)", - artifacts = MarkwonArtifact.CORE, - tags = {Tag.parsing, Tag.rendering} -) -public class NoParsingSample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "# Heading\n" + - "[link](#) was _here_ and `then` and it was:\n" + - "> a quote\n" + - "```java\n" + - "final int someJavaCode = 0;\n" + - "```\n"; - - final Markwon markwon = Markwon.builder(context) - // disable inline parsing - .usePlugin(MarkwonInlineParserPlugin.create(MarkwonInlineParser.factoryBuilderNoDefaults())) - .usePlugin(new AbstractMarkwonPlugin() { - @Override - public void configureParser(@NonNull Parser.Builder builder) { - builder.enabledBlockTypes(Collections.emptySet()); - } - }) - .build(); - - markwon.setMarkdown(textView, md); - } -} diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ParagraphSpanStyle.java b/app-sample/src/main/java/io/noties/markwon/app/samples/ParagraphSpanStyle.java index 9174584e..8daa2075 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ParagraphSpanStyle.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ParagraphSpanStyle.java @@ -5,7 +5,7 @@ import android.text.style.ForegroundColorSpan; import androidx.annotation.NonNull; -import org.commonmark.node.Paragraph; +import com.vladsch.flexmark.ast.Paragraph; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/RecyclerSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/RecyclerSample.java index 599e1a18..0093e1ed 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/RecyclerSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/RecyclerSample.java @@ -3,8 +3,8 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; -import org.commonmark.ext.gfm.tables.TableBlock; -import org.commonmark.node.FencedCodeBlock; +import com.vladsch.flexmark.ast.FencedCodeBlock; +import com.vladsch.flexmark.ext.tables.TableBlock; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; @@ -59,7 +59,7 @@ public class RecyclerSample extends MarkwonRecyclerViewSample { // NB the `trim` operation on literal (as code will have a new line at the end) final CharSequence code = visitor.configuration() .syntaxHighlight() - .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim()); + .highlight(fencedCodeBlock.getInfo().unescape(), fencedCodeBlock.toAstString(false).trim()); visitor.builder().append(code); }); } diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ThematicBreakBottomMarginSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/ThematicBreakBottomMarginSample.java index 5f0edaf6..5725d279 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ThematicBreakBottomMarginSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ThematicBreakBottomMarginSample.java @@ -2,8 +2,8 @@ package io.noties.markwon.app.samples; import androidx.annotation.NonNull; -import org.commonmark.node.Node; -import org.commonmark.node.ThematicBreak; +import com.vladsch.flexmark.ast.ThematicBreak; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.BlockHandlerDef; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt index 7fadb663..f05d9676 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt @@ -19,7 +19,7 @@ import io.noties.markwon.sample.annotations.Tag artifacts = [MarkwonArtifact.CORE, MarkwonArtifact.IMAGE], tags = [Tag.toast, Tag.hack] ) -class ToastDynamicContentSample : MarkwonTextViewSample() { +public class ToastDynamicContentSample : MarkwonTextViewSample() { override fun render() { val md = """ # Head! diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/basics/SimpleWalkthrough.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/basics/SimpleWalkthrough.kt index 26c99c2f..ba397565 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/basics/SimpleWalkthrough.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/basics/SimpleWalkthrough.kt @@ -1,13 +1,13 @@ package io.noties.markwon.app.samples.basics import android.text.Spanned +import com.vladsch.flexmark.util.ast.Node import io.noties.markwon.Markwon import io.noties.markwon.app.sample.ui.MarkwonTextViewSample import io.noties.markwon.core.CorePlugin import io.noties.markwon.sample.annotations.MarkwonArtifact import io.noties.markwon.sample.annotations.MarkwonSampleInfo import io.noties.markwon.sample.annotations.Tag -import org.commonmark.node.Node @MarkwonSampleInfo( id = "20200626153426", diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/inlineparsing/InlineParsingTooltipSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/inlineparsing/InlineParsingTooltipSample.java index ac2d962e..cc933abe 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/inlineparsing/InlineParsingTooltipSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/inlineparsing/InlineParsingTooltipSample.java @@ -15,8 +15,9 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.CustomNode; -import org.commonmark.node.Node; + +import com.vladsch.flexmark.parser.InlineParser; +import com.vladsch.flexmark.parser.internal.InlineParserImpl; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,8 +27,6 @@ import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonVisitor; import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; import io.noties.markwon.image.ImagesPlugin; -import io.noties.markwon.inlineparser.InlineProcessor; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.sample.annotations.MarkwonArtifact; import io.noties.markwon.sample.annotations.MarkwonSampleInfo; import io.noties.markwon.sample.annotations.Tag; @@ -79,7 +78,7 @@ public class InlineParsingTooltipSample extends MarkwonTextViewSample { } } -class TooltipInlineProcessor extends InlineProcessor { +class TooltipInlineProcessor extends InlineParserImpl { // NB! without bang // `\\{` is required (although marked as redundant), without it - runtime crash diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexLegacySample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexLegacySample.java deleted file mode 100644 index bedd3982..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexLegacySample.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.noties.markwon.app.samples.latex; - -import io.noties.markwon.Markwon; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.app.samples.latex.shared.LatexHolder; -import io.noties.markwon.ext.latex.JLatexMathPlugin; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200701090335", - title = "LaTeX blocks in legacy mode", - description = "Sample using _legacy_ LaTeX block parsing (pre `4.3.0` Markwon version)", - artifacts = MarkwonArtifact.EXT_LATEX, - tags = Tag.rendering -) -public class LatexLegacySample extends MarkwonTextViewSample { - @Override - public void render() { - final String md = "" + - "# LaTeX legacy\n" + - "There are no inlines in previous versions, only blocks:\n" + - "$$\n" + - "" + LatexHolder.LATEX_BOXES + "\n" + - "$$\n" + - "yeah"; - - final Markwon markwon = Markwon.builder(context) - .usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> { - builder.blocksLegacy(true); - })) - .build(); - - markwon.setMarkdown(textView, md); - } -} diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexOmegaSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexOmegaSample.java index d017018a..814a44e6 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexOmegaSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexOmegaSample.java @@ -3,7 +3,6 @@ package io.noties.markwon.app.samples.latex; import io.noties.markwon.Markwon; import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; import io.noties.markwon.ext.latex.JLatexMathPlugin; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.sample.annotations.MarkwonArtifact; import io.noties.markwon.sample.annotations.MarkwonSampleInfo; import io.noties.markwon.sample.annotations.Tag; @@ -27,7 +26,6 @@ public class LatexOmegaSample extends MarkwonTextViewSample { "$$\\Omega$$"; final Markwon markwon = Markwon.builder(context) - .usePlugin(MarkwonInlineParserPlugin.create()) .usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> { builder.inlinesEnabled(true); })) diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexThemeSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexThemeSample.java index b3b26679..722c02ff 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexThemeSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/latex/LatexThemeSample.java @@ -8,7 +8,6 @@ import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; import io.noties.markwon.app.samples.latex.shared.LatexHolder; import io.noties.markwon.ext.latex.JLatexMathPlugin; import io.noties.markwon.ext.latex.JLatexMathTheme; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.sample.annotations.MarkwonArtifact; import io.noties.markwon.sample.annotations.MarkwonSampleInfo; import io.noties.markwon.sample.annotations.Tag; @@ -35,7 +34,6 @@ public class LatexThemeSample extends MarkwonTextViewSample { final int blockPadding = (int) (16 * context.getResources().getDisplayMetrics().density + 0.5F); final Markwon markwon = Markwon.builder(context) - .usePlugin(MarkwonInlineParserPlugin.create()) .usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> { builder.inlinesEnabled(true); builder.theme() diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/notification/RemoteViewsSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/notification/RemoteViewsSample.java index 2d11f19d..73760dae 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/notification/RemoteViewsSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/notification/RemoteViewsSample.java @@ -13,13 +13,13 @@ import android.widget.RemoteViews; import androidx.annotation.NonNull; -import org.commonmark.ext.gfm.strikethrough.Strikethrough; -import org.commonmark.node.BlockQuote; -import org.commonmark.node.Code; -import org.commonmark.node.Emphasis; -import org.commonmark.node.Heading; -import org.commonmark.node.ListItem; -import org.commonmark.node.StrongEmphasis; +import com.vladsch.flexmark.ast.BlockQuote; +import com.vladsch.flexmark.ast.Code; +import com.vladsch.flexmark.ast.Emphasis; +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.ListItem; +import com.vladsch.flexmark.ast.StrongEmphasis; +import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/parser/CustomHeadingParserSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/parser/CustomHeadingParserSample.kt deleted file mode 100644 index f539733d..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/parser/CustomHeadingParserSample.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.noties.markwon.app.samples.parser - -import io.noties.markwon.AbstractMarkwonPlugin -import io.noties.markwon.Markwon -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample -import io.noties.markwon.core.CorePlugin -import io.noties.markwon.sample.annotations.MarkwonArtifact -import io.noties.markwon.sample.annotations.MarkwonSampleInfo -import io.noties.markwon.sample.annotations.Tag -import org.commonmark.node.Heading -import org.commonmark.parser.Parser -import org.commonmark.parser.block.BlockParserFactory -import org.commonmark.parser.block.BlockStart -import org.commonmark.parser.block.MatchedBlockParser -import org.commonmark.parser.block.ParserState - -@MarkwonSampleInfo( - id = "20201111221207", - title = "Custom heading parser", - description = "Custom heading block parser. Actual parser is not implemented", - artifacts = [MarkwonArtifact.CORE], - tags = [Tag.parsing, Tag.heading] -) -class CustomHeadingParserSample : MarkwonTextViewSample() { - override fun render() { - val md = "#Head" - val markwon = Markwon.builder(context) - .usePlugin(object : AbstractMarkwonPlugin() { - override fun configureParser(builder: Parser.Builder) { - val enabled = CorePlugin.enabledBlockTypes() - .filter { it != Heading::class.java } - .toSet() - builder.enabledBlockTypes(enabled) - builder.customBlockParserFactory(MyHeadingBlockParserFactory) - } - }) - .build() - markwon.setMarkdown(textView, md) - } - - object MyHeadingBlockParserFactory : BlockParserFactory { - override fun tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): BlockStart? { - // TODO("Not yet implemented") - return null - } - } -} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/plugins/TableOfContentsSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/plugins/TableOfContentsSample.java deleted file mode 100644 index fe11bfd9..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/plugins/TableOfContentsSample.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.noties.markwon.app.samples.plugins; - -import androidx.annotation.NonNull; - -import org.commonmark.node.AbstractVisitor; -import org.commonmark.node.BulletList; -import org.commonmark.node.CustomBlock; -import org.commonmark.node.Heading; -import org.commonmark.node.Link; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -import io.noties.markwon.AbstractMarkwonPlugin; -import io.noties.markwon.Markwon; -import io.noties.markwon.MarkwonVisitor; -import io.noties.markwon.app.R; -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.app.samples.plugins.shared.AnchorHeadingPlugin; -import io.noties.markwon.core.SimpleBlockNodeVisitor; -import io.noties.markwon.sample.annotations.MarkwonArtifact; -import io.noties.markwon.sample.annotations.MarkwonSampleInfo; -import io.noties.markwon.sample.annotations.Tag; - -@MarkwonSampleInfo( - id = "20200629161226", - title = "Table of contents", - description = "Sample plugin that adds a table of contents header", - artifacts = MarkwonArtifact.CORE, - tags = {Tag.rendering, Tag.plugin} -) -public class TableOfContentsSample extends MarkwonTextViewSample { - - @Override - public void render() { - final String lorem = context.getString(R.string.lorem); - final String md = "" + - "# First\n" + - "" + lorem + "\n\n" + - "# Second\n" + - "" + lorem + "\n\n" + - "## Second level\n\n" + - "" + lorem + "\n\n" + - "### Level 3\n\n" + - "" + lorem + "\n\n" + - "# First again\n" + - "" + lorem + "\n\n"; - - final Markwon markwon = Markwon.builder(context) - .usePlugin(new TableOfContentsPlugin()) - // NB! plugin is defined in `AnchorSample` file - .usePlugin(new AnchorHeadingPlugin((view, top) -> scrollView.smoothScrollTo(0, top))) - .build(); - - markwon.setMarkdown(textView, md); - } -} - -class TableOfContentsPlugin extends AbstractMarkwonPlugin { - @Override - public void configure(@NonNull Registry registry) { - // just to make it explicit - registry.require(AnchorHeadingPlugin.class); - } - - @Override - public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { - builder.on(TableOfContentsBlock.class, new SimpleBlockNodeVisitor()); - } - - @Override - public void beforeRender(@NonNull Node node) { - - // custom block to hold TOC - final TableOfContentsBlock block = new TableOfContentsBlock(); - - // create TOC title - { - final Text text = new Text("Table of contents"); - final Heading heading = new Heading(); - // important one - set TOC heading level - heading.setLevel(1); - heading.appendChild(text); - block.appendChild(heading); - } - - final HeadingVisitor visitor = new HeadingVisitor(block); - node.accept(visitor); - - // make it the very first node in rendered markdown - node.prependChild(block); - } - - private static class HeadingVisitor extends AbstractVisitor { - - private final BulletList bulletList = new BulletList(); - private final StringBuilder builder = new StringBuilder(); - private boolean isInsideHeading; - - HeadingVisitor(@NonNull Node node) { - node.appendChild(bulletList); - } - - @Override - public void visit(Heading heading) { - this.isInsideHeading = true; - try { - // reset build from previous content - builder.setLength(0); - - // obtain level (can additionally filter by level, to skip lower ones) - final int level = heading.getLevel(); - - // build heading title - visitChildren(heading); - - // initial list item - final ListItem listItem = new ListItem(); - - Node parent = listItem; - Node node = listItem; - - for (int i = 1; i < level; i++) { - final ListItem li = new ListItem(); - final BulletList bulletList = new BulletList(); - bulletList.appendChild(li); - parent.appendChild(bulletList); - parent = li; - node = li; - } - - final String content = builder.toString(); - final Link link = new Link("#" + AnchorHeadingPlugin.createAnchor(content), null); - final Text text = new Text(content); - link.appendChild(text); - node.appendChild(link); - bulletList.appendChild(listItem); - - - } finally { - isInsideHeading = false; - } - } - - @Override - public void visit(Text text) { - // can additionally check if we are building heading (to skip all other texts) - if (isInsideHeading) { - builder.append(text.getLiteral()); - } - } - } - - private static class TableOfContentsBlock extends CustomBlock { - } -} diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/table/TableLatexSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/table/TableLatexSample.java index 26f4f654..1a0dbc92 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/table/TableLatexSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/table/TableLatexSample.java @@ -5,7 +5,6 @@ import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; import io.noties.markwon.ext.latex.JLatexMathPlugin; import io.noties.markwon.ext.tables.TablePlugin; import io.noties.markwon.image.ImagesPlugin; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.sample.annotations.MarkwonArtifact; import io.noties.markwon.sample.annotations.MarkwonSampleInfo; import io.noties.markwon.sample.annotations.Tag; @@ -34,7 +33,6 @@ public class TableLatexSample extends MarkwonTextViewSample { "\n"; final Markwon markwon = Markwon.builder(context) - .usePlugin(MarkwonInlineParserPlugin.create()) .usePlugin(ImagesPlugin.create()) .usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> builder.inlinesEnabled(true))) .usePlugin(TablePlugin.create(context)) diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateNestedSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateNestedSample.kt deleted file mode 100644 index cf275342..00000000 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateNestedSample.kt +++ /dev/null @@ -1,157 +0,0 @@ -package io.noties.markwon.app.samples.tasklist - -import android.text.style.ClickableSpan -import android.view.View -import io.noties.debug.Debug -import io.noties.markwon.AbstractMarkwonPlugin -import io.noties.markwon.Markwon -import io.noties.markwon.MarkwonVisitor -import io.noties.markwon.SoftBreakAddsNewLinePlugin -import io.noties.markwon.SpannableBuilder -import io.noties.markwon.app.sample.ui.MarkwonTextViewSample -import io.noties.markwon.ext.tasklist.TaskListItem -import io.noties.markwon.ext.tasklist.TaskListPlugin -import io.noties.markwon.ext.tasklist.TaskListProps -import io.noties.markwon.ext.tasklist.TaskListSpan -import io.noties.markwon.sample.annotations.MarkwonArtifact -import io.noties.markwon.sample.annotations.MarkwonSampleInfo -import io.noties.markwon.sample.annotations.Tag -import org.commonmark.node.AbstractVisitor -import org.commonmark.node.Block -import org.commonmark.node.HardLineBreak -import org.commonmark.node.Node -import org.commonmark.node.Paragraph -import org.commonmark.node.SoftLineBreak -import org.commonmark.node.Text - -@MarkwonSampleInfo( - id = "20201228120444", - title = "Task list mutate nested", - description = "Task list mutation with nested items", - artifacts = [MarkwonArtifact.EXT_TASKLIST], - tags = [Tag.plugin] -) -class TaskListMutateNestedSample : MarkwonTextViewSample() { - override fun render() { - val md = """ - # Task list - - [ ] not done - - [X] done - - [ ] nested not done - and text and textand text and text - - [X] nested done - """.trimIndent() - - val markwon = Markwon.builder(context) - .usePlugin(TaskListPlugin.create(context)) - .usePlugin(SoftBreakAddsNewLinePlugin.create()) - .usePlugin(object : AbstractMarkwonPlugin() { - override fun configureVisitor(builder: MarkwonVisitor.Builder) { - builder.on(TaskListItem::class.java) { visitor, node -> - - val length = visitor.length() - - visitor.visitChildren(node) - - TaskListProps.DONE.set(visitor.renderProps(), node.isDone) - - val spans = visitor.configuration() - .spansFactory() - .get(TaskListItem::class.java) - ?.getSpans(visitor.configuration(), visitor.renderProps()) - - if (spans != null) { - - val taskListSpan = if (spans is Array<*>) { - spans.first { it is TaskListSpan } as? TaskListSpan - } else { - spans as? TaskListSpan - } - - Debug.i("#### ${visitor.builder().substring(length, length + 3)}") - val content = TaskListContextVisitor.contentLength(node) - Debug.i("#### content: $content, '${visitor.builder().subSequence(length, length + content)}'") - - if (content > 0 && taskListSpan != null) { - // maybe additionally identify this task list (for persistence) - visitor.builder().setSpan( - ToggleTaskListSpan(taskListSpan, visitor.builder().substring(length, length + content)), - length, - length + content - ) - } - } - - SpannableBuilder.setSpans( - visitor.builder(), - spans, - length, - visitor.length() - ) - - if (visitor.hasNext(node)) { - visitor.ensureNewLine() - } - } - } - }) - .build() - - markwon.setMarkdown(textView, md) - } - - class TaskListContextVisitor : AbstractVisitor() { - - companion object { - fun contentLength(node: Node): Int { - val visitor = TaskListContextVisitor() - visitor.visitChildren(node) - return visitor.contentLength - } - } - - var contentLength: Int = 0 - - override fun visit(text: Text) { - super.visit(text) - contentLength += text.literal.length - } - - // NB! if count both soft and hard breaks as having length of 1 - override fun visit(softLineBreak: SoftLineBreak?) { - super.visit(softLineBreak) - contentLength += 1 - } - - // NB! if count both soft and hard breaks as having length of 1 - override fun visit(hardLineBreak: HardLineBreak?) { - super.visit(hardLineBreak) - contentLength += 1 - } - - override fun visitChildren(parent: Node) { - var node = parent.firstChild - while (node != null) { - // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no - // node after visiting it. So get the next node before visiting. - val next = node.next - if (node is Block && node !is Paragraph) { - break - } - node.accept(this) - node = next - } - } - } - - class ToggleTaskListSpan( - val span: TaskListSpan, - val content: String - ) : ClickableSpan() { - override fun onClick(widget: View) { - span.isDone = !span.isDone - widget.invalidate() - Debug.i("task-list click, isDone: ${span.isDone}, content: '$content'") - } - } -} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateSample.java index bc907c37..abdb1622 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/tasklist/TaskListMutateSample.java @@ -14,7 +14,6 @@ import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonSpansFactory; import io.noties.markwon.SpanFactory; import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; -import io.noties.markwon.ext.tasklist.TaskListItem; import io.noties.markwon.ext.tasklist.TaskListPlugin; import io.noties.markwon.ext.tasklist.TaskListSpan; import io.noties.markwon.sample.annotations.MarkwonArtifact; @@ -23,6 +22,8 @@ import io.noties.markwon.sample.annotations.Tag; import static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD; +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListItem; + @MarkwonSampleInfo( id = "20200702140901", title = "GFM task list mutate", diff --git a/build.gradle b/build.gradle index 8d24619f..0b7b868e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,14 @@ buildscript { - ext.kotlin_version = '1.4.10' + ext.kotlin_version = '1.7.20' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' + classpath 'com.android.tools.build:gradle:7.4.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.28.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" } } @@ -27,16 +28,23 @@ allprojects { tasks.withType(Javadoc) { enabled = false } + + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + kotlinOptions { + // Treat all Kotlin warnings as errors + // allWarningsAsErrors = true + + freeCompilerArgs += '-opt-in=kotlin.RequiresOptIn' + // Set JVM target to 11 + jvmTarget = JavaVersion.VERSION_11 + } + } } task clean(type: Delete) { delete rootProject.buildDir } -wrapper { - gradleVersion '6.1.1' - distributionType 'all' -} if (hasProperty('local')) { if (!hasProperty('LOCAL_MAVEN_URL')) { @@ -49,10 +57,10 @@ if (hasProperty('local')) { ext { config = [ - 'build-tools' : '29.0.3', - 'compile-sdk' : 29, - 'target-sdk' : 29, - 'min-sdk' : 16, + 'build-tools' : '33.0.1', + 'compile-sdk' : 33, + 'target-sdk' : 33, + 'min-sdk' : 21, 'push-aar-gradle': 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' ] @@ -72,6 +80,7 @@ ext { 'x-appcompat' : 'androidx.appcompat:appcompat:1.1.0', 'x-cardview' : 'androidx.cardview:cardview:1.0.0', 'x-fragment' : 'androidx.fragment:fragment:1.0.0', + 'flexmark' : 'com.vladsch.flexmark:flexmark-all:0.64.0', 'commonmark' : "com.atlassian.commonmark:commonmark:$commonMarkVersion", 'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion", 'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion", diff --git a/gradle.properties b/gradle.properties index f7ef5b5d..35d2bac2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,19 +5,21 @@ org.gradle.configureondemand=true android.useAndroidX=true android.enableJetifier=true -android.enableBuildCache=true -android.buildCacheDir=build/pre-dex-cache -VERSION_NAME=4.6.2 +VERSION_NAME=5.0.0 GROUP=io.noties.markwon POM_DESCRIPTION=Markwon markdown for Android -POM_URL=https://github.com/noties/Markwon -POM_SCM_URL=https://github.com/noties/Markwon -POM_SCM_CONNECTION=scm:git:git://github.com/noties/Markwon.git -POM_SCM_DEV_CONNECTION=scm:git:git://github.com/noties/Markwon.git +POM_URL=https://github.com/cpacm/Markwon +POM_SCM_URL=https://github.com/cpacm/Markwon +POM_SCM_CONNECTION=scm:git:git://github.com/cpacm/Markwon.git +POM_SCM_DEV_CONNECTION=scm:git:git://github.com/cpacm/Markwon.git POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo -POM_DEVELOPER_ID=noties -POM_DEVELOPER_NAME=Dimitry Ivanov \ No newline at end of file +POM_DEVELOPER_ID=cpacm +POM_DEVELOPER_NAME=cpacm +POM_DEVELOPER_EMAIL=cpacm@8bgm.com +POM_COMPANY=Github +POM_OFFICIAL_WEBSITE=https://github.com/cpacm/Markwon +POM_SCM_ISSUES=https://github.com/cpacm/Markwon/issues \ No newline at end of file diff --git a/gradle/publishAllToMavenLocal.sh b/gradle/publishAllToMavenLocal.sh new file mode 100755 index 00000000..06435186 --- /dev/null +++ b/gradle/publishAllToMavenLocal.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +./gradlew clean \ +&& ./gradlew :markwon-core:publishToMavenLocal \ +&& ./gradlew :markwon-ext-latex:publishToMavenLocal \ +&& ./gradlew :markwon-ext-strikethrough:publishToMavenLocal \ +&& ./gradlew :markwon-ext-tables:publishToMavenLocal \ +&& ./gradlew :markwon-ext-tasklist:publishToMavenLocal \ +&& ./gradlew :markwon-html:publishToMavenLocal \ +&& ./gradlew :markwon-image:publishToMavenLocal \ +&& ./gradlew :markwon-image-coil:publishToMavenLocal \ +&& ./gradlew :markwon-image-glide:publishToMavenLocal \ +&& ./gradlew :markwon-image-picasso:publishToMavenLocal \ +&& ./gradlew :markwon-linkify:publishToMavenLocal \ +&& ./gradlew :markwon-recycler:publishToMavenLocal \ +&& ./gradlew :markwon-recycler-table:publishToMavenLocal \ +&& ./gradlew :markwon-simple-ext:publishToMavenLocal \ +&& ./gradlew :markwon-syntax-highlight:publishToMavenLocal \ +&& ./gradlew :markwon-editor:publishToMavenLocal \ +&& ./gradlew clean \ No newline at end of file diff --git a/gradle/publishMaven.gradle b/gradle/publishMaven.gradle new file mode 100644 index 00000000..038daa15 --- /dev/null +++ b/gradle/publishMaven.gradle @@ -0,0 +1,240 @@ +/* + * Copyright 2021 Cpacm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Based on: https://github.com/mcxiaoke/gradle-mvn-push/blob/master/gradle-mvn-push.gradle. + * + * To install in a local maven repo: + * 1. In the project you want to test, add mavenLocal() to the repositories list. + * 2. In Project, run: ./gradlew publishToMavenLocal + * + * For faster runs add: -x check when building. + */ + +apply plugin: 'maven-publish' +apply plugin: 'signing' + +version = VERSION_NAME +group = GROUP + + + +@SuppressWarnings("GrMethodMayBeStatic") +def isReleaseBuild() { + return !version.contains("SNAPSHOT") +} + +def getReleaseRepositoryUrl() { + return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL + : 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' +} + +def getSnapshotRepositoryUrl() { + return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL + : 'https://s01.oss.sonatype.org/content/repositories/snapshots/' +} + +def getRepositoryUsername() { + return hasProperty('USERNAME') ? USERNAME : (hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : '') +} + +def getRepositoryPassword() { + return hasProperty('PASSWORD') ? PASSWORD : (hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : '') +} + +def configurePom(pom) { + pom.name = POM_NAME + pom.packaging = POM_PACKAGING + pom.description = POM_DESCRIPTION + pom.url = POM_URL + + pom.scm { + url = POM_SCM_URL + connection = POM_SCM_CONNECTION + developerConnection = POM_SCM_DEV_CONNECTION + } + + pom.licenses { + license { + name = POM_LICENCE_NAME + url = POM_LICENCE_URL + distribution = POM_LICENCE_DIST + } + } + + pom.issueManagement { + system = 'GitHub Issues' + url = POM_SCM_ISSUES + } + + pom.developers { + developer { + id = POM_DEVELOPER_ID + name = POM_DEVELOPER_NAME + email = POM_DEVELOPER_EMAIL + } + } +} + +afterEvaluate { project -> + def isAndroidProject = project.plugins.hasPlugin('com.android.application') || project.plugins.hasPlugin('com.android.library') + publishing { + repositories { + maven { + def releasesRepoUrl = getReleaseRepositoryUrl() + def snapshotsRepoUrl = getSnapshotRepositoryUrl() + url = isReleaseBuild() ? releasesRepoUrl : snapshotsRepoUrl + credentials(PasswordCredentials) { + username = getRepositoryUsername() + password = getRepositoryPassword() + } + } + } + } + + if (isAndroidProject) { + task androidJavadocs(type: Javadoc, dependsOn: assembleDebug) { + source = android.sourceSets.main.java.source + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + // include generated file + classpath += project.files("${buildDir}/generated/source/buildConfig/debug") + classpath += project.files("${buildDir}/generated/ap_generated_sources/debug/out") + excludes = ['**/*.kt'] + } + + task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { + classifier = 'javadoc' + from androidJavadocs.destinationDir + } + + task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.source + } + + android.libraryVariants.all { variant -> + tasks.androidJavadocs.doFirst { + classpath += files(variant.javaCompileProvider.get().classpath.files.join(File.pathSeparator)) + } + } + } else { + + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + + task javadocsJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + artifacts { + archives sourcesJar + archives javadocsJar + } + } + + if (JavaVersion.current().isJava8Compatible()) { + allprojects { + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + } + } + + if (JavaVersion.current().isJava9Compatible()) { + allprojects { + tasks.withType(Javadoc) { + options.addBooleanOption('html5', true) + } + } + } + + artifacts { + if (isAndroidProject) { + archives androidSourcesJar + archives androidJavadocsJar + + archives project.tasks.bundleDebugAar + } + } + + publishing { + publications { + mavenAgent(MavenPublication) { + groupId GROUP + artifactId POM_ARTIFACT_ID + version version + configurePom(pom) + + if (isAndroidProject) { + artifact bundleReleaseAar + artifact androidSourcesJar + + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + project.configurations.all { configuration -> + def name = configuration.name + // api will duplicate with implementation + if (name == 'releaseImplementation' || name == 'implementation') { + configuration.allDependencies.each { + if (it.name != "unspecified" && it.version != "unspecified") { + def groupId = it.group + def artifactId = it.name + if (it instanceof ProjectDependency) { + // skip eg:implementation project(:module) + // def properties = it.getDependencyProject().getProperties() + // groupId = properties.get("GROUP") + // artifactId = properties.get("POM_ARTIFACT_ID") + // if (!artifactId.equals("annotation")) {return} + return + } + println "dependencies:" + groupId + ":" + artifactId + ":" + it.version + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', groupId) + dependencyNode.appendNode('artifactId', artifactId) + dependencyNode.appendNode('version', it.version) + dependencyNode.appendNode('scope', 'compile') + } + } + } + } + } + } else { + from components.java + artifact sourcesJar + artifact javadocsJar + } + } + + if (project.plugins.hasPlugin('java-gradle-plugin')) { + pluginMaven(MavenPublication) { + groupId GROUP + artifactId POM_ARTIFACT_ID + version version + configurePom(pom) + } + } + } + } +} + +signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("publish") } + publishing.publications.all { publication -> + sign publication + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e1cc9db..8fad3f5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/markwon-core/build.gradle b/markwon-core/build.gradle index e9c088d5..1025d16d 100644 --- a/markwon-core/build.gradle +++ b/markwon-core/build.gradle @@ -17,7 +17,7 @@ dependencies { deps.with { api it['x-annotations'] - api it['commonmark'] + api it['flexmark'] // @since 4.1.0 to allow PrecomputedTextSetterCompat // note that this dependency must be added on a client side explicitly @@ -35,4 +35,6 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) + +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-core/src/main/java/io/noties/markwon/AbstractMarkwonPlugin.java b/markwon-core/src/main/java/io/noties/markwon/AbstractMarkwonPlugin.java index 65db542a..b37ff796 100644 --- a/markwon-core/src/main/java/io/noties/markwon/AbstractMarkwonPlugin.java +++ b/markwon-core/src/main/java/io/noties/markwon/AbstractMarkwonPlugin.java @@ -5,8 +5,8 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import org.commonmark.node.Node; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.core.MarkwonTheme; diff --git a/markwon-core/src/main/java/io/noties/markwon/BlockHandlerDef.java b/markwon-core/src/main/java/io/noties/markwon/BlockHandlerDef.java index 58567f43..7b5e53eb 100644 --- a/markwon-core/src/main/java/io/noties/markwon/BlockHandlerDef.java +++ b/markwon-core/src/main/java/io/noties/markwon/BlockHandlerDef.java @@ -2,7 +2,7 @@ package io.noties.markwon; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; /** * @since 4.3.0 diff --git a/markwon-core/src/main/java/io/noties/markwon/Markwon.java b/markwon-core/src/main/java/io/noties/markwon/Markwon.java index 326a70c2..41ef3577 100644 --- a/markwon-core/src/main/java/io/noties/markwon/Markwon.java +++ b/markwon-core/src/main/java/io/noties/markwon/Markwon.java @@ -7,7 +7,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import java.util.List; diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java index ae0f2f9d..f6422610 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java @@ -5,7 +5,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.parser.Parser; import java.util.ArrayList; import java.util.Collections; diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonImpl.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonImpl.java index 1e0d7930..59bef452 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonImpl.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonImpl.java @@ -8,8 +8,14 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; -import org.commonmark.parser.Parser; + +import com.vladsch.flexmark.ast.Code; +import com.vladsch.flexmark.ast.Text; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.NodeVisitor; +import com.vladsch.flexmark.util.ast.VisitHandler; import java.util.Collections; import java.util.List; @@ -74,7 +80,7 @@ class MarkwonImpl extends Markwon { // @since 4.1.1 obtain visitor via factory final MarkwonVisitor visitor = visitorFactory.create(); - node.accept(visitor); + visitor.visit(node); for (MarkwonPlugin plugin : plugins) { plugin.afterRender(node, visitor); diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonPlugin.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonPlugin.java index 33527a9b..b3bf169d 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonPlugin.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonPlugin.java @@ -5,8 +5,8 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import org.commonmark.node.Node; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.core.CorePlugin; import io.noties.markwon.core.MarkwonTheme; diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonReducer.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonReducer.java index a987576a..675a718a 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonReducer.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonReducer.java @@ -2,8 +2,8 @@ package io.noties.markwon; import androidx.annotation.NonNull; -import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.Node; +import com.vladsch.flexmark.ast.Reference; +import com.vladsch.flexmark.util.ast.Node; import java.util.ArrayList; import java.util.Collections; @@ -51,7 +51,7 @@ public abstract class MarkwonReducer { while (node != null) { // @since 4.5.0 do not include LinkReferenceDefinition node (would result // in empty textView if rendered in recycler-view) - if (!(node instanceof LinkReferenceDefinition)) { + if (!(node instanceof Reference)) { list.add(node); } temp = node.getNext(); diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactory.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactory.java index 43d2b1ec..d0c7c3f4 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactory.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactory.java @@ -3,7 +3,7 @@ package io.noties.markwon; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; /** * Class that controls what spans are used for certain Nodes. diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactoryImpl.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactoryImpl.java index fd7a99d5..ee991fe2 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactoryImpl.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonSpansFactoryImpl.java @@ -3,7 +3,7 @@ package io.noties.markwon; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import java.util.ArrayList; import java.util.Collections; diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitor.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitor.java index 2f69acf6..48fd0ad8 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitor.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitor.java @@ -3,8 +3,12 @@ package io.noties.markwon; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; -import org.commonmark.node.Visitor; +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.util.BlockVisitor; +import com.vladsch.flexmark.ast.util.InlineVisitor; +import com.vladsch.flexmark.util.ast.Node; + +import org.jetbrains.annotations.NotNull; /** * Configurable visitor of parsed markdown. Allows visiting certain (registered) nodes without @@ -14,7 +18,7 @@ import org.commonmark.node.Visitor; * @see MarkwonPlugin#configureVisitor(Builder) * @since 3.0.0 */ -public interface MarkwonVisitor extends Visitor { +public interface MarkwonVisitor extends BlockVisitor, InlineVisitor { /** * @see Builder#on(Class, NodeVisitor) @@ -69,13 +73,6 @@ public interface MarkwonVisitor extends Visitor { @NonNull SpannableBuilder builder(); - /** - * Visits all children of supplied node. - * - * @param node to visit - */ - void visitChildren(@NonNull Node node); - /** * Executes a check if there is further content available. * @@ -166,4 +163,8 @@ public interface MarkwonVisitor extends Visitor { * @since 4.3.0 */ void blockEnd(@NonNull Node node); + + void visit(@NotNull Node node); + + void visitChildren(Node node); } diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitorImpl.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitorImpl.java index ce116111..459189af 100644 --- a/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitorImpl.java +++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonVisitorImpl.java @@ -3,30 +3,39 @@ package io.noties.markwon; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.BlockQuote; -import org.commonmark.node.BulletList; -import org.commonmark.node.Code; -import org.commonmark.node.CustomBlock; -import org.commonmark.node.CustomNode; -import org.commonmark.node.Document; -import org.commonmark.node.Emphasis; -import org.commonmark.node.FencedCodeBlock; -import org.commonmark.node.HardLineBreak; -import org.commonmark.node.Heading; -import org.commonmark.node.HtmlBlock; -import org.commonmark.node.HtmlInline; -import org.commonmark.node.Image; -import org.commonmark.node.IndentedCodeBlock; -import org.commonmark.node.Link; -import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.OrderedList; -import org.commonmark.node.Paragraph; -import org.commonmark.node.SoftLineBreak; -import org.commonmark.node.StrongEmphasis; -import org.commonmark.node.Text; -import org.commonmark.node.ThematicBreak; +import com.vladsch.flexmark.ast.AutoLink; +import com.vladsch.flexmark.ast.BlockQuote; +import com.vladsch.flexmark.ast.BulletList; +import com.vladsch.flexmark.ast.BulletListItem; +import com.vladsch.flexmark.ast.Code; +import com.vladsch.flexmark.ast.Emphasis; +import com.vladsch.flexmark.ast.FencedCodeBlock; +import com.vladsch.flexmark.ast.HardLineBreak; +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.HtmlBlock; +import com.vladsch.flexmark.ast.HtmlCommentBlock; +import com.vladsch.flexmark.ast.HtmlEntity; +import com.vladsch.flexmark.ast.HtmlInline; +import com.vladsch.flexmark.ast.HtmlInlineComment; +import com.vladsch.flexmark.ast.Image; +import com.vladsch.flexmark.ast.ImageRef; +import com.vladsch.flexmark.ast.IndentedCodeBlock; +import com.vladsch.flexmark.ast.Link; +import com.vladsch.flexmark.ast.LinkRef; +import com.vladsch.flexmark.ast.ListItem; +import com.vladsch.flexmark.ast.MailLink; +import com.vladsch.flexmark.ast.OrderedList; +import com.vladsch.flexmark.ast.OrderedListItem; +import com.vladsch.flexmark.ast.Paragraph; +import com.vladsch.flexmark.ast.Reference; +import com.vladsch.flexmark.ast.SoftLineBreak; +import com.vladsch.flexmark.ast.StrongEmphasis; +import com.vladsch.flexmark.ast.Text; +import com.vladsch.flexmark.ast.ThematicBreak; +import com.vladsch.flexmark.ast.util.BlockVisitorExt; +import com.vladsch.flexmark.ast.util.InlineVisitorExt; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; import java.util.Collections; import java.util.HashMap; @@ -35,7 +44,7 @@ import java.util.Map; /** * @since 3.0.0 */ -class MarkwonVisitorImpl implements MarkwonVisitor { +class MarkwonVisitorImpl extends com.vladsch.flexmark.util.ast.NodeVisitor implements MarkwonVisitor { private final MarkwonConfiguration configuration; @@ -59,124 +68,169 @@ class MarkwonVisitorImpl implements MarkwonVisitor { this.builder = builder; this.nodes = nodes; this.blockHandler = blockHandler; + + addHandlers(BlockVisitorExt.VISIT_HANDLERS(this)); + addHandlers(InlineVisitorExt.VISIT_HANDLERS(this)); } @Override public void visit(BlockQuote blockQuote) { - visit((Node) blockQuote); + visitImpl(blockQuote); } @Override public void visit(BulletList bulletList) { - visit((Node) bulletList); + visitImpl(bulletList); + } + + @Override + public void visit(AutoLink autoLink) { + visitImpl(autoLink); } @Override public void visit(Code code) { - visit((Node) code); + visitImpl(code); } @Override public void visit(Document document) { - visit((Node) document); + visitImpl(document); } @Override public void visit(Emphasis emphasis) { - visit((Node) emphasis); + visitImpl(emphasis); } @Override public void visit(FencedCodeBlock fencedCodeBlock) { - visit((Node) fencedCodeBlock); + visitImpl(fencedCodeBlock); + } + + public void visit(HardLineBreak hardLineBreak) { + visitImpl(hardLineBreak); } @Override - public void visit(HardLineBreak hardLineBreak) { - visit((Node) hardLineBreak); + public void visit(HtmlEntity node) { + visitImpl(node); } @Override public void visit(Heading heading) { - visit((Node) heading); + visitImpl(heading); } @Override public void visit(ThematicBreak thematicBreak) { - visit((Node) thematicBreak); + visitImpl(thematicBreak); } @Override public void visit(HtmlInline htmlInline) { - visit((Node) htmlInline); + visitImpl(htmlInline); + } + + @Override + public void visit(HtmlInlineComment node) { + visitImpl(node); } @Override public void visit(HtmlBlock htmlBlock) { - visit((Node) htmlBlock); + visitImpl(htmlBlock); } + @Override + public void visit(HtmlCommentBlock node) { + visitImpl(node); + } + + /** + * ![Alt text](/path/to/img.jpg "Optional title") + */ @Override public void visit(Image image) { - visit((Node) image); + visitImpl(image); + } + + /** + * This is a ![foo][bar] image. + * [bar]: /url/of/bar.jpg "optional title attribute" + */ + @Override + public void visit(ImageRef imageRef) { + visitImpl(imageRef); } @Override public void visit(IndentedCodeBlock indentedCodeBlock) { - visit((Node) indentedCodeBlock); + visitImpl(indentedCodeBlock); + } + + +// @Override +// public void visit(ListItem listItem) { +// visitImpl(listItem); +// } + + @Override + public void visit(BulletListItem listItem) { + visitImpl(listItem); + } + + @Override + public void visit(OrderedListItem listItem) { + visitImpl(listItem); } @Override public void visit(Link link) { - visit((Node) link); + visitImpl(link); } @Override - public void visit(ListItem listItem) { - visit((Node) listItem); + public void visit(LinkRef node) { + visitImpl(node); + } + + @Override + public void visit(MailLink node) { + visitImpl(node); } @Override public void visit(OrderedList orderedList) { - visit((Node) orderedList); + visitImpl(orderedList); } @Override public void visit(Paragraph paragraph) { - visit((Node) paragraph); + visitImpl(paragraph); + } + + @Override + public void visit(Reference node) { + // Reference 需要和 ImageRef,LinkRef配合使用,因为这些都是指向 Reference,再由Reference指向真正的地址 } @Override public void visit(SoftLineBreak softLineBreak) { - visit((Node) softLineBreak); + visitImpl(softLineBreak); } @Override public void visit(StrongEmphasis strongEmphasis) { - visit((Node) strongEmphasis); + visitImpl(strongEmphasis); } @Override public void visit(Text text) { - visit((Node) text); + visitImpl(text); } - @Override - public void visit(LinkReferenceDefinition linkReferenceDefinition) { - visit((Node) linkReferenceDefinition); - } - - @Override - public void visit(CustomBlock customBlock) { - visit((Node) customBlock); - } - - @Override - public void visit(CustomNode customNode) { - visit((Node) customNode); - } - - private void visit(@NonNull Node node) { + private void visitImpl(@NonNull Node node) { //noinspection unchecked final NodeVisitor nodeVisitor = (NodeVisitor) nodes.get(node.getClass()); if (nodeVisitor != null) { @@ -204,17 +258,6 @@ class MarkwonVisitorImpl implements MarkwonVisitor { return builder; } - @Override - public void visitChildren(@NonNull Node parent) { - Node node = parent.getFirstChild(); - while (node != null) { - // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no - // node after visiting it. So get the next node before visiting. - Node next = node.getNext(); - node.accept(this); - node = next; - } - } @Override public boolean hasNext(@NonNull Node node) { diff --git a/markwon-core/src/main/java/io/noties/markwon/SoftBreakAddsNewLinePlugin.java b/markwon-core/src/main/java/io/noties/markwon/SoftBreakAddsNewLinePlugin.java index 9c7572fb..dee5451a 100644 --- a/markwon-core/src/main/java/io/noties/markwon/SoftBreakAddsNewLinePlugin.java +++ b/markwon-core/src/main/java/io/noties/markwon/SoftBreakAddsNewLinePlugin.java @@ -2,7 +2,7 @@ package io.noties.markwon; import androidx.annotation.NonNull; -import org.commonmark.node.SoftLineBreak; +import com.vladsch.flexmark.ast.SoftLineBreak; /** * @since 4.3.0 diff --git a/markwon-core/src/main/java/io/noties/markwon/core/CorePlugin.java b/markwon-core/src/main/java/io/noties/markwon/core/CorePlugin.java index 4961505a..d7bfc969 100644 --- a/markwon-core/src/main/java/io/noties/markwon/core/CorePlugin.java +++ b/markwon-core/src/main/java/io/noties/markwon/core/CorePlugin.java @@ -9,27 +9,27 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.commonmark.node.Block; -import org.commonmark.node.BlockQuote; -import org.commonmark.node.BulletList; -import org.commonmark.node.Code; -import org.commonmark.node.Emphasis; -import org.commonmark.node.FencedCodeBlock; -import org.commonmark.node.HardLineBreak; -import org.commonmark.node.Heading; -import org.commonmark.node.HtmlBlock; -import org.commonmark.node.Image; -import org.commonmark.node.IndentedCodeBlock; -import org.commonmark.node.Link; -import org.commonmark.node.ListBlock; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.OrderedList; -import org.commonmark.node.Paragraph; -import org.commonmark.node.SoftLineBreak; -import org.commonmark.node.StrongEmphasis; -import org.commonmark.node.Text; -import org.commonmark.node.ThematicBreak; +import com.vladsch.flexmark.ast.BlockQuote; +import com.vladsch.flexmark.ast.BulletList; +import com.vladsch.flexmark.ast.Code; +import com.vladsch.flexmark.ast.Emphasis; +import com.vladsch.flexmark.ast.FencedCodeBlock; +import com.vladsch.flexmark.ast.HardLineBreak; +import com.vladsch.flexmark.ast.Heading; +import com.vladsch.flexmark.ast.HtmlBlock; +import com.vladsch.flexmark.ast.Image; +import com.vladsch.flexmark.ast.IndentedCodeBlock; +import com.vladsch.flexmark.ast.Link; +import com.vladsch.flexmark.ast.ListBlock; +import com.vladsch.flexmark.ast.ListItem; +import com.vladsch.flexmark.ast.OrderedList; +import com.vladsch.flexmark.ast.Paragraph; +import com.vladsch.flexmark.ast.SoftLineBreak; +import com.vladsch.flexmark.ast.StrongEmphasis; +import com.vladsch.flexmark.ast.Text; +import com.vladsch.flexmark.ast.ThematicBreak; +import com.vladsch.flexmark.util.ast.Block; +import com.vladsch.flexmark.util.ast.Node; import java.util.ArrayList; import java.util.Arrays; @@ -212,8 +212,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) { - final String literal = text.getLiteral(); - + String literal = text.toAstString(true); visitor.builder().append(literal); // @since 4.0.0 @@ -278,7 +277,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { // unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted visitor.builder() .append('\u00a0') - .append(code.getLiteral()) + .append(code.toAstString(true)) .append('\u00a0'); visitor.setSpansForNodeOptional(code, length); @@ -290,7 +289,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { builder.on(FencedCodeBlock.class, new MarkwonVisitor.NodeVisitor() { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull FencedCodeBlock fencedCodeBlock) { - visitCodeBlock(visitor, fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral(), fencedCodeBlock); + visitCodeBlock(visitor, fencedCodeBlock.getInfo().unescape(), fencedCodeBlock.toAstString(true), fencedCodeBlock); } }); } @@ -299,7 +298,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { builder.on(IndentedCodeBlock.class, new MarkwonVisitor.NodeVisitor() { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull IndentedCodeBlock indentedCodeBlock) { - visitCodeBlock(visitor, null, indentedCodeBlock.getLiteral(), indentedCodeBlock); + visitCodeBlock(visitor, null, indentedCodeBlock.toAstString(true), indentedCodeBlock); } }); } @@ -335,7 +334,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { final String destination = configuration .imageDestinationProcessor() - .process(image.getDestination()); + .process(image.getUrl().unescape()); final RenderProps props = visitor.renderProps(); @@ -538,7 +537,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { final int length = visitor.length(); visitor.visitChildren(link); - final String destination = link.getDestination(); + final String destination = link.getUrl().unescape(); CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination); diff --git a/markwon-core/src/main/java/io/noties/markwon/core/SimpleBlockNodeVisitor.java b/markwon-core/src/main/java/io/noties/markwon/core/SimpleBlockNodeVisitor.java index 6a27599e..be944b9e 100644 --- a/markwon-core/src/main/java/io/noties/markwon/core/SimpleBlockNodeVisitor.java +++ b/markwon-core/src/main/java/io/noties/markwon/core/SimpleBlockNodeVisitor.java @@ -2,7 +2,7 @@ package io.noties.markwon.core; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.MarkwonVisitor; diff --git a/markwon-core/src/main/java/io/noties/markwon/syntax/SyntaxHighlight.java b/markwon-core/src/main/java/io/noties/markwon/syntax/SyntaxHighlight.java index 5a7dd839..69603b2a 100644 --- a/markwon-core/src/main/java/io/noties/markwon/syntax/SyntaxHighlight.java +++ b/markwon-core/src/main/java/io/noties/markwon/syntax/SyntaxHighlight.java @@ -3,6 +3,7 @@ package io.noties.markwon.syntax; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + @SuppressWarnings("WeakerAccess") public interface SyntaxHighlight { diff --git a/markwon-core/src/main/java/io/noties/markwon/utils/DumpNodes.java b/markwon-core/src/main/java/io/noties/markwon/utils/DumpNodes.java index e06194b8..a950ff40 100644 --- a/markwon-core/src/main/java/io/noties/markwon/utils/DumpNodes.java +++ b/markwon-core/src/main/java/io/noties/markwon/utils/DumpNodes.java @@ -4,8 +4,8 @@ import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Node; -import org.commonmark.node.Visitor; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.Visitor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -70,7 +70,8 @@ public abstract class DumpNodes { return null; } }); - node.accept(visitor); + visitor.visit(node); + //node.accept(visitor); return builder.toString(); } @@ -104,7 +105,7 @@ public abstract class DumpNodes { // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no // node after visiting it. So get the next node before visiting. Node next = node.getNext(); - node.accept(visitor); + visitor.visit(node); node = next; } } diff --git a/markwon-core/src/main/java/io/noties/markwon/utils/ParserUtils.java b/markwon-core/src/main/java/io/noties/markwon/utils/ParserUtils.java index d697d26f..f400e70e 100644 --- a/markwon-core/src/main/java/io/noties/markwon/utils/ParserUtils.java +++ b/markwon-core/src/main/java/io/noties/markwon/utils/ParserUtils.java @@ -2,7 +2,7 @@ package io.noties.markwon.utils; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; /** * @since 4.6.0 diff --git a/markwon-editor/build.gradle b/markwon-editor/build.gradle index 884f6fb6..bd288b5f 100644 --- a/markwon-editor/build.gradle +++ b/markwon-editor/build.gradle @@ -28,4 +28,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-ext-latex/build.gradle b/markwon-ext-latex/build.gradle index b0d3fc92..6ea48452 100644 --- a/markwon-ext-latex/build.gradle +++ b/markwon-ext-latex/build.gradle @@ -16,7 +16,6 @@ android { dependencies { api project(':markwon-core') - api project(':markwon-inline-parser') api deps['jlatexmath-android'] @@ -27,4 +26,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlock.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlock.java deleted file mode 100644 index 4066caba..00000000 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlock.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.noties.markwon.ext.latex; - -import org.commonmark.node.CustomBlock; - -public class JLatexMathBlock extends CustomBlock { - - private String latex; - - public String latex() { - return latex; - } - - public void latex(String latex) { - this.latex = latex; - } -} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java deleted file mode 100644 index cf212e32..00000000 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParser.java +++ /dev/null @@ -1,119 +0,0 @@ -package io.noties.markwon.ext.latex; - -import androidx.annotation.NonNull; - -import org.commonmark.internal.util.Parsing; -import org.commonmark.node.Block; -import org.commonmark.parser.block.AbstractBlockParser; -import org.commonmark.parser.block.AbstractBlockParserFactory; -import org.commonmark.parser.block.BlockContinue; -import org.commonmark.parser.block.BlockStart; -import org.commonmark.parser.block.MatchedBlockParser; -import org.commonmark.parser.block.ParserState; - -/** - * @since 4.3.0 (although there was a class with the same name, - * which is renamed now to {@link JLatexMathBlockParserLegacy}) - */ -class JLatexMathBlockParser extends AbstractBlockParser { - - private static final char DOLLAR = '$'; - private static final char SPACE = ' '; - - private final JLatexMathBlock block = new JLatexMathBlock(); - - private final StringBuilder builder = new StringBuilder(); - - private final int signs; - - JLatexMathBlockParser(int signs) { - this.signs = signs; - } - - @Override - public Block getBlock() { - return block; - } - - @Override - public BlockContinue tryContinue(ParserState parserState) { - final int nextNonSpaceIndex = parserState.getNextNonSpaceIndex(); - final CharSequence line = parserState.getLine(); - final int length = line.length(); - - // check for closing - if (parserState.getIndent() < Parsing.CODE_BLOCK_INDENT) { - if (consume(DOLLAR, line, nextNonSpaceIndex, length) == signs) { - // okay, we have our number of signs - // let's consume spaces until the end - if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) == length) { - return BlockContinue.finished(); - } - } - } - - return BlockContinue.atIndex(parserState.getIndex()); - } - - @Override - public void addLine(CharSequence line) { - builder.append(line); - builder.append('\n'); - } - - @Override - public void closeBlock() { - block.latex(builder.toString()); - } - - public static class Factory extends AbstractBlockParserFactory { - - @Override - public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { - - // let's define the spec: - // * 0-3 spaces before are allowed (Parsing.CODE_BLOCK_INDENT = 4) - // * 2+ subsequent `$` signs - // * any optional amount of spaces - // * new line - // * block is closed when the same amount of opening signs is met - - final int indent = state.getIndent(); - - // check if it's an indented code block - if (indent >= Parsing.CODE_BLOCK_INDENT) { - return BlockStart.none(); - } - - final int nextNonSpaceIndex = state.getNextNonSpaceIndex(); - final CharSequence line = state.getLine(); - final int length = line.length(); - - final int signs = consume(DOLLAR, line, nextNonSpaceIndex, length); - - // 2 is minimum - if (signs < 2) { - return BlockStart.none(); - } - - // consume spaces until the end of the line, if any other content is found -> NONE - if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) != length) { - return BlockStart.none(); - } - - return BlockStart.of(new JLatexMathBlockParser(signs)) - .atIndex(length + 1); - } - } - - @SuppressWarnings("SameParameterValue") - private static int consume(char c, @NonNull CharSequence line, int start, int end) { - for (int i = start; i < end; i++) { - if (c != line.charAt(i)) { - return i - start; - } - } - // all consumed - return end - start; - } -} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java deleted file mode 100644 index 9b4565bc..00000000 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathBlockParserLegacy.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.noties.markwon.ext.latex; - -import org.commonmark.node.Block; -import org.commonmark.parser.block.AbstractBlockParser; -import org.commonmark.parser.block.AbstractBlockParserFactory; -import org.commonmark.parser.block.BlockContinue; -import org.commonmark.parser.block.BlockStart; -import org.commonmark.parser.block.MatchedBlockParser; -import org.commonmark.parser.block.ParserState; - -/** - * @since 4.3.0 (although it is just renamed parser from previous versions) - */ -class JLatexMathBlockParserLegacy extends AbstractBlockParser { - - private final JLatexMathBlock block = new JLatexMathBlock(); - - private final StringBuilder builder = new StringBuilder(); - - private boolean isClosed; - - @Override - public Block getBlock() { - return block; - } - - @Override - public BlockContinue tryContinue(ParserState parserState) { - - if (isClosed) { - return BlockContinue.finished(); - } - - return BlockContinue.atIndex(parserState.getIndex()); - } - - @Override - public void addLine(CharSequence line) { - - if (builder.length() > 0) { - builder.append('\n'); - } - - builder.append(line); - - final int length = builder.length(); - if (length > 1) { - isClosed = '$' == builder.charAt(length - 1) - && '$' == builder.charAt(length - 2); - if (isClosed) { - builder.replace(length - 2, length, ""); - } - } - } - - @Override - public void closeBlock() { - block.latex(builder.toString()); - } - - public static class Factory extends AbstractBlockParserFactory { - - @Override - public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { - - final CharSequence line = state.getLine(); - final int length = line != null - ? line.length() - : 0; - - if (length > 1) { - if ('$' == line.charAt(0) - && '$' == line.charAt(1)) { - return BlockStart.of(new JLatexMathBlockParserLegacy()) - .atIndex(state.getIndex() + 2); - } - } - - return BlockStart.none(); - } - } -} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathInlineProcessor.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathInlineProcessor.java deleted file mode 100644 index d368778c..00000000 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathInlineProcessor.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.noties.markwon.ext.latex; - -import androidx.annotation.Nullable; - -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -import io.noties.markwon.inlineparser.InlineProcessor; - -/** - * @since 4.3.0 - */ -class JLatexMathInlineProcessor extends InlineProcessor { - - private static final Pattern RE = Pattern.compile("(\\${2})([\\s\\S]+?)\\1"); - - @Override - public char specialCharacter() { - return '$'; - } - - @Nullable - @Override - protected Node parse() { - - final String latex = match(RE); - if (latex == null) { - return null; - } - - final JLatexMathNode node = new JLatexMathNode(); - node.latex(latex.substring(2, latex.length() - 2)); - return node; - } -} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathNode.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathNode.java deleted file mode 100644 index 46948cc5..00000000 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathNode.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.noties.markwon.ext.latex; - -import org.commonmark.node.CustomNode; - -/** - * @since 4.3.0 - */ -public class JLatexMathNode extends CustomNode { - - private String latex; - - public String latex() { - return latex; - } - - public void latex(String latex) { - this.latex = latex; - } -} diff --git a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java index dd8a606e..b9f7e975 100644 --- a/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java +++ b/markwon-ext-latex/src/main/java/io/noties/markwon/ext/latex/JLatexMathPlugin.java @@ -14,8 +14,11 @@ import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.annotation.VisibleForTesting; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.ext.gitlab.GitLabExtension; +import com.vladsch.flexmark.ext.gitlab.GitLabInlineMath; +import com.vladsch.flexmark.parser.Parser; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -31,7 +34,6 @@ import io.noties.markwon.image.AsyncDrawableScheduler; import io.noties.markwon.image.AsyncDrawableSpan; import io.noties.markwon.image.DrawableUtils; import io.noties.markwon.image.ImageSizeResolver; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import ru.noties.jlatexmath.JLatexMathDrawable; /** @@ -117,8 +119,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { // @since 4.3.0 final boolean blocksEnabled; - final boolean blocksLegacy; - final boolean inlinesEnabled; // @since 4.3.0 final ErrorHandler errorHandler; @@ -128,8 +128,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { Config(@NonNull Builder builder) { this.theme = builder.theme.build(); this.blocksEnabled = builder.blocksEnabled; - this.blocksLegacy = builder.blocksLegacy; - this.inlinesEnabled = builder.inlinesEnabled; this.errorHandler = builder.errorHandler; // @since 4.0.0 ExecutorService executorService = builder.executorService; @@ -155,59 +153,34 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { this.inlineImageSizeResolver = new InlineImageSizeResolver(); } - @Override - public void configure(@NonNull Registry registry) { - if (config.inlinesEnabled) { - registry.require(MarkwonInlineParserPlugin.class) - .factoryBuilder() - .addInlineProcessor(new JLatexMathInlineProcessor()); - } - } - @Override public void configureParser(@NonNull Parser.Builder builder) { - // @since 4.3.0 + builder.extensions(Collections.singleton(GitLabExtension.create())); if (config.blocksEnabled) { - if (config.blocksLegacy) { - builder.customBlockParserFactory(new JLatexMathBlockParserLegacy.Factory()); - } else { - builder.customBlockParserFactory(new JLatexMathBlockParser.Factory()); - } + builder.set(GitLabExtension.RENDER_BLOCK_MATH, true); } } @Override public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { - addBlockVisitor(builder); - addInlineVisitor(builder); - } - - private void addBlockVisitor(@NonNull MarkwonVisitor.Builder builder) { - if (!config.blocksEnabled) { - return; - } - - builder.on(JLatexMathBlock.class, new MarkwonVisitor.NodeVisitor() { + builder.on(GitLabInlineMath.class, new MarkwonVisitor.NodeVisitor() { @Override - public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathBlock jLatexMathBlock) { - - visitor.blockStart(jLatexMathBlock); - - final String latex = jLatexMathBlock.latex(); - + public void visit(@NonNull MarkwonVisitor visitor, @NonNull GitLabInlineMath gitLabInlineMath) { + if (config.blocksEnabled) visitor.blockStart(gitLabInlineMath); + final String tex = gitLabInlineMath.getText().unescape(); final int length = visitor.length(); // @since 4.0.2 we cannot append _raw_ latex as a placeholder-text, // because Android will draw formula for each line of text, thus // leading to formula duplicated (drawn on each line of text) - visitor.builder().append(prepareLatexTextPlaceholder(latex)); + visitor.builder().append(prepareLatexTextPlaceholder(tex)); final MarkwonConfiguration configuration = visitor.configuration(); final AsyncDrawableSpan span = new JLatexAsyncDrawableSpan( configuration.theme(), new JLatextAsyncDrawable( - latex, + tex, jLatextAsyncDrawableLoader, jLatexBlockImageSizeResolver, null, @@ -217,43 +190,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { visitor.setSpans(length, span); - visitor.blockEnd(jLatexMathBlock); - } - }); - } - - private void addInlineVisitor(@NonNull MarkwonVisitor.Builder builder) { - - if (!config.inlinesEnabled) { - return; - } - - builder.on(JLatexMathNode.class, new MarkwonVisitor.NodeVisitor() { - @Override - public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathNode jLatexMathNode) { - final String latex = jLatexMathNode.latex(); - - final int length = visitor.length(); - - // @since 4.0.2 we cannot append _raw_ latex as a placeholder-text, - // because Android will draw formula for each line of text, thus - // leading to formula duplicated (drawn on each line of text) - visitor.builder().append(prepareLatexTextPlaceholder(latex)); - - final MarkwonConfiguration configuration = visitor.configuration(); - - final AsyncDrawableSpan span = new JLatexInlineAsyncDrawableSpan( - configuration.theme(), - new JLatextAsyncDrawable( - latex, - jLatextAsyncDrawableLoader, - inlineImageSizeResolver, - null, - false), - config.theme.inlineTextColor() - ); - - visitor.setSpans(length, span); + if (config.blocksEnabled) visitor.blockEnd(gitLabInlineMath); } }); } @@ -310,16 +247,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin { return this; } - /** - * @param blocksLegacy indicates if blocks should be handled in legacy mode ({@code pre 4.3.0}) - * @since 4.3.0 - */ - @NonNull - public Builder blocksLegacy(boolean blocksLegacy) { - this.blocksLegacy = blocksLegacy; - return this; - } - /** * @param inlinesEnabled indicates if inline parsing should be enabled. * NB, this requires `MarkwonInlineParserPlugin` to be used when creating `MarkwonInstance` diff --git a/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathBlockParserTest.java b/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathBlockParserTest.java deleted file mode 100644 index b1587e1d..00000000 --- a/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathBlockParserTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package io.noties.markwon.ext.latex; - -import androidx.annotation.NonNull; - -import org.commonmark.internal.BlockContinueImpl; -import org.commonmark.internal.BlockStartImpl; -import org.commonmark.internal.util.Parsing; -import org.commonmark.parser.block.BlockStart; -import org.commonmark.parser.block.ParserState; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class JLatexMathBlockParserTest { - - private static final String[] NO_MATCH = { - " ", - " ", - " ", - "$ ", - " $ $", - "-$$", - " -$$", - "$$-", - " $$ -", - " $$ -", - "$$$ -" - }; - - private static final String[] MATCH = { - "$$", - " $$", - " $$", - " $$", - "$$ ", - " $$ ", - " $$ ", - " $$ ", - "$$$", - " $$$", - " $$$", - "$$$$", - " $$$$", - "$$$$$$$$$$$$$$$$$$$$$", - " $$$$$$$$$$$$$$$$$$$$$", - " $$$$$$$$$$$$$$$$$$$$$", - " $$$$$$$$$$$$$$$$$$$$$" - }; - - private JLatexMathBlockParser.Factory factory; - - @Before - public void before() { - factory = new JLatexMathBlockParser.Factory(); - } - - @Test - public void factory_indentBlock() { - // when state indent is greater than block -> nono - - final ParserState state = mock(ParserState.class); - when(state.getIndent()).thenReturn(Parsing.CODE_BLOCK_INDENT); - - // hm, interesting, `BlockStart.none()` actually returns null - final BlockStart start = factory.tryStart(state, null); - assertNull(start); - } - - @Test - public void factory_noMatch() { - - for (String line : NO_MATCH) { - final ParserState state = createState(line); - - assertNull(factory.tryStart(state, null)); - } - } - - @Test - public void factory_match() { - - for (String line : MATCH) { - final ParserState state = createState(line); - - final BlockStart start = factory.tryStart(state, null); - assertNotNull(start); - - // hm... - final BlockStartImpl impl = (BlockStartImpl) start; - assertEquals(quote(line), line.length() + 1, impl.getNewIndex()); - } - } - - @Test - public void finish() { - - for (String line : MATCH) { - final ParserState state = createState(line); - - // we will have 2 checks here: - // * must pass for correct length - // * must fail for incorrect - - final int count = countDollarSigns(line); - - // pass - { - final JLatexMathBlockParser parser = new JLatexMathBlockParser(count); - final BlockContinueImpl impl = (BlockContinueImpl) parser.tryContinue(state); - assertTrue(quote(line), impl.isFinalize()); - } - - // fail (in terms of closing, not failing test) - { - final JLatexMathBlockParser parser = new JLatexMathBlockParser(count + 1); - final BlockContinueImpl impl = (BlockContinueImpl) parser.tryContinue(state); - assertFalse(quote(line), impl.isFinalize()); - } - } - } - - @Test - public void finish_noMatch() { - for (String line : NO_MATCH) { - final ParserState state = createState(line); - // doesn't matter - final int count = 2; - final JLatexMathBlockParser parser = new JLatexMathBlockParser(count); - final BlockContinueImpl impl = (BlockContinueImpl) parser.tryContinue(state); - assertFalse(quote(line), impl.isFinalize()); - } - } - - @NonNull - private static ParserState createState(@NonNull String line) { - - final ParserState state = mock(ParserState.class); - - int i = 0; - for (int length = line.length(); i < length; i++) { - if (' ' != line.charAt(i)) { - // previous is the last space - break; - } - } - - when(state.getIndent()).thenReturn(i); - when(state.getNextNonSpaceIndex()).thenReturn(i); - when(state.getLine()).thenReturn(line); - - return state; - } - - private static int countDollarSigns(@NonNull String line) { - int count = 0; - for (int i = 0, length = line.length(); i < length; i++) { - if ('$' == line.charAt(i)) count += 1; - } - return count; - } - - @NonNull - private static String quote(@NonNull String s) { - return '\'' + s + '\''; - } -} \ No newline at end of file diff --git a/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathPluginTest.java b/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathPluginTest.java deleted file mode 100644 index a2467ae3..00000000 --- a/markwon-ext-latex/src/test/java/io/noties/markwon/ext/latex/JLatexMathPluginTest.java +++ /dev/null @@ -1,230 +0,0 @@ -package io.noties.markwon.ext.latex; - -import androidx.annotation.NonNull; - -import org.commonmark.parser.Parser; -import org.commonmark.parser.block.BlockParserFactory; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import java.util.List; -import java.util.concurrent.ExecutorService; - -import io.noties.markwon.MarkwonConfiguration; -import io.noties.markwon.MarkwonPlugin; -import io.noties.markwon.MarkwonVisitor; -import io.noties.markwon.SpannableBuilder; -import io.noties.markwon.inlineparser.InlineProcessor; -import io.noties.markwon.inlineparser.MarkwonInlineParser; -import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class JLatexMathPluginTest { - - @Test - public void latex_text_placeholder() { - // text placeholder cannot have new-line characters and should be trimmed from ends - - final String[] in = { - "hello", - "he\nllo", - " hello\n\n", - "\n\nhello\n\n", - "\n", - " \nhello\n " - }; - - for (String latex : in) { - final String placeholder = JLatexMathPlugin.prepareLatexTextPlaceholder(latex); - assertTrue(placeholder, placeholder.indexOf('\n') < 0); - if (placeholder.length() > 0) { - assertFalse(placeholder, Character.isWhitespace(placeholder.charAt(0))); - assertFalse(placeholder, Character.isWhitespace(placeholder.charAt(placeholder.length() - 1))); - } - } - } - - @Test - public void block_parser_registered() { - final JLatexMathPlugin plugin = JLatexMathPlugin.create(0); - final Parser.Builder builder = mock(Parser.Builder.class); - plugin.configureParser(builder); - verify(builder, times(1)).customBlockParserFactory(any(BlockParserFactory.class)); - } - - @Test - public void visitor_registered() { - final JLatexMathPlugin plugin = JLatexMathPlugin.create(0); - final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); - plugin.configureVisitor(builder); - //noinspection unchecked - verify(builder, times(1)) - .on(eq(JLatexMathBlock.class), any(MarkwonVisitor.NodeVisitor.class)); - } - - @Test - public void visit() { - final JLatexMathPlugin plugin = JLatexMathPlugin.create(0, new JLatexMathPlugin.BuilderConfigure() { - @Override - public void configureBuilder(@NonNull JLatexMathPlugin.Builder builder) { - // no async in test (nooped for this test) - builder.executorService(mock(ExecutorService.class)); - } - }); - final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); - final ArgumentCaptor captor = - ArgumentCaptor.forClass(MarkwonVisitor.NodeVisitor.class); - plugin.configureVisitor(builder); - //noinspection unchecked - verify(builder, times(1)) - .on(eq(JLatexMathBlock.class), captor.capture()); - final MarkwonVisitor.NodeVisitor nodeVisitor = captor.getValue(); - - final MarkwonVisitor visitor = mock(MarkwonVisitor.class); - final JLatexMathBlock block = mock(JLatexMathBlock.class); - when(block.latex()).thenReturn(" first\nsecond\n "); - - final SpannableBuilder spannableBuilder = mock(SpannableBuilder.class); - when(visitor.builder()).thenReturn(spannableBuilder); - when(visitor.configuration()).thenReturn(mock(MarkwonConfiguration.class)); - - //noinspection unchecked - nodeVisitor.visit(visitor, block); - - verify(block, times(1)).latex(); - verify(visitor, times(1)).length(); - - final ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class); - verify(spannableBuilder, times(1)).append(stringArgumentCaptor.capture()); - - final String placeholder = stringArgumentCaptor.getValue(); - assertTrue(placeholder, placeholder.indexOf('\n') < 0); - - verify(visitor, times(1)).setSpans(eq(0), any()); - } - - @Test - public void legacy() { - // if render mode is legacy: - // - no inline plugin is required, - // - parser has legacy block parser factory - // - no inline node is registered (node) - - final JLatexMathPlugin plugin = JLatexMathPlugin.create(1, new JLatexMathPlugin.BuilderConfigure() { - @Override - public void configureBuilder(@NonNull JLatexMathPlugin.Builder builder) { - builder.blocksLegacy(true); - builder.inlinesEnabled(false); - } - }); - - // registry - { - final MarkwonPlugin.Registry registry = mock(MarkwonPlugin.Registry.class); - plugin.configure(registry); - verify(registry, never()).require(any(Class.class)); - } - - // parser - { - final Parser.Builder builder = mock(Parser.Builder.class); - plugin.configureParser(builder); - - final ArgumentCaptor captor = - ArgumentCaptor.forClass(BlockParserFactory.class); - verify(builder, times(1)).customBlockParserFactory(captor.capture()); - final BlockParserFactory factory = captor.getValue(); - assertTrue(factory.getClass().getName(), factory instanceof JLatexMathBlockParserLegacy.Factory); - } - - // visitor - { - final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); - plugin.configureVisitor(builder); - - final ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - verify(builder, times(1)).on(captor.capture(), any(MarkwonVisitor.NodeVisitor.class)); - - assertEquals(JLatexMathBlock.class, captor.getValue()); - } - } - - @Test - public void blocks_inlines_implicit() { - final JLatexMathPlugin plugin = JLatexMathPlugin.create(1); - final JLatexMathPlugin.Config config = plugin.config; - assertTrue("blocksEnabled", config.blocksEnabled); - assertFalse("blocksLegacy", config.blocksLegacy); - assertFalse("inlinesEnabled", config.inlinesEnabled); - } - - @Test - public void blocks_inlines() { - final JLatexMathPlugin plugin = JLatexMathPlugin.create(12, new JLatexMathPlugin.BuilderConfigure() { - @Override - public void configureBuilder(@NonNull JLatexMathPlugin.Builder builder) { - builder.inlinesEnabled(true); - } - }); - - // registry - { - final MarkwonInlineParser.FactoryBuilder factoryBuilder = mock(MarkwonInlineParser.FactoryBuilder.class); - final MarkwonInlineParserPlugin inlineParserPlugin = mock(MarkwonInlineParserPlugin.class); - final MarkwonPlugin.Registry registry = mock(MarkwonPlugin.Registry.class); - when(inlineParserPlugin.factoryBuilder()).thenReturn(factoryBuilder); - when(registry.require(eq(MarkwonInlineParserPlugin.class))).thenReturn(inlineParserPlugin); - plugin.configure(registry); - - verify(registry, times(1)).require(eq(MarkwonInlineParserPlugin.class)); - verify(inlineParserPlugin, times(1)).factoryBuilder(); - - final ArgumentCaptor captor = ArgumentCaptor.forClass(InlineProcessor.class); - verify(factoryBuilder, times(1)).addInlineProcessor(captor.capture()); - - final InlineProcessor inlineProcessor = captor.getValue(); - assertTrue(inlineParserPlugin.getClass().getName(), inlineProcessor instanceof JLatexMathInlineProcessor); - } - - // parser - { - final Parser.Builder builder = mock(Parser.Builder.class); - plugin.configureParser(builder); - - final ArgumentCaptor captor = - ArgumentCaptor.forClass(BlockParserFactory.class); - verify(builder, times(1)).customBlockParserFactory(captor.capture()); - final BlockParserFactory factory = captor.getValue(); - assertTrue(factory.getClass().getName(), factory instanceof JLatexMathBlockParser.Factory); - } - - // visitor - { - final MarkwonVisitor.Builder builder = mock(MarkwonVisitor.Builder.class); - plugin.configureVisitor(builder); - - final ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - verify(builder, times(2)).on(captor.capture(), any(MarkwonVisitor.NodeVisitor.class)); - - final List nodes = captor.getAllValues(); - assertEquals(2, nodes.size()); - assertTrue(nodes.toString(), nodes.contains(JLatexMathNode.class)); - assertTrue(nodes.toString(), nodes.contains(JLatexMathBlock.class)); - } - } -} \ No newline at end of file diff --git a/markwon-ext-strikethrough/build.gradle b/markwon-ext-strikethrough/build.gradle index 6cd72e5e..65f48c54 100644 --- a/markwon-ext-strikethrough/build.gradle +++ b/markwon-ext-strikethrough/build.gradle @@ -18,8 +18,6 @@ dependencies { api project(':markwon-core') deps.with { - api it['commonmark-strikethrough'] - // NB! ix-java dependency to be used in tests testImplementation it['ix-java'] } @@ -32,4 +30,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-ext-strikethrough/src/main/java/io/noties/markwon/ext/strikethrough/StrikethroughPlugin.java b/markwon-ext-strikethrough/src/main/java/io/noties/markwon/ext/strikethrough/StrikethroughPlugin.java index f805f612..7efef2e2 100644 --- a/markwon-ext-strikethrough/src/main/java/io/noties/markwon/ext/strikethrough/StrikethroughPlugin.java +++ b/markwon-ext-strikethrough/src/main/java/io/noties/markwon/ext/strikethrough/StrikethroughPlugin.java @@ -4,9 +4,9 @@ import android.text.style.StrikethroughSpan; import androidx.annotation.NonNull; -import org.commonmark.ext.gfm.strikethrough.Strikethrough; -import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough; +import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension; +import com.vladsch.flexmark.parser.Parser; import java.util.Collections; @@ -18,8 +18,8 @@ import io.noties.markwon.RenderProps; import io.noties.markwon.SpanFactory; /** - * Plugin to add strikethrough markdown feature. This plugin will extend commonmark-java.Parser - * with strikethrough extension, add SpanFactory and register commonmark-java.Strikethrough node + * Plugin to add strikethrough markdown feature. This plugin will extend flexmark.Parser + * with strikethrough extension, add SpanFactory and register flexmark.Strikethrough node * visitor * * @see #create() diff --git a/markwon-ext-tables/build.gradle b/markwon-ext-tables/build.gradle index ee753b1a..1a0f89aa 100644 --- a/markwon-ext-tables/build.gradle +++ b/markwon-ext-tables/build.gradle @@ -11,15 +11,16 @@ android { versionCode 1 versionName version } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } } dependencies { api project(':markwon-core') - - deps.with { - api it['commonmark-table'] - } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/Table.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/Table.java index 1d973ddc..1918f6bd 100644 --- a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/Table.java +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/Table.java @@ -5,12 +5,16 @@ import android.text.Spanned; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.ext.gfm.tables.TableBlock; -import org.commonmark.ext.gfm.tables.TableCell; -import org.commonmark.ext.gfm.tables.TableHead; -import org.commonmark.ext.gfm.tables.TableRow; -import org.commonmark.node.AbstractVisitor; -import org.commonmark.node.CustomNode; +import com.vladsch.flexmark.ast.BlockQuote; +import com.vladsch.flexmark.ast.util.BlockVisitorExt; +import com.vladsch.flexmark.ast.util.InlineVisitorExt; +import com.vladsch.flexmark.ext.tables.TableBlock; +import com.vladsch.flexmark.ext.tables.TableCell; +import com.vladsch.flexmark.ext.tables.TableHead; +import com.vladsch.flexmark.ext.tables.TableRow; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.NodeVisitor; +import com.vladsch.flexmark.util.ast.VisitHandler; import java.util.ArrayList; import java.util.List; @@ -40,7 +44,8 @@ public class Table { final Table table; final ParseVisitor visitor = new ParseVisitor(markwon); - tableBlock.accept(visitor); + visitor.visit(tableBlock); + final List rows = visitor.rows(); if (rows == null) { @@ -135,7 +140,7 @@ public class Table { '}'; } - static class ParseVisitor extends AbstractVisitor { + static class ParseVisitor extends NodeVisitor implements TableVisitor{ private final Markwon markwon; @@ -146,6 +151,7 @@ public class Table { ParseVisitor(@NonNull Markwon markwon) { this.markwon = markwon; + addHandlers(TableVisitor.VISIT_HANDLERS(this)); } @Nullable @@ -154,44 +160,47 @@ public class Table { } @Override - public void visit(CustomNode customNode) { - - if (customNode instanceof TableCell) { - - final TableCell cell = (TableCell) customNode; - - if (pendingRow == null) { - pendingRow = new ArrayList<>(2); - } - - pendingRow.add(new Table.Column(alignment(cell.getAlignment()), markwon.render(cell))); - pendingRowIsHeader = cell.isHeader(); - - return; + public void visit(TableCell cell) { + if (pendingRow == null) { + pendingRow = new ArrayList<>(2); } - if (customNode instanceof TableHead - || customNode instanceof TableRow) { + pendingRow.add(new Table.Column(alignment(cell.getAlignment()), markwon.render(cell))); + pendingRowIsHeader = cell.isHeader(); + } - visitChildren(customNode); + @Override + public void visit(TableHead head) { + visitChildren(head); - // this can happen, ignore such row - if (pendingRow != null && pendingRow.size() > 0) { - - if (rows == null) { - rows = new ArrayList<>(2); - } - - rows.add(new Table.Row(pendingRowIsHeader, pendingRow)); + // this can happen, ignore such row + if (pendingRow != null && pendingRow.size() > 0) { + if (rows == null) { + rows = new ArrayList<>(2); } - pendingRow = null; - pendingRowIsHeader = false; - - return; + rows.add(new Table.Row(pendingRowIsHeader, pendingRow)); } - visitChildren(customNode); + pendingRow = null; + pendingRowIsHeader = false; + } + + @Override + public void visit(TableRow row) { + visitChildren(row); + + // this can happen, ignore such row + if (pendingRow != null && pendingRow.size() > 0) { + if (rows == null) { + rows = new ArrayList<>(2); + } + + rows.add(new Table.Row(pendingRowIsHeader, pendingRow)); + } + + pendingRow = null; + pendingRowIsHeader = false; } @NonNull @@ -206,5 +215,6 @@ public class Table { } return out; } + } } diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java index d5d0f74f..c1b30aef 100644 --- a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java @@ -6,14 +6,14 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import org.commonmark.ext.gfm.tables.TableBlock; -import org.commonmark.ext.gfm.tables.TableBody; -import org.commonmark.ext.gfm.tables.TableCell; -import org.commonmark.ext.gfm.tables.TableHead; -import org.commonmark.ext.gfm.tables.TableRow; -import org.commonmark.ext.gfm.tables.TablesExtension; -import org.commonmark.node.Node; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.ext.tables.TableBlock; +import com.vladsch.flexmark.ext.tables.TableBody; +import com.vladsch.flexmark.ext.tables.TableCell; +import com.vladsch.flexmark.ext.tables.TableHead; +import com.vladsch.flexmark.ext.tables.TableRow; +import com.vladsch.flexmark.ext.tables.TablesExtension; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Node; import java.util.ArrayList; import java.util.Collections; diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableVisitor.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableVisitor.java new file mode 100644 index 00000000..60322eeb --- /dev/null +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableVisitor.java @@ -0,0 +1,28 @@ +package io.noties.markwon.ext.tables; + +import com.vladsch.flexmark.ext.tables.TableCell; +import com.vladsch.flexmark.ext.tables.TableHead; +import com.vladsch.flexmark.ext.tables.TableRow; +import com.vladsch.flexmark.util.ast.VisitHandler; + +/** + *

+ * + * @author cpacm 2023/4/4 + */ +public interface TableVisitor { + + void visit(TableCell node); + + void visit(TableHead head); + + void visit(TableRow row); + + public static VisitHandler[] VISIT_HANDLERS(V visitor) { + return new VisitHandler[]{ + new VisitHandler<>(TableCell.class, visitor::visit), + new VisitHandler<>(TableHead.class, visitor::visit), + new VisitHandler<>(TableRow.class, visitor::visit), + }; + } +} diff --git a/markwon-ext-tasklist/build.gradle b/markwon-ext-tasklist/build.gradle index 315808d4..9f969bbb 100644 --- a/markwon-ext-tasklist/build.gradle +++ b/markwon-ext-tasklist/build.gradle @@ -27,4 +27,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListItem.java b/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListItem.java deleted file mode 100644 index 497bddb2..00000000 --- a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListItem.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.noties.markwon.ext.tasklist; - -import androidx.annotation.NonNull; - -import org.commonmark.node.CustomBlock; - -/** - * @since 1.0.1 - */ -@SuppressWarnings("WeakerAccess") -public class TaskListItem extends CustomBlock { - - private final boolean isDone; - - public TaskListItem(boolean isDone) { - this.isDone = isDone; - } - - public boolean isDone() { - return isDone; - } - - @Override - @NonNull - public String toString() { - return "TaskListItem{" + - "isDone=" + isDone + - '}'; - } -} diff --git a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPlugin.java b/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPlugin.java index a27aeb01..4847b196 100644 --- a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPlugin.java +++ b/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPlugin.java @@ -9,12 +9,15 @@ import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import org.commonmark.parser.Parser; +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension; +import com.vladsch.flexmark.ext.gfm.tasklist.TaskListItem; +import com.vladsch.flexmark.parser.Parser; + +import java.util.Collections; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.MarkwonSpansFactory; import io.noties.markwon.MarkwonVisitor; -import io.noties.markwon.core.SimpleBlockNodeVisitor; /** * @since 3.0.0 @@ -62,20 +65,19 @@ public class TaskListPlugin extends AbstractMarkwonPlugin { this.drawable = drawable; } - @Override - public void configureParser(@NonNull Parser.Builder builder) { - builder.postProcessor(new TaskListPostProcessor()); - } - @Override public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { builder.setFactory(TaskListItem.class, new TaskListSpanFactory(drawable)); } + @Override + public void configureParser(@NonNull Parser.Builder builder) { + builder.extensions(Collections.singleton(TaskListExtension.create())); + } + @Override public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { - builder - .on(TaskListItem.class, new MarkwonVisitor.NodeVisitor() { + builder.on(TaskListItem.class, new MarkwonVisitor.NodeVisitor() { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull TaskListItem taskListItem) { @@ -83,7 +85,7 @@ public class TaskListPlugin extends AbstractMarkwonPlugin { visitor.visitChildren(taskListItem); - TaskListProps.DONE.set(visitor.renderProps(), taskListItem.isDone()); + TaskListProps.DONE.set(visitor.renderProps(), taskListItem.isItemDoneMarker()); visitor.setSpansForNode(taskListItem, length); diff --git a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPostProcessor.java b/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPostProcessor.java deleted file mode 100644 index 7ea97244..00000000 --- a/markwon-ext-tasklist/src/main/java/io/noties/markwon/ext/tasklist/TaskListPostProcessor.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.noties.markwon.ext.tasklist; - -import android.text.TextUtils; - -import org.commonmark.node.AbstractVisitor; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.Paragraph; -import org.commonmark.node.Text; -import org.commonmark.parser.PostProcessor; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.noties.markwon.utils.ParserUtils; - -// @since 4.6.0 -// Hint taken from commonmark-ext-task-list-items artifact -class TaskListPostProcessor implements PostProcessor { - - @Override - public Node process(Node node) { - final TaskListVisitor visitor = new TaskListVisitor(); - node.accept(visitor); - return node; - } - - private static class TaskListVisitor extends AbstractVisitor { - - private static final Pattern REGEX_TASK_LIST_ITEM = Pattern.compile("^\\[([xX\\s])]\\s+(.*)"); - - @Override - public void visit(ListItem listItem) { - // Takes first child and checks if it is Text (we are looking for exact `[xX\s]` without any formatting) - final Node child = listItem.getFirstChild(); - // check if it is paragraph (can contain text) - if (child instanceof Paragraph) { - final Node node = child.getFirstChild(); - if (node instanceof Text) { - - final Text textNode = (Text) node; - final Matcher matcher = REGEX_TASK_LIST_ITEM.matcher(textNode.getLiteral()); - - if (matcher.matches()) { - final String checked = matcher.group(1); - final boolean isChecked = "x".equals(checked) || "X".equals(checked); - - final TaskListItem taskListItem = new TaskListItem(isChecked); - - final Paragraph paragraph = new Paragraph(); - - // insert before list item (directly before inside parent) - listItem.insertBefore(taskListItem); - - // append the rest of matched text (can be empty) - final String restMatchedText = matcher.group(2); - if (!TextUtils.isEmpty(restMatchedText)) { - paragraph.appendChild(new Text(restMatchedText)); - } - - // move all the rest children (from the first paragraph) - ParserUtils.moveChildren(paragraph, node); - - // append our created paragraph - taskListItem.appendChild(paragraph); - - // move all the rest children from the listItem (further nested lists, etc) - ParserUtils.moveChildren(taskListItem, child); - - // remove list item from node - listItem.unlink(); - - // visit taskListItem children - visitChildren(taskListItem); - return; - } - } - } - visitChildren(listItem); - } - } -} diff --git a/markwon-html/build.gradle b/markwon-html/build.gradle index 4337e0d9..adcff6aa 100644 --- a/markwon-html/build.gradle +++ b/markwon-html/build.gradle @@ -22,7 +22,6 @@ dependencies { // we will try to obtain a SpanFactory for a Strikethrough node and use // it to be consistent with markdown (please note that we do not use markwon plugin // for that in case if different implementation is used) - compileOnly it['commonmark-strikethrough'] testImplementation it['ix-java'] } @@ -34,3 +33,4 @@ dependencies { } registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java b/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java index f2c2ea54..0d1f626a 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java @@ -3,9 +3,9 @@ package io.noties.markwon.html; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.HtmlBlock; -import org.commonmark.node.HtmlInline; -import org.commonmark.node.Node; +import com.vladsch.flexmark.ast.HtmlBlock; +import com.vladsch.flexmark.ast.HtmlInline; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.MarkwonConfiguration; @@ -161,13 +161,13 @@ public class HtmlPlugin extends AbstractMarkwonPlugin { .on(HtmlBlock.class, new MarkwonVisitor.NodeVisitor() { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlBlock htmlBlock) { - visitHtml(visitor, htmlBlock.getLiteral()); + visitHtml(visitor, htmlBlock.toAstString(false)); } }) .on(HtmlInline.class, new MarkwonVisitor.NodeVisitor() { @Override public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlInline htmlInline) { - visitHtml(visitor, htmlInline.getLiteral()); + visitHtml(visitor, htmlInline.toAstString(false)); } }); } diff --git a/markwon-html/src/main/java/io/noties/markwon/html/jsoup/nodes/CommonMarkEntities.java b/markwon-html/src/main/java/io/noties/markwon/html/jsoup/nodes/CommonMarkEntities.java index 353f6d9b..ae5db174 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/jsoup/nodes/CommonMarkEntities.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/jsoup/nodes/CommonMarkEntities.java @@ -2,7 +2,7 @@ package io.noties.markwon.html.jsoup.nodes; import androidx.annotation.NonNull; -import org.commonmark.internal.util.Html5Entities; +import com.vladsch.flexmark.util.sequence.Html5Entities; import java.lang.reflect.Field; import java.util.Collections; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/BlockquoteHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/BlockquoteHandler.java index e579a7d3..3c44656f 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/BlockquoteHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/BlockquoteHandler.java @@ -2,7 +2,7 @@ package io.noties.markwon.html.tag; import androidx.annotation.NonNull; -import org.commonmark.node.BlockQuote; +import com.vladsch.flexmark.ast.BlockQuote; import java.util.Collection; import java.util.Collections; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/EmphasisHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/EmphasisHandler.java index d1f7fdd8..672c9ff2 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/EmphasisHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/EmphasisHandler.java @@ -3,7 +3,7 @@ package io.noties.markwon.html.tag; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Emphasis; +import com.vladsch.flexmark.ast.Emphasis; import java.util.Arrays; import java.util.Collection; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/HeadingHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/HeadingHandler.java index 7c694e2d..c46b2bb1 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/HeadingHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/HeadingHandler.java @@ -3,7 +3,7 @@ package io.noties.markwon.html.tag; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Heading; +import com.vladsch.flexmark.ast.Heading; import java.util.Arrays; import java.util.Collection; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/ImageHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/ImageHandler.java index 833c4926..4563c92e 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/ImageHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/ImageHandler.java @@ -5,7 +5,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Image; +import com.vladsch.flexmark.ast.Image; import java.util.Collection; import java.util.Collections; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/LinkHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/LinkHandler.java index d5e032f7..e09f066f 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/LinkHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/LinkHandler.java @@ -5,7 +5,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Link; +import com.vladsch.flexmark.ast.Link; import java.util.Collection; import java.util.Collections; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/ListHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/ListHandler.java index 33499b6c..d8e362c3 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/ListHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/ListHandler.java @@ -2,7 +2,7 @@ package io.noties.markwon.html.tag; import androidx.annotation.NonNull; -import org.commonmark.node.ListItem; +import com.vladsch.flexmark.ast.ListItem; import java.util.Arrays; import java.util.Collection; diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/StrikeHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/StrikeHandler.java index 03a0e952..d594c5da 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/StrikeHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/StrikeHandler.java @@ -5,6 +5,8 @@ import android.text.style.StrikethroughSpan; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough; + import java.util.Arrays; import java.util.Collection; @@ -63,7 +65,7 @@ public class StrikeHandler extends TagHandler { private static Object getMarkdownSpans(@NonNull MarkwonVisitor visitor) { final MarkwonConfiguration configuration = visitor.configuration(); final SpanFactory spanFactory = configuration.spansFactory() - .get(org.commonmark.ext.gfm.strikethrough.Strikethrough.class); + .get(Strikethrough.class); if (spanFactory == null) { return null; } diff --git a/markwon-html/src/main/java/io/noties/markwon/html/tag/StrongEmphasisHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/tag/StrongEmphasisHandler.java index a698ae44..84386dca 100644 --- a/markwon-html/src/main/java/io/noties/markwon/html/tag/StrongEmphasisHandler.java +++ b/markwon-html/src/main/java/io/noties/markwon/html/tag/StrongEmphasisHandler.java @@ -3,7 +3,7 @@ package io.noties.markwon.html.tag; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.StrongEmphasis; +import com.vladsch.flexmark.ast.StrongEmphasis; import java.util.Arrays; import java.util.Collection; diff --git a/markwon-image-coil/build.gradle b/markwon-image-coil/build.gradle index 26fe01ea..911af7bf 100644 --- a/markwon-image-coil/build.gradle +++ b/markwon-image-coil/build.gradle @@ -23,3 +23,4 @@ dependencies { } registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-image-coil/src/main/java/io/noties/markwon/image/coil/CoilImagesPlugin.java b/markwon-image-coil/src/main/java/io/noties/markwon/image/coil/CoilImagesPlugin.java index 605e5146..af4ce8eb 100644 --- a/markwon-image-coil/src/main/java/io/noties/markwon/image/coil/CoilImagesPlugin.java +++ b/markwon-image-coil/src/main/java/io/noties/markwon/image/coil/CoilImagesPlugin.java @@ -8,7 +8,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.commonmark.node.Image; +import com.vladsch.flexmark.ast.Image; import java.util.HashMap; import java.util.Map; diff --git a/markwon-image-glide/build.gradle b/markwon-image-glide/build.gradle index a9d3ef66..0b185ec5 100644 --- a/markwon-image-glide/build.gradle +++ b/markwon-image-glide/build.gradle @@ -18,4 +18,5 @@ dependencies { api deps['glide'] } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java b/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java index c48df4af..93be5d88 100644 --- a/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java +++ b/markwon-image-glide/src/main/java/io/noties/markwon/image/glide/GlideImagesPlugin.java @@ -14,8 +14,7 @@ import com.bumptech.glide.RequestManager; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; - -import org.commonmark.node.Image; +import com.vladsch.flexmark.ast.Image; import java.util.HashMap; import java.util.Map; diff --git a/markwon-image-picasso/build.gradle b/markwon-image-picasso/build.gradle index b646c01c..07c07fa4 100644 --- a/markwon-image-picasso/build.gradle +++ b/markwon-image-picasso/build.gradle @@ -18,4 +18,5 @@ dependencies { api deps['picasso'] } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-image-picasso/src/main/java/io/noties/markwon/image/picasso/PicassoImagesPlugin.java b/markwon-image-picasso/src/main/java/io/noties/markwon/image/picasso/PicassoImagesPlugin.java index ea751030..99c88660 100644 --- a/markwon-image-picasso/src/main/java/io/noties/markwon/image/picasso/PicassoImagesPlugin.java +++ b/markwon-image-picasso/src/main/java/io/noties/markwon/image/picasso/PicassoImagesPlugin.java @@ -14,8 +14,7 @@ import androidx.annotation.Nullable; import com.squareup.picasso.Picasso; import com.squareup.picasso.RequestCreator; import com.squareup.picasso.Target; - -import org.commonmark.node.Image; +import com.vladsch.flexmark.ast.Image; import java.util.HashMap; import java.util.Map; diff --git a/markwon-image/build.gradle b/markwon-image/build.gradle index 9953b415..dfdd3a97 100644 --- a/markwon-image/build.gradle +++ b/markwon-image/build.gradle @@ -34,4 +34,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" diff --git a/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java b/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java index bd653369..eecfd612 100644 --- a/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java +++ b/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.commonmark.node.Image; +import com.vladsch.flexmark.ast.Image; import java.util.concurrent.ExecutorService; diff --git a/markwon-inline-parser/README.md b/markwon-inline-parser/README.md deleted file mode 100644 index bcfa3802..00000000 --- a/markwon-inline-parser/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Inline parser - -**Experimental** due to usage of internal (but still visible) classes of commonmark-java: - -```java -import org.commonmark.internal.Bracket; -import org.commonmark.internal.Delimiter; -import org.commonmark.internal.ReferenceParser; -import org.commonmark.internal.util.Escaping; -import org.commonmark.internal.util.Html5Entities; -import org.commonmark.internal.util.Parsing; -import org.commonmark.internal.inline.AsteriskDelimiterProcessor; -import org.commonmark.internal.inline.UnderscoreDelimiterProcessor; -``` - -`StaggeredDelimiterProcessor` class source is copied (required for InlineParser) \ No newline at end of file diff --git a/markwon-inline-parser/build.gradle b/markwon-inline-parser/build.gradle deleted file mode 100644 index 32a45d7c..00000000 --- a/markwon-inline-parser/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -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') - api deps['x-annotations'] - api deps['commonmark'] - - deps['test'].with { - testImplementation it['junit'] - testImplementation it['commonmark-test-util'] - } -} - -registerArtifact(this) \ No newline at end of file diff --git a/markwon-inline-parser/gradle.properties b/markwon-inline-parser/gradle.properties deleted file mode 100644 index 264a18ee..00000000 --- a/markwon-inline-parser/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -POM_NAME=Inline Parser -POM_ARTIFACT_ID=inline-parser -POM_DESCRIPTION=Markwon customizable commonmark-java InlineParser -POM_PACKAGING=aar \ No newline at end of file diff --git a/markwon-inline-parser/src/main/AndroidManifest.xml b/markwon-inline-parser/src/main/AndroidManifest.xml deleted file mode 100644 index 1a8bcbb5..00000000 --- a/markwon-inline-parser/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/AutolinkInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/AutolinkInlineProcessor.java deleted file mode 100644 index cbba2763..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/AutolinkInlineProcessor.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.node.Link; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -import java.util.regex.Pattern; - -/** - * Parses autolinks, for example {@code } - * - * @since 4.2.0 - */ -public class AutolinkInlineProcessor extends InlineProcessor { - - private static final Pattern EMAIL_AUTOLINK = Pattern - .compile("^<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>"); - - private static final Pattern AUTOLINK = Pattern - .compile("^<[a-zA-Z][a-zA-Z0-9.+-]{1,31}:[^<>\u0000-\u0020]*>"); - - @Override - public char specialCharacter() { - return '<'; - } - - @Override - protected Node parse() { - String m; - if ((m = match(EMAIL_AUTOLINK)) != null) { - String dest = m.substring(1, m.length() - 1); - Link node = new Link("mailto:" + dest, null); - node.appendChild(new Text(dest)); - return node; - } else if ((m = match(AUTOLINK)) != null) { - String dest = m.substring(1, m.length() - 1); - Link node = new Link(dest, null); - node.appendChild(new Text(dest)); - return node; - } else { - return null; - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackslashInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackslashInlineProcessor.java deleted file mode 100644 index c4afc3e0..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackslashInlineProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.node.HardLineBreak; -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -/** - * @since 4.2.0 - */ -public class BackslashInlineProcessor extends InlineProcessor { - - private static final Pattern ESCAPABLE = MarkwonInlineParser.ESCAPABLE; - - @Override - public char specialCharacter() { - return '\\'; - } - - @Override - protected Node parse() { - index++; - Node node; - if (peek() == '\n') { - node = new HardLineBreak(); - index++; - } else if (index < input.length() && ESCAPABLE.matcher(input.substring(index, index + 1)).matches()) { - node = text(input, index, index + 1); - index++; - } else { - node = text("\\"); - } - return node; - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackticksInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackticksInlineProcessor.java deleted file mode 100644 index ef5be678..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BackticksInlineProcessor.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.util.Parsing; -import org.commonmark.node.Code; -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -/** - * Parses inline code surrounded with {@code `} chars {@code `code`} - * - * @since 4.2.0 - */ -public class BackticksInlineProcessor extends InlineProcessor { - - private static final Pattern TICKS = Pattern.compile("`+"); - - private static final Pattern TICKS_HERE = Pattern.compile("^`+"); - - @Override - public char specialCharacter() { - return '`'; - } - - @Override - protected Node parse() { - String ticks = match(TICKS_HERE); - if (ticks == null) { - return null; - } - int afterOpenTicks = index; - String matched; - while ((matched = match(TICKS)) != null) { - if (matched.equals(ticks)) { - Code node = new Code(); - String content = input.substring(afterOpenTicks, index - ticks.length()); - content = content.replace('\n', ' '); - - // spec: If the resulting string both begins and ends with a space character, but does not consist - // entirely of space characters, a single space character is removed from the front and back. - if (content.length() >= 3 && - content.charAt(0) == ' ' && - content.charAt(content.length() - 1) == ' ' && - Parsing.hasNonSpace(content)) { - content = content.substring(1, content.length() - 1); - } - - node.setLiteral(content); - return node; - } - } - // If we got here, we didn't match a closing backtick sequence. - index = afterOpenTicks; - return text(ticks); - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BangInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BangInlineProcessor.java deleted file mode 100644 index eb96c1e9..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/BangInlineProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.Bracket; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -/** - * Parses markdown images {@code ![alt](#href)} - * - * @since 4.2.0 - */ -public class BangInlineProcessor extends InlineProcessor { - @Override - public char specialCharacter() { - return '!'; - } - - @Override - protected Node parse() { - int startIndex = index; - index++; - if (peek() == '[') { - index++; - - Text node = text("!["); - - // Add entry to stack for this opener - addBracket(Bracket.image(node, startIndex + 1, lastBracket(), lastDelimiter())); - - return node; - } else { - return null; - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/CloseBracketInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/CloseBracketInlineProcessor.java deleted file mode 100644 index b9d2f867..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/CloseBracketInlineProcessor.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.Bracket; -import org.commonmark.internal.util.Escaping; -import org.commonmark.node.Image; -import org.commonmark.node.Link; -import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -import static io.noties.markwon.inlineparser.InlineParserUtils.mergeChildTextNodes; - -/** - * Parses markdown link or image, relies on {@link OpenBracketInlineProcessor} - * to handle start of these elements - * - * @since 4.2.0 - */ -public class CloseBracketInlineProcessor extends InlineProcessor { - - private static final Pattern WHITESPACE = MarkwonInlineParser.WHITESPACE; - - @Override - public char specialCharacter() { - return ']'; - } - - @Override - protected Node parse() { - index++; - int startIndex = index; - - // Get previous `[` or `![` - Bracket opener = lastBracket(); - if (opener == null) { - // No matching opener, just return a literal. - return text("]"); - } - - if (!opener.allowed) { - // Matching opener but it's not allowed, just return a literal. - removeLastBracket(); - return text("]"); - } - - // Check to see if we have a link/image - - String dest = null; - String title = null; - boolean isLinkOrImage = false; - - // Maybe a inline link like `[foo](/uri "title")` - if (peek() == '(') { - index++; - spnl(); - if ((dest = parseLinkDestination()) != null) { - spnl(); - // title needs a whitespace before - if (WHITESPACE.matcher(input.substring(index - 1, index)).matches()) { - title = parseLinkTitle(); - spnl(); - } - if (peek() == ')') { - index++; - isLinkOrImage = true; - } else { - index = startIndex; - } - } - } - - // Maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]` - if (!isLinkOrImage) { - - // See if there's a link label like `[bar]` or `[]` - int beforeLabel = index; - parseLinkLabel(); - int labelLength = index - beforeLabel; - String ref = null; - if (labelLength > 2) { - ref = input.substring(beforeLabel, beforeLabel + labelLength); - } else if (!opener.bracketAfter) { - // If the second label is empty `[foo][]` or missing `[foo]`, then the first label is the reference. - // But it can only be a reference when there's no (unescaped) bracket in it. - // If there is, we don't even need to try to look up the reference. This is an optimization. - ref = input.substring(opener.index, startIndex); - } - - if (ref != null) { - String label = Escaping.normalizeReference(ref); - LinkReferenceDefinition definition = context.getLinkReferenceDefinition(label); - if (definition != null) { - dest = definition.getDestination(); - title = definition.getTitle(); - isLinkOrImage = true; - } - } - } - - if (isLinkOrImage) { - // If we got here, open is a potential opener - Node linkOrImage = opener.image ? new Image(dest, title) : new Link(dest, title); - - Node node = opener.node.getNext(); - while (node != null) { - Node next = node.getNext(); - linkOrImage.appendChild(node); - node = next; - } - - // Process delimiters such as emphasis inside link/image - processDelimiters(opener.previousDelimiter); - mergeChildTextNodes(linkOrImage); - // We don't need the corresponding text node anymore, we turned it into a link/image node - opener.node.unlink(); - removeLastBracket(); - - // Links within links are not allowed. We found this link, so there can be no other link around it. - if (!opener.image) { - Bracket bracket = lastBracket(); - while (bracket != null) { - if (!bracket.image) { - // Disallow link opener. It will still get matched, but will not result in a link. - bracket.allowed = false; - } - bracket = bracket.previous; - } - } - - return linkOrImage; - - } else { // no link or image - index = startIndex; - removeLastBracket(); - - return text("]"); - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/EntityInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/EntityInlineProcessor.java deleted file mode 100644 index 353f9902..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/EntityInlineProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.util.Escaping; -import org.commonmark.internal.util.Html5Entities; -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -/** - * Parses HTML entities {@code &} - * - * @since 4.2.0 - */ -public class EntityInlineProcessor extends InlineProcessor { - - private static final Pattern ENTITY_HERE = Pattern.compile('^' + Escaping.ENTITY, Pattern.CASE_INSENSITIVE); - - @Override - public char specialCharacter() { - return '&'; - } - - @Override - protected Node parse() { - String m; - if ((m = match(ENTITY_HERE)) != null) { - return text(Html5Entities.entityToString(m)); - } else { - return null; - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/HtmlInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/HtmlInlineProcessor.java deleted file mode 100644 index d3bd579d..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/HtmlInlineProcessor.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.util.Parsing; -import org.commonmark.node.HtmlInline; -import org.commonmark.node.Node; - -import java.util.regex.Pattern; - -/** - * Parses inline HTML tags - * - * @since 4.2.0 - */ -public class HtmlInlineProcessor extends InlineProcessor { - - private static final String HTMLCOMMENT = "|"; - private static final String PROCESSINGINSTRUCTION = "[<][?].*?[?][>]"; - private static final String DECLARATION = "]*>"; - private static final String CDATA = ""; - private static final String HTMLTAG = "(?:" + Parsing.OPENTAG + "|" + Parsing.CLOSETAG + "|" + HTMLCOMMENT - + "|" + PROCESSINGINSTRUCTION + "|" + DECLARATION + "|" + CDATA + ")"; - private static final Pattern HTML_TAG = Pattern.compile('^' + HTMLTAG, Pattern.CASE_INSENSITIVE); - - @Override - public char specialCharacter() { - return '<'; - } - - @Override - protected Node parse() { - String m = match(HTML_TAG); - if (m != null) { - HtmlInline node = new HtmlInline(); - node.setLiteral(m); - return node; - } else { - return null; - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineParserUtils.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineParserUtils.java deleted file mode 100644 index 1ffb9131..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineParserUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -/** - * @since 4.2.0 - */ -public abstract class InlineParserUtils { - - public static void mergeTextNodesBetweenExclusive(Node fromNode, Node toNode) { - // No nodes between them - if (fromNode == toNode || fromNode.getNext() == toNode) { - return; - } - - mergeTextNodesInclusive(fromNode.getNext(), toNode.getPrevious()); - } - - public static void mergeChildTextNodes(Node node) { - // No children or just one child node, no need for merging - if (node.getFirstChild() == node.getLastChild()) { - return; - } - - mergeTextNodesInclusive(node.getFirstChild(), node.getLastChild()); - } - - public static void mergeTextNodesInclusive(Node fromNode, Node toNode) { - Text first = null; - Text last = null; - int length = 0; - - Node node = fromNode; - while (node != null) { - if (node instanceof Text) { - Text text = (Text) node; - if (first == null) { - first = text; - } - length += text.getLiteral().length(); - last = text; - } else { - mergeIfNeeded(first, last, length); - first = null; - last = null; - length = 0; - } - if (node == toNode) { - break; - } - node = node.getNext(); - } - - mergeIfNeeded(first, last, length); - } - - public static void mergeIfNeeded(Text first, Text last, int textLength) { - if (first != null && last != null && first != last) { - StringBuilder sb = new StringBuilder(textLength); - sb.append(first.getLiteral()); - Node node = first.getNext(); - Node stop = last.getNext(); - while (node != stop) { - sb.append(((Text) node).getLiteral()); - Node unlink = node; - node = node.getNext(); - unlink.unlink(); - } - String literal = sb.toString(); - first.setLiteral(literal); - } - } - - private InlineParserUtils() { - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineProcessor.java deleted file mode 100644 index b7917578..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/InlineProcessor.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.noties.markwon.inlineparser; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.commonmark.internal.Bracket; -import org.commonmark.internal.Delimiter; -import org.commonmark.node.Link; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -import java.util.Map; -import java.util.regex.Pattern; - -/** - * @see AutolinkInlineProcessor - * @see BackslashInlineProcessor - * @see BackticksInlineProcessor - * @see BangInlineProcessor - * @see CloseBracketInlineProcessor - * @see EntityInlineProcessor - * @see HtmlInlineProcessor - * @see NewLineInlineProcessor - * @see OpenBracketInlineProcessor - * @see MarkwonInlineParser.FactoryBuilder#addInlineProcessor(InlineProcessor) - * @see MarkwonInlineParser.FactoryBuilder#excludeInlineProcessor(Class) - * @since 4.2.0 - */ -public abstract class InlineProcessor { - - /** - * Special character that triggers parsing attempt - */ - public abstract char specialCharacter(); - - /** - * @return boolean indicating if parsing succeeded - */ - @Nullable - protected abstract Node parse(); - - - protected MarkwonInlineParserContext context; - protected Node block; - protected String input; - protected int index; - - @Nullable - public Node parse(@NonNull MarkwonInlineParserContext context) { - this.context = context; - this.block = context.block(); - this.input = context.input(); - this.index = context.index(); - - final Node result = parse(); - - // synchronize index - context.setIndex(index); - - return result; - } - - protected Bracket lastBracket() { - return context.lastBracket(); - } - - protected Delimiter lastDelimiter() { - return context.lastDelimiter(); - } - - protected void addBracket(Bracket bracket) { - context.addBracket(bracket); - } - - protected void removeLastBracket() { - context.removeLastBracket(); - } - - protected void spnl() { - context.setIndex(index); - context.spnl(); - index = context.index(); - } - - @Nullable - protected String match(@NonNull Pattern re) { - // before trying to match, we must notify context about our index (which we store additionally here) - context.setIndex(index); - - final String result = context.match(re); - - // after match we must reflect index change here - this.index = context.index(); - - return result; - } - - @Nullable - protected String parseLinkDestination() { - context.setIndex(index); - final String result = context.parseLinkDestination(); - this.index = context.index(); - return result; - } - - @Nullable - protected String parseLinkTitle() { - context.setIndex(index); - final String result = context.parseLinkTitle(); - this.index = context.index(); - return result; - } - - protected int parseLinkLabel() { - context.setIndex(index); - final int result = context.parseLinkLabel(); - this.index = context.index(); - return result; - } - - protected void processDelimiters(Delimiter stackBottom) { - context.setIndex(index); - context.processDelimiters(stackBottom); - this.index = context.index(); - } - - @NonNull - protected Text text(@NonNull String text) { - return context.text(text); - } - - @NonNull - protected Text text(@NonNull String text, int start, int end) { - return context.text(text, start, end); - } - - protected char peek() { - context.setIndex(index); - return context.peek(); - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParser.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParser.java deleted file mode 100644 index 14c24597..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParser.java +++ /dev/null @@ -1,831 +0,0 @@ -package io.noties.markwon.inlineparser; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.commonmark.internal.Bracket; -import org.commonmark.internal.Delimiter; -import org.commonmark.internal.inline.AsteriskDelimiterProcessor; -import org.commonmark.internal.inline.UnderscoreDelimiterProcessor; -import org.commonmark.internal.util.Escaping; -import org.commonmark.internal.util.LinkScanner; -import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.Node; -import org.commonmark.node.Text; -import org.commonmark.parser.InlineParser; -import org.commonmark.parser.InlineParserContext; -import org.commonmark.parser.InlineParserFactory; -import org.commonmark.parser.delimiter.DelimiterProcessor; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static io.noties.markwon.inlineparser.InlineParserUtils.mergeChildTextNodes; -import static io.noties.markwon.inlineparser.InlineParserUtils.mergeTextNodesBetweenExclusive; - -/** - * @see #factoryBuilder() - * @see #factoryBuilderNoDefaults() - * @see FactoryBuilder - * @since 4.2.0 - */ -public class MarkwonInlineParser implements InlineParser, MarkwonInlineParserContext { - - @SuppressWarnings("unused") - public interface FactoryBuilder { - - /** - * @see InlineProcessor - */ - @NonNull - FactoryBuilder addInlineProcessor(@NonNull InlineProcessor processor); - - /** - * @see AsteriskDelimiterProcessor - * @see UnderscoreDelimiterProcessor - */ - @NonNull - FactoryBuilder addDelimiterProcessor(@NonNull DelimiterProcessor processor); - - /** - * Indicate if markdown references are enabled. By default = `true` - */ - @NonNull - FactoryBuilder referencesEnabled(boolean referencesEnabled); - - @NonNull - FactoryBuilder excludeInlineProcessor(@NonNull Class processor); - - @NonNull - FactoryBuilder excludeDelimiterProcessor(@NonNull Class processor); - - @NonNull - InlineParserFactory build(); - } - - public interface FactoryBuilderNoDefaults extends FactoryBuilder { - /** - * Includes all default delimiter and inline processors, and sets {@code referencesEnabled=true}. - * Useful with subsequent calls to {@link #excludeInlineProcessor(Class)} or {@link #excludeDelimiterProcessor(Class)} - */ - @NonNull - FactoryBuilder includeDefaults(); - } - - /** - * Creates an instance of {@link FactoryBuilder} and includes all defaults. - * - * @see #factoryBuilderNoDefaults() - */ - @NonNull - public static FactoryBuilder factoryBuilder() { - return new FactoryBuilderImpl().includeDefaults(); - } - - /** - * NB, this return an empty builder, so if no {@link FactoryBuilderNoDefaults#includeDefaults()} - * is called, it means effectively no inline parsing (unless further calls - * to {@link FactoryBuilder#addInlineProcessor(InlineProcessor)} or {@link FactoryBuilder#addDelimiterProcessor(DelimiterProcessor)}). - */ - @NonNull - public static FactoryBuilderNoDefaults factoryBuilderNoDefaults() { - return new FactoryBuilderImpl(); - } - - private static final String ASCII_PUNCTUATION = "!\"#\\$%&'\\(\\)\\*\\+,\\-\\./:;<=>\\?@\\[\\\\\\]\\^_`\\{\\|\\}~"; - private static final Pattern PUNCTUATION = Pattern - .compile("^[" + ASCII_PUNCTUATION + "\\p{Pc}\\p{Pd}\\p{Pe}\\p{Pf}\\p{Pi}\\p{Po}\\p{Ps}]"); - - private static final Pattern SPNL = Pattern.compile("^ *(?:\n *)?"); - - private static final Pattern UNICODE_WHITESPACE_CHAR = Pattern.compile("^[\\p{Zs}\t\r\n\f]"); - - static final Pattern ESCAPABLE = Pattern.compile('^' + Escaping.ESCAPABLE); - static final Pattern WHITESPACE = Pattern.compile("\\s+"); - - private final InlineParserContext inlineParserContext; - - private final boolean referencesEnabled; - - private final BitSet specialCharacters; - private final Map> inlineProcessors; - private final Map delimiterProcessors; - - // currently we still hold a reference to it because we decided not to - // pass previous node argument to inline-processors (current usage is limited with NewLineInlineProcessor) - private Node block; - private String input; - private int index; - - /** - * Top delimiter (emphasis, strong emphasis or custom emphasis). (Brackets are on a separate stack, different - * from the algorithm described in the spec.) - */ - private Delimiter lastDelimiter; - - /** - * Top opening bracket ([ or ![)). - */ - private Bracket lastBracket; - - // might we construct these in factory? - public MarkwonInlineParser( - @NonNull InlineParserContext inlineParserContext, - boolean referencesEnabled, - @NonNull List inlineProcessors, - @NonNull List delimiterProcessors) { - this.inlineParserContext = inlineParserContext; - this.referencesEnabled = referencesEnabled; - this.inlineProcessors = calculateInlines(inlineProcessors); - this.delimiterProcessors = calculateDelimiterProcessors(delimiterProcessors); - this.specialCharacters = calculateSpecialCharacters( - this.inlineProcessors.keySet(), - this.delimiterProcessors.keySet()); - } - - @NonNull - private static Map> calculateInlines(@NonNull List inlines) { - final Map> map = new HashMap<>(inlines.size()); - List list; - for (InlineProcessor inlineProcessor : inlines) { - final char character = inlineProcessor.specialCharacter(); - list = map.get(character); - if (list == null) { - list = new ArrayList<>(1); - map.put(character, list); - } - list.add(inlineProcessor); - } - return map; - } - - @NonNull - private static BitSet calculateSpecialCharacters(Set inlineCharacters, Set delimiterCharacters) { - final BitSet bitSet = new BitSet(); - for (Character c : inlineCharacters) { - bitSet.set(c); - } - for (Character c : delimiterCharacters) { - bitSet.set(c); - } - return bitSet; - } - - private static Map calculateDelimiterProcessors(List delimiterProcessors) { - Map map = new HashMap<>(); - addDelimiterProcessors(delimiterProcessors, map); - return map; - } - - private static void addDelimiterProcessors(Iterable delimiterProcessors, Map map) { - for (DelimiterProcessor delimiterProcessor : delimiterProcessors) { - char opening = delimiterProcessor.getOpeningCharacter(); - char closing = delimiterProcessor.getClosingCharacter(); - if (opening == closing) { - DelimiterProcessor old = map.get(opening); - if (old != null && old.getOpeningCharacter() == old.getClosingCharacter()) { - StaggeredDelimiterProcessor s; - if (old instanceof StaggeredDelimiterProcessor) { - s = (StaggeredDelimiterProcessor) old; - } else { - s = new StaggeredDelimiterProcessor(opening); - s.add(old); - } - s.add(delimiterProcessor); - map.put(opening, s); - } else { - addDelimiterProcessorForChar(opening, delimiterProcessor, map); - } - } else { - addDelimiterProcessorForChar(opening, delimiterProcessor, map); - addDelimiterProcessorForChar(closing, delimiterProcessor, map); - } - } - } - - private static void addDelimiterProcessorForChar(char delimiterChar, DelimiterProcessor toAdd, Map delimiterProcessors) { - DelimiterProcessor existing = delimiterProcessors.put(delimiterChar, toAdd); - if (existing != null) { - throw new IllegalArgumentException("Delimiter processor conflict with delimiter char '" + delimiterChar + "'"); - } - } - - /** - * Parse content in block into inline children, using reference map to resolve references. - */ - @Override - public void parse(String content, Node block) { - reset(content.trim()); - - // we still reference it - this.block = block; - - while (true) { - Node node = parseInline(); - if (node != null) { - block.appendChild(node); - } else { - break; - } - } - - processDelimiters(null); - mergeChildTextNodes(block); - } - - private void reset(String content) { - this.input = content; - this.index = 0; - this.lastDelimiter = null; - this.lastBracket = null; - } - - /** - * Parse the next inline element in subject, advancing input index. - * On success, add the result to block's children and return true. - * On failure, return false. - */ - @Nullable - private Node parseInline() { - - final char c = peek(); - - if (c == '\0') { - return null; - } - - Node node = null; - - final List inlines = this.inlineProcessors.get(c); - - if (inlines != null) { - // @since 4.6.0 index must not be advanced if inline-processor returned null - // so, further processors can be called at the _same_ position (and thus char) - final int startIndex = index; - - for (InlineProcessor inline : inlines) { - node = inline.parse(this); - if (node != null) { - break; - } - - // reset after each iteration (happens only when node is null) - index = startIndex; - } - } else { - final DelimiterProcessor delimiterProcessor = delimiterProcessors.get(c); - if (delimiterProcessor != null) { - node = parseDelimiters(delimiterProcessor, c); - } else { - node = parseString(); - } - } - - if (node != null) { - return node; - } else { - index++; - // When we get here, it's only for a single special character that turned out to not have a special meaning. - // So we shouldn't have a single surrogate here, hence it should be ok to turn it into a String. - String literal = String.valueOf(c); - return text(literal); - } - } - - /** - * If RE matches at current index in the input, advance index and return the match; otherwise return null. - */ - @Override - @Nullable - public String match(@NonNull Pattern re) { - if (index >= input.length()) { - return null; - } - Matcher matcher = re.matcher(input); - matcher.region(index, input.length()); - boolean m = matcher.find(); - if (m) { - index = matcher.end(); - return matcher.group(); - } else { - return null; - } - } - - @NonNull - @Override - public Text text(@NonNull String text) { - return new Text(text); - } - - @NonNull - @Override - public Text text(@NonNull String text, int beginIndex, int endIndex) { - return new Text(text.substring(beginIndex, endIndex)); - } - - @Nullable - @Override - public LinkReferenceDefinition getLinkReferenceDefinition(String label) { - return referencesEnabled - ? inlineParserContext.getLinkReferenceDefinition(label) - : null; - } - - /** - * Returns the char at the current input index, or {@code '\0'} in case there are no more characters. - */ - @Override - public char peek() { - if (index < input.length()) { - return input.charAt(index); - } else { - return '\0'; - } - } - - @NonNull - @Override - public Node block() { - return block; - } - - @NonNull - @Override - public String input() { - return input; - } - - @Override - public int index() { - return index; - } - - @Override - public void setIndex(int index) { - this.index = index; - } - - @Override - public Bracket lastBracket() { - return lastBracket; - } - - @Override - public Delimiter lastDelimiter() { - return lastDelimiter; - } - - @Override - public void addBracket(Bracket bracket) { - if (lastBracket != null) { - lastBracket.bracketAfter = true; - } - lastBracket = bracket; - } - - @Override - public void removeLastBracket() { - lastBracket = lastBracket.previous; - } - - /** - * Parse zero or more space characters, including at most one newline. - */ - @Override - public void spnl() { - match(SPNL); - } - - /** - * Attempt to parse delimiters like emphasis, strong emphasis or custom delimiters. - */ - @Nullable - private Node parseDelimiters(DelimiterProcessor delimiterProcessor, char delimiterChar) { - DelimiterData res = scanDelimiters(delimiterProcessor, delimiterChar); - if (res == null) { - return null; - } - int length = res.count; - int startIndex = index; - - index += length; - Text node = text(input, startIndex, index); - - // Add entry to stack for this opener - lastDelimiter = new Delimiter(node, delimiterChar, res.canOpen, res.canClose, lastDelimiter); - lastDelimiter.length = length; - lastDelimiter.originalLength = length; - if (lastDelimiter.previous != null) { - lastDelimiter.previous.next = lastDelimiter; - } - - return node; - } - - /** - * Attempt to parse link destination, returning the string or null if no match. - */ - @Override - @Nullable - public String parseLinkDestination() { - int afterDest = LinkScanner.scanLinkDestination(input, index); - if (afterDest == -1) { - return null; - } - - String dest; - if (peek() == '<') { - // chop off surrounding <..>: - dest = input.substring(index + 1, afterDest - 1); - } else { - dest = input.substring(index, afterDest); - } - - index = afterDest; - return Escaping.unescapeString(dest); - } - - /** - * Attempt to parse link title (sans quotes), returning the string or null if no match. - */ - @Override - @Nullable - public String parseLinkTitle() { - int afterTitle = LinkScanner.scanLinkTitle(input, index); - if (afterTitle == -1) { - return null; - } - - // chop off ', " or parens - String title = input.substring(index + 1, afterTitle - 1); - index = afterTitle; - return Escaping.unescapeString(title); - } - - /** - * Attempt to parse a link label, returning number of characters parsed. - */ - @Override - public int parseLinkLabel() { - if (index >= input.length() || input.charAt(index) != '[') { - return 0; - } - - int startContent = index + 1; - int endContent = LinkScanner.scanLinkLabelContent(input, startContent); - // spec: A link label can have at most 999 characters inside the square brackets. - int contentLength = endContent - startContent; - if (endContent == -1 || contentLength > 999) { - return 0; - } - if (endContent >= input.length() || input.charAt(endContent) != ']') { - return 0; - } - index = endContent + 1; - return contentLength + 2; - } - - /** - * Parse a run of ordinary characters, or a single character with a special meaning in markdown, as a plain string. - */ - private Node parseString() { - int begin = index; - int length = input.length(); - while (index != length) { - if (specialCharacters.get(input.charAt(index))) { - break; - } - index++; - } - if (begin != index) { - return text(input, begin, index); - } else { - return null; - } - } - - /** - * Scan a sequence of characters with code delimiterChar, and return information about the number of delimiters - * and whether they are positioned such that they can open and/or close emphasis or strong emphasis. - * - * @return information about delimiter run, or {@code null} - */ - private DelimiterData scanDelimiters(DelimiterProcessor delimiterProcessor, char delimiterChar) { - int startIndex = index; - - int delimiterCount = 0; - while (peek() == delimiterChar) { - delimiterCount++; - index++; - } - - if (delimiterCount < delimiterProcessor.getMinLength()) { - index = startIndex; - return null; - } - - String before = startIndex == 0 ? "\n" : - input.substring(startIndex - 1, startIndex); - - char charAfter = peek(); - String after = charAfter == '\0' ? "\n" : - String.valueOf(charAfter); - - // We could be more lazy here, in most cases we don't need to do every match case. - boolean beforeIsPunctuation = PUNCTUATION.matcher(before).matches(); - boolean beforeIsWhitespace = UNICODE_WHITESPACE_CHAR.matcher(before).matches(); - boolean afterIsPunctuation = PUNCTUATION.matcher(after).matches(); - boolean afterIsWhitespace = UNICODE_WHITESPACE_CHAR.matcher(after).matches(); - - boolean leftFlanking = !afterIsWhitespace && - (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation); - boolean rightFlanking = !beforeIsWhitespace && - (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation); - boolean canOpen; - boolean canClose; - if (delimiterChar == '_') { - canOpen = leftFlanking && (!rightFlanking || beforeIsPunctuation); - canClose = rightFlanking && (!leftFlanking || afterIsPunctuation); - } else { - canOpen = leftFlanking && delimiterChar == delimiterProcessor.getOpeningCharacter(); - canClose = rightFlanking && delimiterChar == delimiterProcessor.getClosingCharacter(); - } - - index = startIndex; - return new DelimiterData(delimiterCount, canOpen, canClose); - } - - @Override - public void processDelimiters(Delimiter stackBottom) { - - Map openersBottom = new HashMap<>(); - - // find first closer above stackBottom: - Delimiter closer = lastDelimiter; - while (closer != null && closer.previous != stackBottom) { - closer = closer.previous; - } - // move forward, looking for closers, and handling each - while (closer != null) { - char delimiterChar = closer.delimiterChar; - - DelimiterProcessor delimiterProcessor = delimiterProcessors.get(delimiterChar); - if (!closer.canClose || delimiterProcessor == null) { - closer = closer.next; - continue; - } - - char openingDelimiterChar = delimiterProcessor.getOpeningCharacter(); - - // Found delimiter closer. Now look back for first matching opener. - int useDelims = 0; - boolean openerFound = false; - boolean potentialOpenerFound = false; - Delimiter opener = closer.previous; - while (opener != null && opener != stackBottom && opener != openersBottom.get(delimiterChar)) { - if (opener.canOpen && opener.delimiterChar == openingDelimiterChar) { - potentialOpenerFound = true; - useDelims = delimiterProcessor.getDelimiterUse(opener, closer); - if (useDelims > 0) { - openerFound = true; - break; - } - } - opener = opener.previous; - } - - if (!openerFound) { - if (!potentialOpenerFound) { - // Set lower bound for future searches for openers. - // Only do this when we didn't even have a potential - // opener (one that matches the character and can open). - // If an opener was rejected because of the number of - // delimiters (e.g. because of the "multiple of 3" rule), - // we want to consider it next time because the number - // of delimiters can change as we continue processing. - openersBottom.put(delimiterChar, closer.previous); - if (!closer.canOpen) { - // We can remove a closer that can't be an opener, - // once we've seen there's no matching opener: - removeDelimiterKeepNode(closer); - } - } - closer = closer.next; - continue; - } - - Text openerNode = opener.node; - Text closerNode = closer.node; - - // Remove number of used delimiters from stack and inline nodes. - opener.length -= useDelims; - closer.length -= useDelims; - openerNode.setLiteral( - openerNode.getLiteral().substring(0, - openerNode.getLiteral().length() - useDelims)); - closerNode.setLiteral( - closerNode.getLiteral().substring(0, - closerNode.getLiteral().length() - useDelims)); - - removeDelimitersBetween(opener, closer); - // The delimiter processor can re-parent the nodes between opener and closer, - // so make sure they're contiguous already. Exclusive because we want to keep opener/closer themselves. - mergeTextNodesBetweenExclusive(openerNode, closerNode); - delimiterProcessor.process(openerNode, closerNode, useDelims); - - // No delimiter characters left to process, so we can remove delimiter and the now empty node. - if (opener.length == 0) { - removeDelimiterAndNode(opener); - } - - if (closer.length == 0) { - Delimiter next = closer.next; - removeDelimiterAndNode(closer); - closer = next; - } - } - - // remove all delimiters - while (lastDelimiter != null && lastDelimiter != stackBottom) { - removeDelimiterKeepNode(lastDelimiter); - } - } - - private void removeDelimitersBetween(Delimiter opener, Delimiter closer) { - Delimiter delimiter = closer.previous; - while (delimiter != null && delimiter != opener) { - Delimiter previousDelimiter = delimiter.previous; - removeDelimiterKeepNode(delimiter); - delimiter = previousDelimiter; - } - } - - /** - * Remove the delimiter and the corresponding text node. For used delimiters, e.g. `*` in `*foo*`. - */ - private void removeDelimiterAndNode(Delimiter delim) { - Text node = delim.node; - node.unlink(); - removeDelimiter(delim); - } - - /** - * Remove the delimiter but keep the corresponding node as text. For unused delimiters such as `_` in `foo_bar`. - */ - private void removeDelimiterKeepNode(Delimiter delim) { - removeDelimiter(delim); - } - - private void removeDelimiter(Delimiter delim) { - if (delim.previous != null) { - delim.previous.next = delim.next; - } - if (delim.next == null) { - // top of stack - lastDelimiter = delim.previous; - } else { - delim.next.previous = delim.previous; - } - } - - private static class DelimiterData { - - final int count; - final boolean canClose; - final boolean canOpen; - - DelimiterData(int count, boolean canOpen, boolean canClose) { - this.count = count; - this.canOpen = canOpen; - this.canClose = canClose; - } - } - - static class FactoryBuilderImpl implements FactoryBuilder, FactoryBuilderNoDefaults { - - private final List inlineProcessors = new ArrayList<>(3); - private final List delimiterProcessors = new ArrayList<>(3); - private boolean referencesEnabled; - - @NonNull - @Override - public FactoryBuilder addInlineProcessor(@NonNull InlineProcessor processor) { - this.inlineProcessors.add(processor); - return this; - } - - @NonNull - @Override - public FactoryBuilder addDelimiterProcessor(@NonNull DelimiterProcessor processor) { - this.delimiterProcessors.add(processor); - return this; - } - - @NonNull - @Override - public FactoryBuilder referencesEnabled(boolean referencesEnabled) { - this.referencesEnabled = referencesEnabled; - return this; - } - - @NonNull - @Override - public FactoryBuilder includeDefaults() { - - // by default enabled - this.referencesEnabled = true; - - this.inlineProcessors.addAll(Arrays.asList( - new AutolinkInlineProcessor(), - new BackslashInlineProcessor(), - new BackticksInlineProcessor(), - new BangInlineProcessor(), - new CloseBracketInlineProcessor(), - new EntityInlineProcessor(), - new HtmlInlineProcessor(), - new NewLineInlineProcessor(), - new OpenBracketInlineProcessor())); - - this.delimiterProcessors.addAll(Arrays.asList( - new AsteriskDelimiterProcessor(), - new UnderscoreDelimiterProcessor())); - - return this; - } - - @NonNull - @Override - public FactoryBuilder excludeInlineProcessor(@NonNull Class type) { - for (int i = 0, size = inlineProcessors.size(); i < size; i++) { - if (type.equals(inlineProcessors.get(i).getClass())) { - inlineProcessors.remove(i); - break; - } - } - return this; - } - - @NonNull - @Override - public FactoryBuilder excludeDelimiterProcessor(@NonNull Class type) { - for (int i = 0, size = delimiterProcessors.size(); i < size; i++) { - if (type.equals(delimiterProcessors.get(i).getClass())) { - delimiterProcessors.remove(i); - break; - } - } - return this; - } - - @NonNull - @Override - public InlineParserFactory build() { - return new InlineParserFactoryImpl(referencesEnabled, inlineProcessors, delimiterProcessors); - } - } - - static class InlineParserFactoryImpl implements InlineParserFactory { - - private final boolean referencesEnabled; - private final List inlineProcessors; - private final List delimiterProcessors; - - InlineParserFactoryImpl( - boolean referencesEnabled, - @NonNull List inlineProcessors, - @NonNull List delimiterProcessors) { - this.referencesEnabled = referencesEnabled; - this.inlineProcessors = inlineProcessors; - this.delimiterProcessors = delimiterProcessors; - } - - @Override - public InlineParser create(InlineParserContext inlineParserContext) { - final List delimiterProcessors; - final List customDelimiterProcessors = inlineParserContext.getCustomDelimiterProcessors(); - final int size = customDelimiterProcessors != null - ? customDelimiterProcessors.size() - : 0; - if (size > 0) { - delimiterProcessors = new ArrayList<>(size + this.delimiterProcessors.size()); - delimiterProcessors.addAll(this.delimiterProcessors); - delimiterProcessors.addAll(customDelimiterProcessors); - } else { - delimiterProcessors = this.delimiterProcessors; - } - return new MarkwonInlineParser( - inlineParserContext, - referencesEnabled, - inlineProcessors, - delimiterProcessors); - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserContext.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserContext.java deleted file mode 100644 index 46870f91..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserContext.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.noties.markwon.inlineparser; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.commonmark.internal.Bracket; -import org.commonmark.internal.Delimiter; -import org.commonmark.node.Link; -import org.commonmark.node.LinkReferenceDefinition; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -import java.util.Map; -import java.util.regex.Pattern; - -public interface MarkwonInlineParserContext { - - @NonNull - Node block(); - - @NonNull - String input(); - - int index(); - - void setIndex(int index); - - Bracket lastBracket(); - - Delimiter lastDelimiter(); - - void addBracket(Bracket bracket); - - void removeLastBracket(); - - void spnl(); - - /** - * Returns the char at the current input index, or {@code '\0'} in case there are no more characters. - */ - char peek(); - - @Nullable - String match(@NonNull Pattern re); - - @NonNull - Text text(@NonNull String text); - - @NonNull - Text text(@NonNull String text, int beginIndex, int endIndex); - - @Nullable - LinkReferenceDefinition getLinkReferenceDefinition(String label); - - @Nullable - String parseLinkDestination(); - - @Nullable - String parseLinkTitle(); - - int parseLinkLabel(); - - void processDelimiters(Delimiter stackBottom); -} 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 deleted file mode 100644 index 470e2fb8..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/MarkwonInlineParserPlugin.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.noties.markwon.inlineparser; - -import androidx.annotation.NonNull; - -import org.commonmark.parser.Parser; - -import io.noties.markwon.AbstractMarkwonPlugin; - -/** - * @since 4.3.0 - */ -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/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/NewLineInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/NewLineInlineProcessor.java deleted file mode 100644 index ef978b72..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/NewLineInlineProcessor.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.node.HardLineBreak; -import org.commonmark.node.Node; -import org.commonmark.node.SoftLineBreak; -import org.commonmark.node.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @since 4.2.0 - */ -public class NewLineInlineProcessor extends InlineProcessor { - - private static final Pattern FINAL_SPACE = Pattern.compile(" *$"); - - @Override - public char specialCharacter() { - return '\n'; - } - - @Override - protected Node parse() { - index++; // assume we're at a \n - - final Node previous = block.getLastChild(); - - // Check previous text for trailing spaces. - // The "endsWith" is an optimization to avoid an RE match in the common case. - if (previous instanceof Text && ((Text) previous).getLiteral().endsWith(" ")) { - Text text = (Text) previous; - String literal = text.getLiteral(); - Matcher matcher = FINAL_SPACE.matcher(literal); - int spaces = matcher.find() ? matcher.end() - matcher.start() : 0; - if (spaces > 0) { - text.setLiteral(literal.substring(0, literal.length() - spaces)); - } - if (spaces >= 2) { - return new HardLineBreak(); - } else { - return new SoftLineBreak(); - } - } else { - return new SoftLineBreak(); - } - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/OpenBracketInlineProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/OpenBracketInlineProcessor.java deleted file mode 100644 index 070d9ccc..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/OpenBracketInlineProcessor.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.internal.Bracket; -import org.commonmark.node.Node; -import org.commonmark.node.Text; - -/** - * Parses markdown links {@code [link](#href)} - * - * @since 4.2.0 - */ -public class OpenBracketInlineProcessor extends InlineProcessor { - @Override - public char specialCharacter() { - return '['; - } - - @Override - protected Node parse() { - int startIndex = index; - index++; - - Text node = text("["); - - // Add entry to stack for this opener - addBracket(Bracket.link(node, startIndex, lastBracket(), lastDelimiter())); - - return node; - } -} diff --git a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/StaggeredDelimiterProcessor.java b/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/StaggeredDelimiterProcessor.java deleted file mode 100644 index c2a92c3d..00000000 --- a/markwon-inline-parser/src/main/java/io/noties/markwon/inlineparser/StaggeredDelimiterProcessor.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.node.Text; -import org.commonmark.parser.delimiter.DelimiterProcessor; -import org.commonmark.parser.delimiter.DelimiterRun; - -import java.util.LinkedList; -import java.util.ListIterator; - -class StaggeredDelimiterProcessor implements DelimiterProcessor { - - private final char delim; - private int minLength = 0; - private LinkedList processors = new LinkedList<>(); // in reverse getMinLength order - - StaggeredDelimiterProcessor(char delim) { - this.delim = delim; - } - - @Override - public char getOpeningCharacter() { - return delim; - } - - @Override - public char getClosingCharacter() { - return delim; - } - - @Override - public int getMinLength() { - return minLength; - } - - void add(DelimiterProcessor dp) { - final int len = dp.getMinLength(); - ListIterator it = processors.listIterator(); - boolean added = false; - while (it.hasNext()) { - DelimiterProcessor p = it.next(); - int pLen = p.getMinLength(); - if (len > pLen) { - it.previous(); - it.add(dp); - added = true; - break; - } else if (len == pLen) { - throw new IllegalArgumentException("Cannot add two delimiter processors for char '" + delim + "' and minimum length " + len); - } - } - if (!added) { - processors.add(dp); - this.minLength = len; - } - } - - private DelimiterProcessor findProcessor(int len) { - for (DelimiterProcessor p : processors) { - if (p.getMinLength() <= len) { - return p; - } - } - return processors.getFirst(); - } - - @Override - public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) { - return findProcessor(opener.length()).getDelimiterUse(opener, closer); - } - - @Override - public void process(Text opener, Text closer, int delimiterUse) { - findProcessor(delimiterUse).process(opener, closer, delimiterUse); - } -} diff --git a/markwon-inline-parser/src/test/java/io/noties/markwon/inlineparser/InlineParserSpecTest.java b/markwon-inline-parser/src/test/java/io/noties/markwon/inlineparser/InlineParserSpecTest.java deleted file mode 100644 index b7afb01d..00000000 --- a/markwon-inline-parser/src/test/java/io/noties/markwon/inlineparser/InlineParserSpecTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.noties.markwon.inlineparser; - -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.commonmark.testutil.SpecTestCase; -import org.commonmark.testutil.example.Example; - -public class InlineParserSpecTest extends SpecTestCase { - - private static final Parser PARSER = Parser.builder() - .inlineParserFactory(MarkwonInlineParser.factoryBuilder().build()) - .build(); - - // The spec says URL-escaping is optional, but the examples assume that it's enabled. - private static final HtmlRenderer RENDERER = HtmlRenderer.builder().percentEncodeUrls(true).build(); - - public InlineParserSpecTest(Example example) { - super(example); - } - - @Override - protected String render(String source) { - return RENDERER.render(PARSER.parse(source)); - } -} diff --git a/markwon-linkify/build.gradle b/markwon-linkify/build.gradle index 39f8eecf..fec2ee9a 100644 --- a/markwon-linkify/build.gradle +++ b/markwon-linkify/build.gradle @@ -24,3 +24,4 @@ dependencies { } registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-linkify/src/main/java/io/noties/markwon/linkify/LinkifyPlugin.java b/markwon-linkify/src/main/java/io/noties/markwon/linkify/LinkifyPlugin.java index ded176f6..a58ee0c2 100644 --- a/markwon-linkify/src/main/java/io/noties/markwon/linkify/LinkifyPlugin.java +++ b/markwon-linkify/src/main/java/io/noties/markwon/linkify/LinkifyPlugin.java @@ -9,7 +9,7 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.core.text.util.LinkifyCompat; -import org.commonmark.node.Link; +import com.vladsch.flexmark.ast.Link; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/markwon-recycler-table/build.gradle b/markwon-recycler-table/build.gradle index 841c2b60..757e5877 100644 --- a/markwon-recycler-table/build.gradle +++ b/markwon-recycler-table/build.gradle @@ -26,4 +26,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntry.java b/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntry.java index 2784221f..70523252 100644 --- a/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntry.java +++ b/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntry.java @@ -19,7 +19,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Px; import androidx.annotation.VisibleForTesting; -import org.commonmark.ext.gfm.tables.TableBlock; +import com.vladsch.flexmark.ext.tables.TableBlock; import java.util.HashMap; import java.util.List; diff --git a/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntryPlugin.java b/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntryPlugin.java index b917bb19..6e7834f1 100644 --- a/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntryPlugin.java +++ b/markwon-recycler-table/src/main/java/io/noties/markwon/recycler/table/TableEntryPlugin.java @@ -4,8 +4,9 @@ import android.content.Context; import androidx.annotation.NonNull; -import org.commonmark.ext.gfm.tables.TablesExtension; -import org.commonmark.parser.Parser; + +import com.vladsch.flexmark.ext.tables.TablesExtension; +import com.vladsch.flexmark.parser.Parser; import java.util.Collections; diff --git a/markwon-recycler/build.gradle b/markwon-recycler/build.gradle index 63c7742e..857cb9d3 100644 --- a/markwon-recycler/build.gradle +++ b/markwon-recycler/build.gradle @@ -22,4 +22,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapter.java b/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapter.java index 265c9273..f0af0802 100644 --- a/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapter.java +++ b/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapter.java @@ -10,7 +10,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import java.util.List; diff --git a/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapterImpl.java b/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapterImpl.java index d773edcc..bfefce28 100644 --- a/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapterImpl.java +++ b/markwon-recycler/src/main/java/io/noties/markwon/recycler/MarkwonAdapterImpl.java @@ -6,7 +6,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import java.util.Collections; import java.util.List; diff --git a/markwon-recycler/src/main/java/io/noties/markwon/recycler/SimpleEntry.java b/markwon-recycler/src/main/java/io/noties/markwon/recycler/SimpleEntry.java index b55be1fb..6b4a3e23 100644 --- a/markwon-recycler/src/main/java/io/noties/markwon/recycler/SimpleEntry.java +++ b/markwon-recycler/src/main/java/io/noties/markwon/recycler/SimpleEntry.java @@ -10,7 +10,7 @@ import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; -import org.commonmark.node.Node; +import com.vladsch.flexmark.util.ast.Node; import java.util.HashMap; import java.util.Map; diff --git a/markwon-simple-ext/build.gradle b/markwon-simple-ext/build.gradle index 39dc63da..c9e58114 100644 --- a/markwon-simple-ext/build.gradle +++ b/markwon-simple-ext/build.gradle @@ -24,4 +24,5 @@ dependencies { } } -registerArtifact(this) \ No newline at end of file +registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file 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 index 927ba3f0..5da5d1e3 100644 --- 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 @@ -2,7 +2,8 @@ package io.noties.markwon.simple.ext; import androidx.annotation.NonNull; -import org.commonmark.parser.delimiter.DelimiterProcessor; + +import com.vladsch.flexmark.parser.delimiter.DelimiterProcessor; import java.util.ArrayList; import java.util.List; 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 index 924525b0..0b895821 100644 --- 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 @@ -2,10 +2,11 @@ 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 com.vladsch.flexmark.parser.InlineParser; +import com.vladsch.flexmark.parser.core.delimiter.Delimiter; +import com.vladsch.flexmark.parser.delimiter.DelimiterProcessor; +import com.vladsch.flexmark.parser.delimiter.DelimiterRun; +import com.vladsch.flexmark.util.ast.Node; import io.noties.markwon.SpanFactory; @@ -52,19 +53,37 @@ class SimpleExtDelimiterProcessor implements DelimiterProcessor { } @Override - public void process(Text opener, Text closer, int delimiterUse) { - + public void process(Delimiter opener, Delimiter closer, int delimitersUsed) { final Node node = new SimpleExtNode(spanFactory); - - Node tmp = opener.getNext(); - Node next; + Delimiter tmp = opener.getNext(); + Delimiter next; while (tmp != null && tmp != closer) { next = tmp.getNext(); - node.appendChild(tmp); + node.appendChild(tmp.getNode()); tmp = next; } - opener.insertAfter(node); + opener.getNode().insertAfter(node); + } + + @Override + public Node unmatchedDelimiterNode(InlineParser inlineParser, DelimiterRun delimiter) { + return null; + } + + @Override + public boolean canBeOpener(String before, String after, boolean leftFlanking, boolean rightFlanking, boolean beforeIsPunctuation, boolean afterIsPunctuation, boolean beforeIsWhitespace, boolean afterIsWhiteSpace) { + return false; + } + + @Override + public boolean canBeCloser(String before, String after, boolean leftFlanking, boolean rightFlanking, boolean beforeIsPunctuation, boolean afterIsPunctuation, boolean beforeIsWhitespace, boolean afterIsWhiteSpace) { + return false; + } + + @Override + public boolean skipNonOpenerCloser() { + return false; } } 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 index 8d1173c3..2259c704 100644 --- 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 @@ -2,13 +2,15 @@ package io.noties.markwon.simple.ext; import androidx.annotation.NonNull; -import org.commonmark.node.CustomNode; -import org.commonmark.node.Visitor; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.sequence.BasedSequence; + +import org.jetbrains.annotations.NotNull; import io.noties.markwon.SpanFactory; // @since 4.0.0 -class SimpleExtNode extends CustomNode { +class SimpleExtNode extends Node { private final SpanFactory spanFactory; @@ -16,13 +18,14 @@ class SimpleExtNode extends CustomNode { this.spanFactory = spanFactory; } - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - @NonNull SpanFactory spanFactory() { return spanFactory; } + + @NonNull + @Override + public @NotNull BasedSequence[] getSegments() { + return BasedSequence.EMPTY_SEGMENTS; + } } 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 index a5d2cf8c..9a34b2a6 100644 --- 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 @@ -2,8 +2,8 @@ package io.noties.markwon.simple.ext; import androidx.annotation.NonNull; -import org.commonmark.parser.Parser; -import org.commonmark.parser.delimiter.DelimiterProcessor; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.parser.delimiter.DelimiterProcessor; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.MarkwonVisitor; diff --git a/markwon-syntax-highlight/build.gradle b/markwon-syntax-highlight/build.gradle index 4c6586ed..3f6fb645 100644 --- a/markwon-syntax-highlight/build.gradle +++ b/markwon-syntax-highlight/build.gradle @@ -23,3 +23,4 @@ dependencies { } registerArtifact(this) +apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle" \ No newline at end of file diff --git a/markwon-test-span/build.gradle b/markwon-test-span/build.gradle index bdf64b2e..68415f0a 100644 --- a/markwon-test-span/build.gradle +++ b/markwon-test-span/build.gradle @@ -21,4 +21,4 @@ dependencies { deps['test'].with { api it['junit'] } -} +} \ No newline at end of file diff --git a/sample-utils/processor/build.gradle b/sample-utils/processor/build.gradle index d2664b3f..ba1aeb79 100644 --- a/sample-utils/processor/build.gradle +++ b/sample-utils/processor/build.gradle @@ -1,8 +1,5 @@ apply plugin: 'java-library' -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - sourceSets { main.java.srcDirs += '../annotations' } diff --git a/settings.gradle b/settings.gradle index 7811c39e..c3430d41 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,7 +10,6 @@ include ':markwon-core', ':markwon-image-coil', ':markwon-image-glide', ':markwon-image-picasso', - ':markwon-inline-parser', ':markwon-linkify', ':markwon-recycler', ':markwon-recycler-table',