Skip to content

Commit

Permalink
Add XML support
Browse files Browse the repository at this point in the history
  • Loading branch information
lytefast committed Oct 25, 2020
1 parent 170bad0 commit 5c87693
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
19 changes: 19 additions & 0 deletions app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,24 @@ object SampleTexts {
```
"""


private const val CODE_BLOCK_XML = """
XML code block:
```xml
<!--
Multi-line
Commnent
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<attr name="primary_100" format="reference|color" />
<!--<editor-fold desc="Android material styles">-->
<item name="colorPrimary">@color/black</item>
</resources>
```
"""

const val CODE_BLOCKS = """
# Code block samples
inlined:```kt private fun test() {}```
Expand All @@ -134,6 +152,7 @@ object SampleTexts {
$CODE_BLOCK_KOTLIN
$CODE_BLOCK_PYTHON
$CODE_BLOCK_RUST
$CODE_BLOCK_XML
That should do it....
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ object CodeRules {
"in|as|ref",
)

val xmlRules = listOf<Rule<R, Node<R>, 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,
Expand All @@ -148,6 +156,9 @@ object CodeRules {

"rs" to rustRules,
"rust" to rustRules,

"xml" to xmlRules,
"http" to xmlRules,
)
}

Expand Down
82 changes: 82 additions & 0 deletions simpleast-core/src/main/java/com/discord/simpleast/code/Xml.kt
Original file line number Diff line number Diff line change
@@ -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("""^<!--[\s\S]*?-->""", 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<RC>(
val opening: String, val closing: String?,
private val codeStyleProviders: CodeStyleProviders<RC>
) : Node.Parent<RC>() {
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 <RC, S> createTagRule(
codeStyleProviders: CodeStyleProviders<RC>
) =
object : Rule<RC, Node<RC>, S>(PATTERN_XML_TAG) {
override fun parse(matcher: Matcher, parser: Parser<RC, in Node<RC>, S>, state: S):
ParseSpec<RC, Node<RC>, 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)
}
}
}
}

0 comments on commit 5c87693

Please sign in to comment.