replace commonMark to flexMark
This commit is contained in:
parent
2ea148c30a
commit
b6135ecc56
@ -44,12 +44,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
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 {
|
kapt {
|
||||||
@ -122,7 +76,7 @@ dependencies {
|
|||||||
kapt it['prism4j-bundler']
|
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-core')
|
||||||
implementation project(':markwon-editor')
|
implementation project(':markwon-editor')
|
||||||
@ -132,7 +86,6 @@ dependencies {
|
|||||||
implementation project(':markwon-ext-tasklist')
|
implementation project(':markwon-ext-tasklist')
|
||||||
implementation project(':markwon-html')
|
implementation project(':markwon-html')
|
||||||
implementation project(':markwon-image')
|
implementation project(':markwon-image')
|
||||||
implementation project(':markwon-inline-parser')
|
|
||||||
implementation project(':markwon-linkify')
|
implementation project(':markwon-linkify')
|
||||||
implementation project(':markwon-recycler')
|
implementation project(':markwon-recycler')
|
||||||
implementation project(':markwon-recycler-table')
|
implementation project(':markwon-recycler-table')
|
||||||
|
@ -651,18 +651,6 @@
|
|||||||
"parsing"
|
"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",
|
"javaClassName": "io.noties.markwon.app.samples.html.HtmlDisableSanitizeSample",
|
||||||
"id": "20200630171424",
|
"id": "20200630171424",
|
||||||
@ -957,31 +945,6 @@
|
|||||||
"editor"
|
"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",
|
"javaClassName": "io.noties.markwon.app.samples.editor.EditorNewLineContinuationSample",
|
||||||
"id": "20200629170348",
|
"id": "20200629170348",
|
||||||
@ -1076,23 +1039,6 @@
|
|||||||
"editor"
|
"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",
|
"javaClassName": "io.noties.markwon.app.samples.GithubUserIssueOnTextAddedSample",
|
||||||
"id": "20200629162024",
|
"id": "20200629162024",
|
||||||
@ -1107,21 +1053,6 @@
|
|||||||
"textAddedListener"
|
"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",
|
"javaClassName": "io.noties.markwon.app.samples.ReadMorePluginSample",
|
||||||
"id": "20200629161505",
|
"id": "20200629161505",
|
||||||
@ -1410,20 +1341,6 @@
|
|||||||
"defaults"
|
"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",
|
"javaClassName": "io.noties.markwon.app.samples.ToastDynamicContentSample",
|
||||||
"id": "20200627074017",
|
"id": "20200627074017",
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
||||||
|
|
||||||
<activity android:name=".sample.MainActivity">
|
<activity
|
||||||
|
android:name=".sample.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
<!-- launcher intent -->
|
<!-- launcher intent -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@ -42,7 +44,7 @@
|
|||||||
android:host="noties.io"
|
android:host="noties.io"
|
||||||
android:scheme="https" />
|
android:scheme="https" />
|
||||||
|
|
||||||
<data android:pathPrefix="/Markwon/app"/>
|
<data android:pathPrefix="/Markwon/app" />
|
||||||
|
|
||||||
<data android:pathPattern="sample/.*" />
|
<data android:pathPattern="sample/.*" />
|
||||||
<data android:pathPattern="search" />
|
<data android:pathPattern="search" />
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.BlockHandlerDef;
|
import io.noties.markwon.BlockHandlerDef;
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -4,7 +4,8 @@ import android.text.style.BulletSpan;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
|
|
||||||
|
|
||||||
import io.noties.debug.Debug;
|
import io.noties.debug.Debug;
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
|
@ -10,6 +10,7 @@ import android.text.style.ClickableSpan
|
|||||||
import android.text.style.LeadingMarginSpan
|
import android.text.style.LeadingMarginSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.vladsch.flexmark.ast.FencedCodeBlock
|
||||||
import io.noties.debug.Debug
|
import io.noties.debug.Debug
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin
|
import io.noties.markwon.AbstractMarkwonPlugin
|
||||||
import io.noties.markwon.Markwon
|
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.MarkwonSampleInfo
|
||||||
import io.noties.markwon.sample.annotations.Tag
|
import io.noties.markwon.sample.annotations.Tag
|
||||||
import io.noties.markwon.utils.LeadingMarginUtils
|
import io.noties.markwon.utils.LeadingMarginUtils
|
||||||
import org.commonmark.node.FencedCodeBlock
|
|
||||||
|
|
||||||
@MarkwonSampleInfo(
|
@MarkwonSampleInfo(
|
||||||
id = "20210315112847",
|
id = "20210315112847",
|
||||||
title = "Copy code block",
|
title = "Copy code block",
|
||||||
description = "Copy contents of fenced code blocks",
|
description = "Copy contents of fenced code blocks",
|
||||||
artifacts = [MarkwonArtifact.CORE],
|
artifacts = [MarkwonArtifact.CORE],
|
||||||
tags = [Tag.rendering, Tag.block, Tag.spanFactory, Tag.span]
|
tags = [Tag.rendering, Tag.block, Tag.spanFactory, Tag.span]
|
||||||
)
|
)
|
||||||
class CopyCodeBlockSample : MarkwonTextViewSample() {
|
class CopyCodeBlockSample : MarkwonTextViewSample() {
|
||||||
|
|
||||||
override fun render() {
|
override fun render() {
|
||||||
val md = """
|
val md = """
|
||||||
# Hello code blocks!
|
# Hello code blocks!
|
||||||
```java
|
```java
|
||||||
final int i = 0;
|
final int i = 0;
|
||||||
@ -43,77 +43,77 @@ class CopyCodeBlockSample : MarkwonTextViewSample() {
|
|||||||
bye bye!
|
bye bye!
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val markwon = Markwon.builder(context)
|
val markwon = Markwon.builder(context)
|
||||||
.usePlugin(object : AbstractMarkwonPlugin() {
|
.usePlugin(object : AbstractMarkwonPlugin() {
|
||||||
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
|
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
|
||||||
builder.appendFactory(FencedCodeBlock::class.java) { _, _ ->
|
builder.appendFactory(FencedCodeBlock::class.java) { _, _ ->
|
||||||
CopyContentsSpan()
|
CopyContentsSpan()
|
||||||
}
|
}
|
||||||
builder.appendFactory(FencedCodeBlock::class.java) { _, _ ->
|
builder.appendFactory(FencedCodeBlock::class.java) { _, _ ->
|
||||||
CopyIconSpan(context.getDrawable(R.drawable.ic_code_white_24dp)!!)
|
CopyIconSpan(context.getDrawable(R.drawable.ic_code_white_24dp)!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build()
|
.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 updateDrawState(ds: TextPaint) {
|
||||||
override fun onClick(widget: View) {
|
// do not apply link styling
|
||||||
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) {
|
class CopyIconSpan(val icon: Drawable) : LeadingMarginSpan {
|
||||||
// do not apply link styling
|
|
||||||
}
|
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 {
|
override fun drawLeadingMargin(
|
||||||
if (icon.bounds.isEmpty) {
|
c: Canvas,
|
||||||
icon.setBounds(0, 0, icon.intrinsicWidth, icon.intrinsicHeight)
|
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(
|
val save = c.save()
|
||||||
c: Canvas,
|
try {
|
||||||
p: Paint,
|
// horizontal position for icon
|
||||||
x: Int,
|
val w = icon.bounds.width().toFloat()
|
||||||
dir: Int,
|
// minus quarter width as padding
|
||||||
top: Int,
|
val left = layout.width - w - (w / 4F)
|
||||||
baseline: Int,
|
c.translate(left, top.toFloat())
|
||||||
bottom: Int,
|
icon.draw(c)
|
||||||
text: CharSequence,
|
} finally {
|
||||||
start: Int,
|
c.restoreToCount(save)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ import java.util.regex.Pattern
|
|||||||
artifacts = [MarkwonArtifact.CORE],
|
artifacts = [MarkwonArtifact.CORE],
|
||||||
tags = [Tag.parsing]
|
tags = [Tag.parsing]
|
||||||
)
|
)
|
||||||
class ExcludeFromParsingSample : MarkwonTextViewSample() {
|
public class ExcludeFromParsingSample : MarkwonTextViewSample() {
|
||||||
override fun render() {
|
override fun render() {
|
||||||
|
|
||||||
// cannot have continuous markdown between parts (so a node started in one part and ended in other)
|
// cannot have continuous markdown between parts (so a node started in one part and ended in other)
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -5,7 +5,7 @@ import android.text.style.ForegroundColorSpan;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -2,8 +2,8 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.BlockHandlerDef;
|
import io.noties.markwon.BlockHandlerDef;
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,10 +5,10 @@ import android.util.SparseIntArray;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.BulletList;
|
import com.vladsch.flexmark.ast.BulletList;
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.ast.OrderedList;
|
||||||
import org.commonmark.node.OrderedList;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -6,7 +6,7 @@ import android.text.style.UpdateAppearance;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -8,7 +8,7 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ import android.text.style.ForegroundColorSpan;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Paragraph;
|
import com.vladsch.flexmark.ast.Paragraph;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -3,8 +3,8 @@ package io.noties.markwon.app.samples;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
import com.vladsch.flexmark.ast.FencedCodeBlock;
|
||||||
import org.commonmark.node.FencedCodeBlock;
|
import com.vladsch.flexmark.ext.tables.TableBlock;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
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)
|
// NB the `trim` operation on literal (as code will have a new line at the end)
|
||||||
final CharSequence code = visitor.configuration()
|
final CharSequence code = visitor.configuration()
|
||||||
.syntaxHighlight()
|
.syntaxHighlight()
|
||||||
.highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim());
|
.highlight(fencedCodeBlock.getInfo().unescape(), fencedCodeBlock.toAstString(false).trim());
|
||||||
visitor.builder().append(code);
|
visitor.builder().append(code);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package io.noties.markwon.app.samples;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.ast.ThematicBreak;
|
||||||
import org.commonmark.node.ThematicBreak;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.BlockHandlerDef;
|
import io.noties.markwon.BlockHandlerDef;
|
||||||
|
@ -19,7 +19,7 @@ import io.noties.markwon.sample.annotations.Tag
|
|||||||
artifacts = [MarkwonArtifact.CORE, MarkwonArtifact.IMAGE],
|
artifacts = [MarkwonArtifact.CORE, MarkwonArtifact.IMAGE],
|
||||||
tags = [Tag.toast, Tag.hack]
|
tags = [Tag.toast, Tag.hack]
|
||||||
)
|
)
|
||||||
class ToastDynamicContentSample : MarkwonTextViewSample() {
|
public class ToastDynamicContentSample : MarkwonTextViewSample() {
|
||||||
override fun render() {
|
override fun render() {
|
||||||
val md = """
|
val md = """
|
||||||
# Head!
|
# Head!
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package io.noties.markwon.app.samples.basics
|
package io.noties.markwon.app.samples.basics
|
||||||
|
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
import com.vladsch.flexmark.util.ast.Node
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
||||||
import io.noties.markwon.core.CorePlugin
|
import io.noties.markwon.core.CorePlugin
|
||||||
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
||||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo
|
import io.noties.markwon.sample.annotations.MarkwonSampleInfo
|
||||||
import io.noties.markwon.sample.annotations.Tag
|
import io.noties.markwon.sample.annotations.Tag
|
||||||
import org.commonmark.node.Node
|
|
||||||
|
|
||||||
@MarkwonSampleInfo(
|
@MarkwonSampleInfo(
|
||||||
id = "20200626153426",
|
id = "20200626153426",
|
||||||
|
@ -15,8 +15,9 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -26,8 +27,6 @@ import io.noties.markwon.Markwon;
|
|||||||
import io.noties.markwon.MarkwonVisitor;
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
||||||
import io.noties.markwon.image.ImagesPlugin;
|
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.MarkwonArtifact;
|
||||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
||||||
import io.noties.markwon.sample.annotations.Tag;
|
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
|
// NB! without bang
|
||||||
// `\\{` is required (although marked as redundant), without it - runtime crash
|
// `\\{` is required (although marked as redundant), without it - runtime crash
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package io.noties.markwon.app.samples.latex;
|
|||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
||||||
import io.noties.markwon.ext.latex.JLatexMathPlugin;
|
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.MarkwonArtifact;
|
||||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
||||||
import io.noties.markwon.sample.annotations.Tag;
|
import io.noties.markwon.sample.annotations.Tag;
|
||||||
@ -27,7 +26,6 @@ public class LatexOmegaSample extends MarkwonTextViewSample {
|
|||||||
"$$\\Omega$$";
|
"$$\\Omega$$";
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(context)
|
final Markwon markwon = Markwon.builder(context)
|
||||||
.usePlugin(MarkwonInlineParserPlugin.create())
|
|
||||||
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
|
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
|
||||||
builder.inlinesEnabled(true);
|
builder.inlinesEnabled(true);
|
||||||
}))
|
}))
|
||||||
|
@ -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.app.samples.latex.shared.LatexHolder;
|
||||||
import io.noties.markwon.ext.latex.JLatexMathPlugin;
|
import io.noties.markwon.ext.latex.JLatexMathPlugin;
|
||||||
import io.noties.markwon.ext.latex.JLatexMathTheme;
|
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.MarkwonArtifact;
|
||||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
||||||
import io.noties.markwon.sample.annotations.Tag;
|
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 int blockPadding = (int) (16 * context.getResources().getDisplayMetrics().density + 0.5F);
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(context)
|
final Markwon markwon = Markwon.builder(context)
|
||||||
.usePlugin(MarkwonInlineParserPlugin.create())
|
|
||||||
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
|
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
|
||||||
builder.inlinesEnabled(true);
|
builder.inlinesEnabled(true);
|
||||||
builder.theme()
|
builder.theme()
|
||||||
|
@ -13,13 +13,13 @@ import android.widget.RemoteViews;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
|
import com.vladsch.flexmark.ast.BlockQuote;
|
||||||
import org.commonmark.node.BlockQuote;
|
import com.vladsch.flexmark.ast.Code;
|
||||||
import org.commonmark.node.Code;
|
import com.vladsch.flexmark.ast.Emphasis;
|
||||||
import org.commonmark.node.Emphasis;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.StrongEmphasis;
|
||||||
import org.commonmark.node.StrongEmphasis;
|
import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
|||||||
import io.noties.markwon.ext.latex.JLatexMathPlugin;
|
import io.noties.markwon.ext.latex.JLatexMathPlugin;
|
||||||
import io.noties.markwon.ext.tables.TablePlugin;
|
import io.noties.markwon.ext.tables.TablePlugin;
|
||||||
import io.noties.markwon.image.ImagesPlugin;
|
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.MarkwonArtifact;
|
||||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
||||||
import io.noties.markwon.sample.annotations.Tag;
|
import io.noties.markwon.sample.annotations.Tag;
|
||||||
@ -34,7 +33,6 @@ public class TableLatexSample extends MarkwonTextViewSample {
|
|||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(context)
|
final Markwon markwon = Markwon.builder(context)
|
||||||
.usePlugin(MarkwonInlineParserPlugin.create())
|
|
||||||
.usePlugin(ImagesPlugin.create())
|
.usePlugin(ImagesPlugin.create())
|
||||||
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> builder.inlinesEnabled(true)))
|
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> builder.inlinesEnabled(true)))
|
||||||
.usePlugin(TablePlugin.create(context))
|
.usePlugin(TablePlugin.create(context))
|
||||||
|
@ -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'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ import io.noties.markwon.Markwon;
|
|||||||
import io.noties.markwon.MarkwonSpansFactory;
|
import io.noties.markwon.MarkwonSpansFactory;
|
||||||
import io.noties.markwon.SpanFactory;
|
import io.noties.markwon.SpanFactory;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
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.TaskListPlugin;
|
||||||
import io.noties.markwon.ext.tasklist.TaskListSpan;
|
import io.noties.markwon.ext.tasklist.TaskListSpan;
|
||||||
import io.noties.markwon.sample.annotations.MarkwonArtifact;
|
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 static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD;
|
||||||
|
|
||||||
|
import com.vladsch.flexmark.ext.gfm.tasklist.TaskListItem;
|
||||||
|
|
||||||
@MarkwonSampleInfo(
|
@MarkwonSampleInfo(
|
||||||
id = "20200702140901",
|
id = "20200702140901",
|
||||||
title = "GFM task list mutate",
|
title = "GFM task list mutate",
|
||||||
|
29
build.gradle
29
build.gradle
@ -1,13 +1,14 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.4.10'
|
ext.kotlin_version = '1.7.20'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
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 'com.github.ben-manes:gradle-versions-plugin:0.28.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
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) {
|
tasks.withType(Javadoc) {
|
||||||
enabled = false
|
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) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper {
|
|
||||||
gradleVersion '6.1.1'
|
|
||||||
distributionType 'all'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasProperty('local')) {
|
if (hasProperty('local')) {
|
||||||
if (!hasProperty('LOCAL_MAVEN_URL')) {
|
if (!hasProperty('LOCAL_MAVEN_URL')) {
|
||||||
@ -49,10 +57,10 @@ if (hasProperty('local')) {
|
|||||||
ext {
|
ext {
|
||||||
|
|
||||||
config = [
|
config = [
|
||||||
'build-tools' : '29.0.3',
|
'build-tools' : '33.0.1',
|
||||||
'compile-sdk' : 29,
|
'compile-sdk' : 33,
|
||||||
'target-sdk' : 29,
|
'target-sdk' : 33,
|
||||||
'min-sdk' : 16,
|
'min-sdk' : 21,
|
||||||
'push-aar-gradle': 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle'
|
'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-appcompat' : 'androidx.appcompat:appcompat:1.1.0',
|
||||||
'x-cardview' : 'androidx.cardview:cardview:1.0.0',
|
'x-cardview' : 'androidx.cardview:cardview:1.0.0',
|
||||||
'x-fragment' : 'androidx.fragment:fragment: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' : "com.atlassian.commonmark:commonmark:$commonMarkVersion",
|
||||||
'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion",
|
'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion",
|
||||||
'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion",
|
'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion",
|
||||||
|
@ -5,19 +5,21 @@ org.gradle.configureondemand=true
|
|||||||
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=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
|
GROUP=io.noties.markwon
|
||||||
POM_DESCRIPTION=Markwon markdown for Android
|
POM_DESCRIPTION=Markwon markdown for Android
|
||||||
POM_URL=https://github.com/noties/Markwon
|
POM_URL=https://github.com/cpacm/Markwon
|
||||||
POM_SCM_URL=https://github.com/noties/Markwon
|
POM_SCM_URL=https://github.com/cpacm/Markwon
|
||||||
POM_SCM_CONNECTION=scm:git:git://github.com/noties/Markwon.git
|
POM_SCM_CONNECTION=scm:git:git://github.com/cpacm/Markwon.git
|
||||||
POM_SCM_DEV_CONNECTION=scm:git:git://github.com/noties/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_NAME=The Apache Software License, Version 2.0
|
||||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
POM_LICENCE_DIST=repo
|
POM_LICENCE_DIST=repo
|
||||||
POM_DEVELOPER_ID=noties
|
POM_DEVELOPER_ID=cpacm
|
||||||
POM_DEVELOPER_NAME=Dimitry Ivanov
|
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
|
20
gradle/publishAllToMavenLocal.sh
Executable file
20
gradle/publishAllToMavenLocal.sh
Executable file
@ -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
|
240
gradle/publishMaven.gradle
Normal file
240
gradle/publishMaven.gradle
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -17,7 +17,7 @@ dependencies {
|
|||||||
|
|
||||||
deps.with {
|
deps.with {
|
||||||
api it['x-annotations']
|
api it['x-annotations']
|
||||||
api it['commonmark']
|
api it['flexmark']
|
||||||
|
|
||||||
// @since 4.1.0 to allow PrecomputedTextSetterCompat
|
// @since 4.1.0 to allow PrecomputedTextSetterCompat
|
||||||
// note that this dependency must be added on a client side explicitly
|
// note that this dependency must be added on a client side explicitly
|
||||||
@ -36,3 +36,5 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -5,8 +5,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
import org.commonmark.parser.Parser;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.core.MarkwonTheme;
|
import io.noties.markwon.core.MarkwonTheme;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
|
@ -7,7 +7,7 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.parser.Parser;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -8,8 +8,14 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -74,7 +80,7 @@ class MarkwonImpl extends Markwon {
|
|||||||
// @since 4.1.1 obtain visitor via factory
|
// @since 4.1.1 obtain visitor via factory
|
||||||
final MarkwonVisitor visitor = visitorFactory.create();
|
final MarkwonVisitor visitor = visitorFactory.create();
|
||||||
|
|
||||||
node.accept(visitor);
|
visitor.visit(node);
|
||||||
|
|
||||||
for (MarkwonPlugin plugin : plugins) {
|
for (MarkwonPlugin plugin : plugins) {
|
||||||
plugin.afterRender(node, visitor);
|
plugin.afterRender(node, visitor);
|
||||||
|
@ -5,8 +5,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
import org.commonmark.parser.Parser;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.core.CorePlugin;
|
import io.noties.markwon.core.CorePlugin;
|
||||||
import io.noties.markwon.core.MarkwonTheme;
|
import io.noties.markwon.core.MarkwonTheme;
|
||||||
|
@ -2,8 +2,8 @@ package io.noties.markwon;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.LinkReferenceDefinition;
|
import com.vladsch.flexmark.ast.Reference;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -51,7 +51,7 @@ public abstract class MarkwonReducer {
|
|||||||
while (node != null) {
|
while (node != null) {
|
||||||
// @since 4.5.0 do not include LinkReferenceDefinition node (would result
|
// @since 4.5.0 do not include LinkReferenceDefinition node (would result
|
||||||
// in empty textView if rendered in recycler-view)
|
// in empty textView if rendered in recycler-view)
|
||||||
if (!(node instanceof LinkReferenceDefinition)) {
|
if (!(node instanceof Reference)) {
|
||||||
list.add(node);
|
list.add(node);
|
||||||
}
|
}
|
||||||
temp = node.getNext();
|
temp = node.getNext();
|
||||||
|
@ -3,7 +3,7 @@ package io.noties.markwon;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.
|
* Class that controls what spans are used for certain Nodes.
|
||||||
|
@ -3,7 +3,7 @@ package io.noties.markwon;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -3,8 +3,12 @@ package io.noties.markwon;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
import org.commonmark.node.Visitor;
|
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
|
* Configurable visitor of parsed markdown. Allows visiting certain (registered) nodes without
|
||||||
@ -14,7 +18,7 @@ import org.commonmark.node.Visitor;
|
|||||||
* @see MarkwonPlugin#configureVisitor(Builder)
|
* @see MarkwonPlugin#configureVisitor(Builder)
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
public interface MarkwonVisitor extends Visitor {
|
public interface MarkwonVisitor extends BlockVisitor, InlineVisitor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Builder#on(Class, NodeVisitor)
|
* @see Builder#on(Class, NodeVisitor)
|
||||||
@ -69,13 +73,6 @@ public interface MarkwonVisitor extends Visitor {
|
|||||||
@NonNull
|
@NonNull
|
||||||
SpannableBuilder builder();
|
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.
|
* Executes a check if there is further content available.
|
||||||
*
|
*
|
||||||
@ -166,4 +163,8 @@ public interface MarkwonVisitor extends Visitor {
|
|||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void blockEnd(@NonNull Node node);
|
void blockEnd(@NonNull Node node);
|
||||||
|
|
||||||
|
void visit(@NotNull Node node);
|
||||||
|
|
||||||
|
void visitChildren(Node node);
|
||||||
}
|
}
|
||||||
|
@ -3,30 +3,39 @@ package io.noties.markwon;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.BlockQuote;
|
import com.vladsch.flexmark.ast.AutoLink;
|
||||||
import org.commonmark.node.BulletList;
|
import com.vladsch.flexmark.ast.BlockQuote;
|
||||||
import org.commonmark.node.Code;
|
import com.vladsch.flexmark.ast.BulletList;
|
||||||
import org.commonmark.node.CustomBlock;
|
import com.vladsch.flexmark.ast.BulletListItem;
|
||||||
import org.commonmark.node.CustomNode;
|
import com.vladsch.flexmark.ast.Code;
|
||||||
import org.commonmark.node.Document;
|
import com.vladsch.flexmark.ast.Emphasis;
|
||||||
import org.commonmark.node.Emphasis;
|
import com.vladsch.flexmark.ast.FencedCodeBlock;
|
||||||
import org.commonmark.node.FencedCodeBlock;
|
import com.vladsch.flexmark.ast.HardLineBreak;
|
||||||
import org.commonmark.node.HardLineBreak;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.HtmlBlock;
|
||||||
import org.commonmark.node.HtmlBlock;
|
import com.vladsch.flexmark.ast.HtmlCommentBlock;
|
||||||
import org.commonmark.node.HtmlInline;
|
import com.vladsch.flexmark.ast.HtmlEntity;
|
||||||
import org.commonmark.node.Image;
|
import com.vladsch.flexmark.ast.HtmlInline;
|
||||||
import org.commonmark.node.IndentedCodeBlock;
|
import com.vladsch.flexmark.ast.HtmlInlineComment;
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.Image;
|
||||||
import org.commonmark.node.LinkReferenceDefinition;
|
import com.vladsch.flexmark.ast.ImageRef;
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.IndentedCodeBlock;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
import org.commonmark.node.OrderedList;
|
import com.vladsch.flexmark.ast.LinkRef;
|
||||||
import org.commonmark.node.Paragraph;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
import org.commonmark.node.SoftLineBreak;
|
import com.vladsch.flexmark.ast.MailLink;
|
||||||
import org.commonmark.node.StrongEmphasis;
|
import com.vladsch.flexmark.ast.OrderedList;
|
||||||
import org.commonmark.node.Text;
|
import com.vladsch.flexmark.ast.OrderedListItem;
|
||||||
import org.commonmark.node.ThematicBreak;
|
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.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -35,7 +44,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
class MarkwonVisitorImpl implements MarkwonVisitor {
|
class MarkwonVisitorImpl extends com.vladsch.flexmark.util.ast.NodeVisitor implements MarkwonVisitor {
|
||||||
|
|
||||||
private final MarkwonConfiguration configuration;
|
private final MarkwonConfiguration configuration;
|
||||||
|
|
||||||
@ -59,124 +68,169 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
|
|||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
this.blockHandler = blockHandler;
|
this.blockHandler = blockHandler;
|
||||||
|
|
||||||
|
addHandlers(BlockVisitorExt.VISIT_HANDLERS(this));
|
||||||
|
addHandlers(InlineVisitorExt.VISIT_HANDLERS(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BlockQuote blockQuote) {
|
public void visit(BlockQuote blockQuote) {
|
||||||
visit((Node) blockQuote);
|
visitImpl(blockQuote);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BulletList bulletList) {
|
public void visit(BulletList bulletList) {
|
||||||
visit((Node) bulletList);
|
visitImpl(bulletList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(AutoLink autoLink) {
|
||||||
|
visitImpl(autoLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Code code) {
|
public void visit(Code code) {
|
||||||
visit((Node) code);
|
visitImpl(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Document document) {
|
public void visit(Document document) {
|
||||||
visit((Node) document);
|
visitImpl(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Emphasis emphasis) {
|
public void visit(Emphasis emphasis) {
|
||||||
visit((Node) emphasis);
|
visitImpl(emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(FencedCodeBlock fencedCodeBlock) {
|
public void visit(FencedCodeBlock fencedCodeBlock) {
|
||||||
visit((Node) fencedCodeBlock);
|
visitImpl(fencedCodeBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visit(HardLineBreak hardLineBreak) {
|
||||||
|
visitImpl(hardLineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(HardLineBreak hardLineBreak) {
|
public void visit(HtmlEntity node) {
|
||||||
visit((Node) hardLineBreak);
|
visitImpl(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Heading heading) {
|
public void visit(Heading heading) {
|
||||||
visit((Node) heading);
|
visitImpl(heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ThematicBreak thematicBreak) {
|
public void visit(ThematicBreak thematicBreak) {
|
||||||
visit((Node) thematicBreak);
|
visitImpl(thematicBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(HtmlInline htmlInline) {
|
public void visit(HtmlInline htmlInline) {
|
||||||
visit((Node) htmlInline);
|
visitImpl(htmlInline);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(HtmlInlineComment node) {
|
||||||
|
visitImpl(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(HtmlBlock htmlBlock) {
|
public void visit(HtmlBlock htmlBlock) {
|
||||||
visit((Node) htmlBlock);
|
visitImpl(htmlBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(HtmlCommentBlock node) {
|
||||||
|
visitImpl(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void visit(Image image) {
|
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
|
@Override
|
||||||
public void visit(IndentedCodeBlock indentedCodeBlock) {
|
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
|
@Override
|
||||||
public void visit(Link link) {
|
public void visit(Link link) {
|
||||||
visit((Node) link);
|
visitImpl(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ListItem listItem) {
|
public void visit(LinkRef node) {
|
||||||
visit((Node) listItem);
|
visitImpl(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MailLink node) {
|
||||||
|
visitImpl(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(OrderedList orderedList) {
|
public void visit(OrderedList orderedList) {
|
||||||
visit((Node) orderedList);
|
visitImpl(orderedList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Paragraph paragraph) {
|
public void visit(Paragraph paragraph) {
|
||||||
visit((Node) paragraph);
|
visitImpl(paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Reference node) {
|
||||||
|
// Reference 需要和 ImageRef,LinkRef配合使用,因为这些都是指向 Reference,再由Reference指向真正的地址
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(SoftLineBreak softLineBreak) {
|
public void visit(SoftLineBreak softLineBreak) {
|
||||||
visit((Node) softLineBreak);
|
visitImpl(softLineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(StrongEmphasis strongEmphasis) {
|
public void visit(StrongEmphasis strongEmphasis) {
|
||||||
visit((Node) strongEmphasis);
|
visitImpl(strongEmphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Text text) {
|
public void visit(Text text) {
|
||||||
visit((Node) text);
|
visitImpl(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void visitImpl(@NonNull Node node) {
|
||||||
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) {
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final NodeVisitor<Node> nodeVisitor = (NodeVisitor<Node>) nodes.get(node.getClass());
|
final NodeVisitor<Node> nodeVisitor = (NodeVisitor<Node>) nodes.get(node.getClass());
|
||||||
if (nodeVisitor != null) {
|
if (nodeVisitor != null) {
|
||||||
@ -204,17 +258,6 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
|
|||||||
return builder;
|
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
|
@Override
|
||||||
public boolean hasNext(@NonNull Node node) {
|
public boolean hasNext(@NonNull Node node) {
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.SoftLineBreak;
|
import com.vladsch.flexmark.ast.SoftLineBreak;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
|
@ -9,27 +9,27 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import org.commonmark.node.Block;
|
import com.vladsch.flexmark.ast.BlockQuote;
|
||||||
import org.commonmark.node.BlockQuote;
|
import com.vladsch.flexmark.ast.BulletList;
|
||||||
import org.commonmark.node.BulletList;
|
import com.vladsch.flexmark.ast.Code;
|
||||||
import org.commonmark.node.Code;
|
import com.vladsch.flexmark.ast.Emphasis;
|
||||||
import org.commonmark.node.Emphasis;
|
import com.vladsch.flexmark.ast.FencedCodeBlock;
|
||||||
import org.commonmark.node.FencedCodeBlock;
|
import com.vladsch.flexmark.ast.HardLineBreak;
|
||||||
import org.commonmark.node.HardLineBreak;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.HtmlBlock;
|
||||||
import org.commonmark.node.HtmlBlock;
|
import com.vladsch.flexmark.ast.Image;
|
||||||
import org.commonmark.node.Image;
|
import com.vladsch.flexmark.ast.IndentedCodeBlock;
|
||||||
import org.commonmark.node.IndentedCodeBlock;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.ListBlock;
|
||||||
import org.commonmark.node.ListBlock;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.OrderedList;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.ast.Paragraph;
|
||||||
import org.commonmark.node.OrderedList;
|
import com.vladsch.flexmark.ast.SoftLineBreak;
|
||||||
import org.commonmark.node.Paragraph;
|
import com.vladsch.flexmark.ast.StrongEmphasis;
|
||||||
import org.commonmark.node.SoftLineBreak;
|
import com.vladsch.flexmark.ast.Text;
|
||||||
import org.commonmark.node.StrongEmphasis;
|
import com.vladsch.flexmark.ast.ThematicBreak;
|
||||||
import org.commonmark.node.Text;
|
import com.vladsch.flexmark.util.ast.Block;
|
||||||
import org.commonmark.node.ThematicBreak;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -212,8 +212,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
||||||
|
|
||||||
final String literal = text.getLiteral();
|
String literal = text.toAstString(true);
|
||||||
|
|
||||||
visitor.builder().append(literal);
|
visitor.builder().append(literal);
|
||||||
|
|
||||||
// @since 4.0.0
|
// @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
|
// unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted
|
||||||
visitor.builder()
|
visitor.builder()
|
||||||
.append('\u00a0')
|
.append('\u00a0')
|
||||||
.append(code.getLiteral())
|
.append(code.toAstString(true))
|
||||||
.append('\u00a0');
|
.append('\u00a0');
|
||||||
|
|
||||||
visitor.setSpansForNodeOptional(code, length);
|
visitor.setSpansForNodeOptional(code, length);
|
||||||
@ -290,7 +289,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
builder.on(FencedCodeBlock.class, new MarkwonVisitor.NodeVisitor<FencedCodeBlock>() {
|
builder.on(FencedCodeBlock.class, new MarkwonVisitor.NodeVisitor<FencedCodeBlock>() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull FencedCodeBlock fencedCodeBlock) {
|
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<IndentedCodeBlock>() {
|
builder.on(IndentedCodeBlock.class, new MarkwonVisitor.NodeVisitor<IndentedCodeBlock>() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull IndentedCodeBlock indentedCodeBlock) {
|
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
|
final String destination = configuration
|
||||||
.imageDestinationProcessor()
|
.imageDestinationProcessor()
|
||||||
.process(image.getDestination());
|
.process(image.getUrl().unescape());
|
||||||
|
|
||||||
final RenderProps props = visitor.renderProps();
|
final RenderProps props = visitor.renderProps();
|
||||||
|
|
||||||
@ -538,7 +537,7 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
final int length = visitor.length();
|
final int length = visitor.length();
|
||||||
visitor.visitChildren(link);
|
visitor.visitChildren(link);
|
||||||
|
|
||||||
final String destination = link.getDestination();
|
final String destination = link.getUrl().unescape();
|
||||||
|
|
||||||
CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination);
|
CoreProps.LINK_DESTINATION.set(visitor.renderProps(), destination);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.core;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.MarkwonVisitor;
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package io.noties.markwon.syntax;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public interface SyntaxHighlight {
|
public interface SyntaxHighlight {
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import androidx.annotation.CheckResult;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
import org.commonmark.node.Visitor;
|
import com.vladsch.flexmark.util.ast.Visitor;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -70,7 +70,8 @@ public abstract class DumpNodes {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
node.accept(visitor);
|
visitor.visit(node);
|
||||||
|
//node.accept(visitor);
|
||||||
return builder.toString();
|
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
|
// 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 after visiting it. So get the next node before visiting.
|
||||||
Node next = node.getNext();
|
Node next = node.getNext();
|
||||||
node.accept(visitor);
|
visitor.visit(node);
|
||||||
node = next;
|
node = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.utils;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
|
@ -29,3 +29,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -16,7 +16,6 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
api project(':markwon-core')
|
api project(':markwon-core')
|
||||||
api project(':markwon-inline-parser')
|
|
||||||
|
|
||||||
api deps['jlatexmath-android']
|
api deps['jlatexmath-android']
|
||||||
|
|
||||||
@ -28,3 +27,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,8 +14,11 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.annotation.Px;
|
import androidx.annotation.Px;
|
||||||
import androidx.annotation.VisibleForTesting;
|
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.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
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.AsyncDrawableSpan;
|
||||||
import io.noties.markwon.image.DrawableUtils;
|
import io.noties.markwon.image.DrawableUtils;
|
||||||
import io.noties.markwon.image.ImageSizeResolver;
|
import io.noties.markwon.image.ImageSizeResolver;
|
||||||
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
|
|
||||||
import ru.noties.jlatexmath.JLatexMathDrawable;
|
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,8 +119,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
// @since 4.3.0
|
// @since 4.3.0
|
||||||
final boolean blocksEnabled;
|
final boolean blocksEnabled;
|
||||||
final boolean blocksLegacy;
|
|
||||||
final boolean inlinesEnabled;
|
|
||||||
|
|
||||||
// @since 4.3.0
|
// @since 4.3.0
|
||||||
final ErrorHandler errorHandler;
|
final ErrorHandler errorHandler;
|
||||||
@ -128,8 +128,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
Config(@NonNull Builder builder) {
|
Config(@NonNull Builder builder) {
|
||||||
this.theme = builder.theme.build();
|
this.theme = builder.theme.build();
|
||||||
this.blocksEnabled = builder.blocksEnabled;
|
this.blocksEnabled = builder.blocksEnabled;
|
||||||
this.blocksLegacy = builder.blocksLegacy;
|
|
||||||
this.inlinesEnabled = builder.inlinesEnabled;
|
|
||||||
this.errorHandler = builder.errorHandler;
|
this.errorHandler = builder.errorHandler;
|
||||||
// @since 4.0.0
|
// @since 4.0.0
|
||||||
ExecutorService executorService = builder.executorService;
|
ExecutorService executorService = builder.executorService;
|
||||||
@ -155,59 +153,34 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
this.inlineImageSizeResolver = new InlineImageSizeResolver();
|
this.inlineImageSizeResolver = new InlineImageSizeResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configure(@NonNull Registry registry) {
|
|
||||||
if (config.inlinesEnabled) {
|
|
||||||
registry.require(MarkwonInlineParserPlugin.class)
|
|
||||||
.factoryBuilder()
|
|
||||||
.addInlineProcessor(new JLatexMathInlineProcessor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureParser(@NonNull Parser.Builder builder) {
|
public void configureParser(@NonNull Parser.Builder builder) {
|
||||||
// @since 4.3.0
|
builder.extensions(Collections.singleton(GitLabExtension.create()));
|
||||||
if (config.blocksEnabled) {
|
if (config.blocksEnabled) {
|
||||||
if (config.blocksLegacy) {
|
builder.set(GitLabExtension.RENDER_BLOCK_MATH, true);
|
||||||
builder.customBlockParserFactory(new JLatexMathBlockParserLegacy.Factory());
|
|
||||||
} else {
|
|
||||||
builder.customBlockParserFactory(new JLatexMathBlockParser.Factory());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
addBlockVisitor(builder);
|
builder.on(GitLabInlineMath.class, new MarkwonVisitor.NodeVisitor<GitLabInlineMath>() {
|
||||||
addInlineVisitor(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addBlockVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
|
||||||
if (!config.blocksEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.on(JLatexMathBlock.class, new MarkwonVisitor.NodeVisitor<JLatexMathBlock>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull JLatexMathBlock jLatexMathBlock) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull GitLabInlineMath gitLabInlineMath) {
|
||||||
|
if (config.blocksEnabled) visitor.blockStart(gitLabInlineMath);
|
||||||
visitor.blockStart(jLatexMathBlock);
|
final String tex = gitLabInlineMath.getText().unescape();
|
||||||
|
|
||||||
final String latex = jLatexMathBlock.latex();
|
|
||||||
|
|
||||||
final int length = visitor.length();
|
final int length = visitor.length();
|
||||||
|
|
||||||
// @since 4.0.2 we cannot append _raw_ latex as a placeholder-text,
|
// @since 4.0.2 we cannot append _raw_ latex as a placeholder-text,
|
||||||
// because Android will draw formula for each line of text, thus
|
// because Android will draw formula for each line of text, thus
|
||||||
// leading to formula duplicated (drawn on each line of text)
|
// 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 MarkwonConfiguration configuration = visitor.configuration();
|
||||||
|
|
||||||
final AsyncDrawableSpan span = new JLatexAsyncDrawableSpan(
|
final AsyncDrawableSpan span = new JLatexAsyncDrawableSpan(
|
||||||
configuration.theme(),
|
configuration.theme(),
|
||||||
new JLatextAsyncDrawable(
|
new JLatextAsyncDrawable(
|
||||||
latex,
|
tex,
|
||||||
jLatextAsyncDrawableLoader,
|
jLatextAsyncDrawableLoader,
|
||||||
jLatexBlockImageSizeResolver,
|
jLatexBlockImageSizeResolver,
|
||||||
null,
|
null,
|
||||||
@ -217,43 +190,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
visitor.setSpans(length, span);
|
visitor.setSpans(length, span);
|
||||||
|
|
||||||
visitor.blockEnd(jLatexMathBlock);
|
if (config.blocksEnabled) visitor.blockEnd(gitLabInlineMath);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInlineVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
|
||||||
|
|
||||||
if (!config.inlinesEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.on(JLatexMathNode.class, new MarkwonVisitor.NodeVisitor<JLatexMathNode>() {
|
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -310,16 +247,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
return this;
|
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.
|
* @param inlinesEnabled indicates if inline parsing should be enabled.
|
||||||
* NB, this requires `MarkwonInlineParserPlugin` to be used when creating `MarkwonInstance`
|
* NB, this requires `MarkwonInlineParserPlugin` to be used when creating `MarkwonInstance`
|
||||||
|
@ -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 + '\'';
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<MarkwonVisitor.NodeVisitor> 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<String> 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<BlockParserFactory> 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<Class> 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<InlineProcessor> 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<BlockParserFactory> 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<Class> captor = ArgumentCaptor.forClass(Class.class);
|
|
||||||
verify(builder, times(2)).on(captor.capture(), any(MarkwonVisitor.NodeVisitor.class));
|
|
||||||
|
|
||||||
final List<Class> nodes = captor.getAllValues();
|
|
||||||
assertEquals(2, nodes.size());
|
|
||||||
assertTrue(nodes.toString(), nodes.contains(JLatexMathNode.class));
|
|
||||||
assertTrue(nodes.toString(), nodes.contains(JLatexMathBlock.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,8 +18,6 @@ dependencies {
|
|||||||
api project(':markwon-core')
|
api project(':markwon-core')
|
||||||
|
|
||||||
deps.with {
|
deps.with {
|
||||||
api it['commonmark-strikethrough']
|
|
||||||
|
|
||||||
// NB! ix-java dependency to be used in tests
|
// NB! ix-java dependency to be used in tests
|
||||||
testImplementation it['ix-java']
|
testImplementation it['ix-java']
|
||||||
}
|
}
|
||||||
@ -33,3 +31,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -4,9 +4,9 @@ import android.text.style.StrikethroughSpan;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
|
import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough;
|
||||||
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
|
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
|
||||||
import org.commonmark.parser.Parser;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ import io.noties.markwon.RenderProps;
|
|||||||
import io.noties.markwon.SpanFactory;
|
import io.noties.markwon.SpanFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to add strikethrough markdown feature. This plugin will extend commonmark-java.Parser
|
* Plugin to add strikethrough markdown feature. This plugin will extend flexmark.Parser
|
||||||
* with strikethrough extension, add SpanFactory and register commonmark-java.Strikethrough node
|
* with strikethrough extension, add SpanFactory and register flexmark.Strikethrough node
|
||||||
* visitor
|
* visitor
|
||||||
*
|
*
|
||||||
* @see #create()
|
* @see #create()
|
||||||
|
@ -11,15 +11,16 @@ android {
|
|||||||
versionCode 1
|
versionCode 1
|
||||||
versionName version
|
versionName version
|
||||||
}
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
api project(':markwon-core')
|
api project(':markwon-core')
|
||||||
|
|
||||||
deps.with {
|
|
||||||
api it['commonmark-table']
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -5,12 +5,16 @@ import android.text.Spanned;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
import com.vladsch.flexmark.ast.BlockQuote;
|
||||||
import org.commonmark.ext.gfm.tables.TableCell;
|
import com.vladsch.flexmark.ast.util.BlockVisitorExt;
|
||||||
import org.commonmark.ext.gfm.tables.TableHead;
|
import com.vladsch.flexmark.ast.util.InlineVisitorExt;
|
||||||
import org.commonmark.ext.gfm.tables.TableRow;
|
import com.vladsch.flexmark.ext.tables.TableBlock;
|
||||||
import org.commonmark.node.AbstractVisitor;
|
import com.vladsch.flexmark.ext.tables.TableCell;
|
||||||
import org.commonmark.node.CustomNode;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -40,7 +44,8 @@ public class Table {
|
|||||||
final Table table;
|
final Table table;
|
||||||
|
|
||||||
final ParseVisitor visitor = new ParseVisitor(markwon);
|
final ParseVisitor visitor = new ParseVisitor(markwon);
|
||||||
tableBlock.accept(visitor);
|
visitor.visit(tableBlock);
|
||||||
|
|
||||||
final List<Row> rows = visitor.rows();
|
final List<Row> rows = visitor.rows();
|
||||||
|
|
||||||
if (rows == null) {
|
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;
|
private final Markwon markwon;
|
||||||
|
|
||||||
@ -146,6 +151,7 @@ public class Table {
|
|||||||
|
|
||||||
ParseVisitor(@NonNull Markwon markwon) {
|
ParseVisitor(@NonNull Markwon markwon) {
|
||||||
this.markwon = markwon;
|
this.markwon = markwon;
|
||||||
|
addHandlers(TableVisitor.VISIT_HANDLERS(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -154,44 +160,47 @@ public class Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(CustomNode customNode) {
|
public void visit(TableCell cell) {
|
||||||
|
if (pendingRow == null) {
|
||||||
if (customNode instanceof TableCell) {
|
pendingRow = new ArrayList<>(2);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customNode instanceof TableHead
|
pendingRow.add(new Table.Column(alignment(cell.getAlignment()), markwon.render(cell)));
|
||||||
|| customNode instanceof TableRow) {
|
pendingRowIsHeader = cell.isHeader();
|
||||||
|
}
|
||||||
|
|
||||||
visitChildren(customNode);
|
@Override
|
||||||
|
public void visit(TableHead head) {
|
||||||
|
visitChildren(head);
|
||||||
|
|
||||||
// this can happen, ignore such row
|
// this can happen, ignore such row
|
||||||
if (pendingRow != null && pendingRow.size() > 0) {
|
if (pendingRow != null && pendingRow.size() > 0) {
|
||||||
|
if (rows == null) {
|
||||||
if (rows == null) {
|
rows = new ArrayList<>(2);
|
||||||
rows = new ArrayList<>(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.add(new Table.Row(pendingRowIsHeader, pendingRow));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingRow = null;
|
rows.add(new Table.Row(pendingRowIsHeader, pendingRow));
|
||||||
pendingRowIsHeader = false;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@NonNull
|
||||||
@ -206,5 +215,6 @@ public class Table {
|
|||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
import com.vladsch.flexmark.ext.tables.TableBlock;
|
||||||
import org.commonmark.ext.gfm.tables.TableBody;
|
import com.vladsch.flexmark.ext.tables.TableBody;
|
||||||
import org.commonmark.ext.gfm.tables.TableCell;
|
import com.vladsch.flexmark.ext.tables.TableCell;
|
||||||
import org.commonmark.ext.gfm.tables.TableHead;
|
import com.vladsch.flexmark.ext.tables.TableHead;
|
||||||
import org.commonmark.ext.gfm.tables.TableRow;
|
import com.vladsch.flexmark.ext.tables.TableRow;
|
||||||
import org.commonmark.ext.gfm.tables.TablesExtension;
|
import com.vladsch.flexmark.ext.tables.TablesExtension;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
import org.commonmark.parser.Parser;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @author cpacm 2023/4/4
|
||||||
|
*/
|
||||||
|
public interface TableVisitor {
|
||||||
|
|
||||||
|
void visit(TableCell node);
|
||||||
|
|
||||||
|
void visit(TableHead head);
|
||||||
|
|
||||||
|
void visit(TableRow row);
|
||||||
|
|
||||||
|
public static <V extends TableVisitor> 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -28,3 +28,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,12 +9,15 @@ import androidx.annotation.AttrRes;
|
|||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
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.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.MarkwonSpansFactory;
|
import io.noties.markwon.MarkwonSpansFactory;
|
||||||
import io.noties.markwon.MarkwonVisitor;
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
import io.noties.markwon.core.SimpleBlockNodeVisitor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
@ -62,20 +65,19 @@ public class TaskListPlugin extends AbstractMarkwonPlugin {
|
|||||||
this.drawable = drawable;
|
this.drawable = drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureParser(@NonNull Parser.Builder builder) {
|
|
||||||
builder.postProcessor(new TaskListPostProcessor());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
|
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
|
||||||
builder.setFactory(TaskListItem.class, new TaskListSpanFactory(drawable));
|
builder.setFactory(TaskListItem.class, new TaskListSpanFactory(drawable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureParser(@NonNull Parser.Builder builder) {
|
||||||
|
builder.extensions(Collections.singleton(TaskListExtension.create()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder
|
builder.on(TaskListItem.class, new MarkwonVisitor.NodeVisitor<TaskListItem>() {
|
||||||
.on(TaskListItem.class, new MarkwonVisitor.NodeVisitor<TaskListItem>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull TaskListItem taskListItem) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull TaskListItem taskListItem) {
|
||||||
|
|
||||||
@ -83,7 +85,7 @@ public class TaskListPlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
visitor.visitChildren(taskListItem);
|
visitor.visitChildren(taskListItem);
|
||||||
|
|
||||||
TaskListProps.DONE.set(visitor.renderProps(), taskListItem.isDone());
|
TaskListProps.DONE.set(visitor.renderProps(), taskListItem.isItemDoneMarker());
|
||||||
|
|
||||||
visitor.setSpansForNode(taskListItem, length);
|
visitor.setSpansForNode(taskListItem, length);
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,7 +22,6 @@ dependencies {
|
|||||||
// we will try to obtain a SpanFactory for a Strikethrough node and use
|
// 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
|
// it to be consistent with markdown (please note that we do not use markwon plugin
|
||||||
// for that in case if different implementation is used)
|
// for that in case if different implementation is used)
|
||||||
compileOnly it['commonmark-strikethrough']
|
|
||||||
|
|
||||||
testImplementation it['ix-java']
|
testImplementation it['ix-java']
|
||||||
}
|
}
|
||||||
@ -34,3 +33,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -3,9 +3,9 @@ package io.noties.markwon.html;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.HtmlBlock;
|
import com.vladsch.flexmark.ast.HtmlBlock;
|
||||||
import org.commonmark.node.HtmlInline;
|
import com.vladsch.flexmark.ast.HtmlInline;
|
||||||
import org.commonmark.node.Node;
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.MarkwonConfiguration;
|
import io.noties.markwon.MarkwonConfiguration;
|
||||||
@ -161,13 +161,13 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
|
|||||||
.on(HtmlBlock.class, new MarkwonVisitor.NodeVisitor<HtmlBlock>() {
|
.on(HtmlBlock.class, new MarkwonVisitor.NodeVisitor<HtmlBlock>() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlBlock htmlBlock) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlBlock htmlBlock) {
|
||||||
visitHtml(visitor, htmlBlock.getLiteral());
|
visitHtml(visitor, htmlBlock.toAstString(false));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on(HtmlInline.class, new MarkwonVisitor.NodeVisitor<HtmlInline>() {
|
.on(HtmlInline.class, new MarkwonVisitor.NodeVisitor<HtmlInline>() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlInline htmlInline) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull HtmlInline htmlInline) {
|
||||||
visitHtml(visitor, htmlInline.getLiteral());
|
visitHtml(visitor, htmlInline.toAstString(false));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.html.jsoup.nodes;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.internal.util.Html5Entities;
|
import com.vladsch.flexmark.util.sequence.Html5Entities;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.html.tag;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.BlockQuote;
|
import com.vladsch.flexmark.ast.BlockQuote;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -3,7 +3,7 @@ package io.noties.markwon.html.tag;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Emphasis;
|
import com.vladsch.flexmark.ast.Emphasis;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -3,7 +3,7 @@ package io.noties.markwon.html.tag;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Heading;
|
import com.vladsch.flexmark.ast.Heading;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -5,7 +5,7 @@ import android.text.TextUtils;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Image;
|
import com.vladsch.flexmark.ast.Image;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -5,7 +5,7 @@ import android.text.TextUtils;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Link;
|
import com.vladsch.flexmark.ast.Link;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -2,7 +2,7 @@ package io.noties.markwon.html.tag;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.commonmark.node.ListItem;
|
import com.vladsch.flexmark.ast.ListItem;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -5,6 +5,8 @@ import android.text.style.StrikethroughSpan;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.vladsch.flexmark.ext.gfm.strikethrough.Strikethrough;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ public class StrikeHandler extends TagHandler {
|
|||||||
private static Object getMarkdownSpans(@NonNull MarkwonVisitor visitor) {
|
private static Object getMarkdownSpans(@NonNull MarkwonVisitor visitor) {
|
||||||
final MarkwonConfiguration configuration = visitor.configuration();
|
final MarkwonConfiguration configuration = visitor.configuration();
|
||||||
final SpanFactory spanFactory = configuration.spansFactory()
|
final SpanFactory spanFactory = configuration.spansFactory()
|
||||||
.get(org.commonmark.ext.gfm.strikethrough.Strikethrough.class);
|
.get(Strikethrough.class);
|
||||||
if (spanFactory == null) {
|
if (spanFactory == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.noties.markwon.html.tag;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.StrongEmphasis;
|
import com.vladsch.flexmark.ast.StrongEmphasis;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -23,3 +23,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -8,7 +8,7 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.commonmark.node.Image;
|
import com.vladsch.flexmark.ast.Image;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -19,3 +19,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -14,8 +14,7 @@ import com.bumptech.glide.RequestManager;
|
|||||||
import com.bumptech.glide.request.target.CustomTarget;
|
import com.bumptech.glide.request.target.CustomTarget;
|
||||||
import com.bumptech.glide.request.target.Target;
|
import com.bumptech.glide.request.target.Target;
|
||||||
import com.bumptech.glide.request.transition.Transition;
|
import com.bumptech.glide.request.transition.Transition;
|
||||||
|
import com.vladsch.flexmark.ast.Image;
|
||||||
import org.commonmark.node.Image;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -19,3 +19,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
@ -14,8 +14,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
import com.squareup.picasso.RequestCreator;
|
import com.squareup.picasso.RequestCreator;
|
||||||
import com.squareup.picasso.Target;
|
import com.squareup.picasso.Target;
|
||||||
|
import com.vladsch.flexmark.ast.Image;
|
||||||
import org.commonmark.node.Image;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -35,3 +35,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerArtifact(this)
|
registerArtifact(this)
|
||||||
|
apply from: "${rootProject.projectDir}/gradle/publishMaven.gradle"
|
||||||
|
@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import org.commonmark.node.Image;
|
import com.vladsch.flexmark.ast.Image;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user