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("$closing>")
+ 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