diff --git a/app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt b/app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt index ef16435..e926d87 100644 --- a/app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt +++ b/app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt @@ -124,6 +124,24 @@ object SampleTexts { ``` """ + + private const val CODE_BLOCK_XML = """ + XML code block: + ```xml + + + + + + + @color/black + + ``` + """ + const val CODE_BLOCKS = """ # Code block samples inlined:```kt private fun test() {}``` @@ -134,6 +152,7 @@ object SampleTexts { $CODE_BLOCK_KOTLIN $CODE_BLOCK_PYTHON $CODE_BLOCK_RUST + $CODE_BLOCK_XML That should do it.... """ 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 4a0b058..b4c1a9d 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 @@ -139,6 +139,14 @@ object CodeRules { "in|as|ref", ) + val xmlRules = listOf, S>>( + Xml.PATTERN_XML_COMMENT + .toFullMatchRule(codeStyleProviders.commentStyleProvider), + Xml.createTagRule(codeStyleProviders), + PATTERN_LEADING_WS_CONSUMER.toFullMatchRule(), + PATTERN_TEXT.toFullMatchRule(), + ) + return mapOf( "kt" to kotlinRules, "kotlin" to kotlinRules, @@ -148,6 +156,9 @@ object CodeRules { "rs" to rustRules, "rust" to rustRules, + + "xml" to xmlRules, + "http" to xmlRules, ) } diff --git a/simpleast-core/src/main/java/com/discord/simpleast/code/Xml.kt b/simpleast-core/src/main/java/com/discord/simpleast/code/Xml.kt new file mode 100644 index 0000000..be73943 --- /dev/null +++ b/simpleast-core/src/main/java/com/discord/simpleast/code/Xml.kt @@ -0,0 +1,82 @@ +package com.discord.simpleast.code + +import android.text.SpannableStringBuilder +import android.text.Spanned +import com.discord.simpleast.core.node.Node +import com.discord.simpleast.core.parser.ParseSpec +import com.discord.simpleast.core.parser.Parser +import com.discord.simpleast.core.parser.Rule +import java.util.regex.Matcher +import java.util.regex.Pattern + + +object Xml { + val PATTERN_XML_COMMENT = Pattern.compile("""^""", Pattern.DOTALL) + + val PATTERN_XML_TAG = Pattern.compile("""^<([\s\S]+?)(?:>(.*?)<\/([\s\S]+?))?>""", Pattern.DOTALL) + val PATTERN_XML_TAG_OPENING_GROUP = 1 + val PATTERN_XML_TAG_CONTENT_GROUP = 2 + val PATTERN_XML_TAG_CLOSING_GROUP = 3 + + class TagNode( + val opening: String, val closing: String?, + private val codeStyleProviders: CodeStyleProviders + ) : Node.Parent() { + override fun render(builder: SpannableStringBuilder, renderContext: RC) { + val (name, remainder) = + when (val index = opening.indexOfFirst { it.isWhitespace() || it == '/' }) { + -1 -> opening to "" + else -> opening.substring(0, index) to opening.substring(index) + } + val typeStylesProvider = codeStyleProviders.genericsStyleProvider::get + + var startIndex = builder.length + builder.append("<$name") + typeStylesProvider(renderContext).forEach { + builder.setSpan(it, startIndex, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + startIndex = builder.length + builder.append("$remainder>") + codeStyleProviders.paramsStyleProvider.get(renderContext).forEach { + builder.setSpan(it, startIndex, builder.length - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + typeStylesProvider(renderContext).forEach { + builder.setSpan(it, builder.length - 1, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + super.render(builder, renderContext) + + if (!closing.isNullOrEmpty()) { + startIndex = builder.length + builder.append("") + typeStylesProvider(renderContext).forEach { + builder.setSpan(it, startIndex + 1, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + } + } + + fun createTagRule( + codeStyleProviders: CodeStyleProviders + ) = + object : Rule, S>(PATTERN_XML_TAG) { + override fun parse(matcher: Matcher, parser: Parser, S>, state: S): + ParseSpec, S> { + val opening = matcher.group(PATTERN_XML_TAG_OPENING_GROUP)!! + val closing = matcher.group(PATTERN_XML_TAG_CLOSING_GROUP) + + return if (matcher.group(PATTERN_XML_TAG_CONTENT_GROUP) != null) { + ParseSpec.createNonterminal( + TagNode(opening, closing, codeStyleProviders), + state, + matcher.start(PATTERN_XML_TAG_CONTENT_GROUP), + matcher.end(PATTERN_XML_TAG_CONTENT_GROUP)) + } else { + ParseSpec.createTerminal( + TagNode(opening, closing, codeStyleProviders), + state) + } + } + } +} \ No newline at end of file