From 652f4275636be7a2824e81eb7788487d3c19def9 Mon Sep 17 00:00:00 2001 From: Sam Shih Date: Tue, 8 Dec 2020 11:52:13 -0800 Subject: [PATCH] [0.2.0] Fix: inline code rule shouldn't consume empty blocks (#26) Empty inline blocks should behave as if they were just text. Additionally add updates to sample app + test to handle escaping ticks Bump the version to 2.2.0 for the change to `createSimpleMarkdownRules` API Context: This enables text such as \```sample text``` to be rendered as: ```sample text``` --- .../discord/simpleast/sample/MainActivity.kt | 8 ++- simpleast-core/build.gradle | 2 +- .../com/discord/simpleast/code/CodeRules.kt | 5 +- .../core/simple/SimpleMarkdownRules.kt | 10 ++-- .../discord/simpleast/code/CodeRulesTest.kt | 49 +++++++++++++++++-- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/discord/simpleast/sample/MainActivity.kt b/app/src/main/java/com/discord/simpleast/sample/MainActivity.kt index e149fc2..03e0855 100644 --- a/app/src/main/java/com/discord/simpleast/sample/MainActivity.kt +++ b/app/src/main/java/com/discord/simpleast/sample/MainActivity.kt @@ -35,7 +35,11 @@ class MainActivity : AppCompatActivity() { setContentView(R.layout.activity_main) parser = Parser, ParseState>() - .addRules(UserMentionRule(), CustomMarkdownRules.createBlockQuoteRule()) + .addRules( + // Allow callers to escape markdown commands such as code block ticks + SimpleMarkdownRules.createEscapeRule(), + UserMentionRule(), + CustomMarkdownRules.createBlockQuoteRule()) .addRules(CustomMarkdownRules.createMarkdownRules( this@MainActivity, listOf(R.style.Demo_Header_1, R.style.Demo_Header_2, R.style.Demo_Header_3), @@ -43,7 +47,7 @@ class MainActivity : AppCompatActivity() { .addRules( CustomMarkdownRules.createCodeRule(this@MainActivity), CustomMarkdownRules.createCodeInlineRule(this@MainActivity)) - .addRules(SimpleMarkdownRules.createSimpleMarkdownRules()) + .addRules(SimpleMarkdownRules.createSimpleMarkdownRules(includeEscapeRule = false)) resultText = findViewById(R.id.result_text) input = findViewById(R.id.input) diff --git a/simpleast-core/build.gradle b/simpleast-core/build.gradle index 1da3157..6f14e36 100644 --- a/simpleast-core/build.gradle +++ b/simpleast-core/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 21 targetSdkVersion 30 versionCode 28 - versionName "2.1.3" + versionName "2.2.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/simpleast-core/src/main/java/com/discord/simpleast/code/CodeRules.kt b/simpleast-core/src/main/java/com/discord/simpleast/code/CodeRules.kt index 9548dd9..6a75550 100644 --- a/simpleast-core/src/main/java/com/discord/simpleast/code/CodeRules.kt +++ b/simpleast-core/src/main/java/com/discord/simpleast/code/CodeRules.kt @@ -41,7 +41,7 @@ object CodeRules { Pattern.compile("""^```(?:([\w+\-.]+?)?(\s*\n))?([^\n].*?)\n*```""", Pattern.DOTALL) val PATTERN_CODE_INLINE: Pattern = - Pattern.compile("""^`(?:\s*)([^\n].*?)\n*`""", Pattern.DOTALL) + Pattern.compile("""^`([^`]*?)`""", Pattern.DOTALL) private const val CODE_BLOCK_LANGUAGE_GROUP = 1 private const val CODE_BLOCK_WS_PREFIX = 2 @@ -272,6 +272,9 @@ object CodeRules { override fun parse(matcher: Matcher, parser: Parser, S>, state: S) : ParseSpec { val codeBody = matcher.group(1).orEmpty() + if (codeBody.isEmpty()) { + return ParseSpec.createTerminal(TextNode(matcher.group()), state) + } val content = CodeNode.Content.Raw(codeBody) diff --git a/simpleast-core/src/main/java/com/discord/simpleast/core/simple/SimpleMarkdownRules.kt b/simpleast-core/src/main/java/com/discord/simpleast/core/simple/SimpleMarkdownRules.kt index 92c4bc5..dffe42b 100644 --- a/simpleast-core/src/main/java/com/discord/simpleast/core/simple/SimpleMarkdownRules.kt +++ b/simpleast-core/src/main/java/com/discord/simpleast/core/simple/SimpleMarkdownRules.kt @@ -69,7 +69,7 @@ object SimpleMarkdownRules { fun createEscapeRule(): Rule, S> { return object : Rule, S>(PATTERN_ESCAPE) { override fun parse(matcher: Matcher, parser: Parser, S>, state: S): ParseSpec { - return ParseSpec.createTerminal(TextNode(matcher.group(1)), state) + return ParseSpec.createTerminal(TextNode(matcher.group(1)!!), state) } } } @@ -80,7 +80,7 @@ object SimpleMarkdownRules { val startIndex: Int val endIndex: Int val asteriskMatch = matcher.group(2) - if (asteriskMatch != null && asteriskMatch.length > 0) { + if (asteriskMatch != null && asteriskMatch.isNotEmpty()) { startIndex = matcher.start(2) endIndex = matcher.end(2) } else { @@ -98,9 +98,11 @@ object SimpleMarkdownRules { } @JvmOverloads @JvmStatic - fun createSimpleMarkdownRules(includeTextRule: Boolean = true): MutableList, S>> { + fun createSimpleMarkdownRules(includeTextRule: Boolean = true, includeEscapeRule:Boolean = true): MutableList, S>> { val rules = ArrayList, S>>() - rules.add(createEscapeRule()) + if (includeEscapeRule) { + rules.add(createEscapeRule()) + } rules.add(createNewlineRule()) rules.add(createBoldRule()) rules.add(createUnderlineRule()) diff --git a/simpleast-core/src/test/java/com/discord/simpleast/code/CodeRulesTest.kt b/simpleast-core/src/test/java/com/discord/simpleast/code/CodeRulesTest.kt index a8db940..6facc79 100644 --- a/simpleast-core/src/test/java/com/discord/simpleast/code/CodeRulesTest.kt +++ b/simpleast-core/src/test/java/com/discord/simpleast/code/CodeRulesTest.kt @@ -1,8 +1,11 @@ package com.discord.simpleast.code +import android.graphics.Color +import android.text.style.BackgroundColorSpan import com.discord.simpleast.assertNodeContents import com.discord.simpleast.core.node.Node import com.discord.simpleast.core.node.StyleNode +import com.discord.simpleast.core.node.TextNode import com.discord.simpleast.core.parser.Parser import com.discord.simpleast.core.simple.SimpleMarkdownRules import com.discord.simpleast.core.utils.TreeMatcher @@ -21,15 +24,51 @@ class CodeRulesTest { fun setup() { parser = Parser() val codeStyleProviders = CodeStyleProviders() - parser.addRule(CodeRules.createCodeRule( - codeStyleProviders.defaultStyleProvider, - CodeRules.createCodeLanguageMap(codeStyleProviders)) - ) - parser.addRules(SimpleMarkdownRules.createSimpleMarkdownRules()) + parser + // Duplicated in `getBasicRules` but required to occur before block + quotes + .addRule(SimpleMarkdownRules.createEscapeRule()) + .addRule(CodeRules.createCodeRule( + codeStyleProviders.defaultStyleProvider, + CodeRules.createCodeLanguageMap(codeStyleProviders)) + ) + .addRule(CodeRules.createInlineCodeRule( + { listOf(BackgroundColorSpan(Color.WHITE)) }, + { listOf(BackgroundColorSpan(Color.DKGRAY)) }) + ) + .addRules(SimpleMarkdownRules.createSimpleMarkdownRules(includeEscapeRule = false)) treeMatcher = TreeMatcher() treeMatcher.registerDefaultMatchers() } + @Test + fun inlined() { + val ast = parser.parse(""" + `inlined` + `inline extra`` + """.trimIndent(), TestState()) + + ast.assertNodeContents>("inlined", "inline extra") + ast.assertNodeContents>("inlined", "\n", "inline extra", "`") + } + + @Test + fun inlineNoContent() { + val ast = parser.parse(""" + `` + """.trimIndent(), TestState()) + + ast.assertNodeContents>("``") + } + + @Test + fun inlineEscapedBlock() { + val ast = parser.parse(""" + \```test``` + """.trimIndent(), TestState()) + + ast.assertNodeContents>("`", "``", "test", "``", "`") + } + @Test fun noLanguageOneLined() { val ast = parser.parse("""