Mixed-Language Parsing¶
Some documents contain multiple languages — for example, HTML with embedded JavaScript and CSS. Kodemirror supports this through the parseMixed wrapper.
Using a mixed-language parser¶
The simplest way to use mixed-language parsing is through a language package that already configures it, like html():
val session = rememberEditorSession(
doc = SampleDocs.html,
extensions = showcaseSetup + html().extension
)
The html() language support automatically nests JavaScript and CSS parsers inside <script> and <style> tags.
parseMixed¶
Under the hood, parseMixed wraps a parser to scan its tree for regions that should be parsed by a different language:
import com.monkopedia.kodemirror.lezer.common.*
val mixedParser = parseMixed { nodeRef, input ->
when (nodeRef.type.name) {
"ScriptContent" -> NestedParse(
parser = javascriptParser,
overlay = null // parse the entire node
)
"StyleContent" -> NestedParse(
parser = cssParser,
overlay = null
)
else -> null
}
}
NestedParse¶
class NestedParse(
val parser: Parser,
val overlay: ParseOverlay? = null,
val bracketed: Boolean = false
)
sealed interface ParseOverlay {
data class Ranges(val ranges: List<TextRange>) : ParseOverlay
data class Predicate(val match: (SyntaxNodeRef) -> ParseOverlayMatch?) : ParseOverlay
}
sealed interface ParseOverlayMatch {
data object FullNode : ParseOverlayMatch
data class CustomRange(val range: TextRange) : ParseOverlayMatch
}
| Property | Description |
|---|---|
parser | The parser for the nested language |
overlay | null to parse the full node; ParseOverlay.Ranges for specific ranges; or ParseOverlay.Predicate for dynamic matching |
bracketed | Whether the nested region is bracket-delimited |
Overlay mode¶
Use overlay mode when the nested language appears in scattered regions within a node. Pass a ParseOverlay.Predicate as overlay:
parseMixed { nodeRef, input ->
if (nodeRef.type.name == "TemplateString") {
NestedParse(
parser = expressionParser,
overlay = ParseOverlay.Predicate { child: SyntaxNodeRef ->
if (child.type.name == "Interpolation") {
ParseOverlayMatch.FullNode
} else {
null
}
}
)
} else null
}
Building a mixed-language support¶
Combine parseMixed with LRLanguage and LanguageSupport:
import com.monkopedia.kodemirror.language.*
val htmlLanguage = LRLanguage.define(
parser = parseMixed { nodeRef, input ->
when (nodeRef.type.name) {
"ScriptContent" -> NestedParse(parser = jsParser)
"StyleContent" -> NestedParse(parser = cssParser)
else -> null
}
}.configure(htmlParser),
name = "html"
)
fun html(): LanguageSupport = LanguageSupport(
language = htmlLanguage,
// supporting extensions for JS, CSS, etc.
)
Key points¶
- The
nestfunction inparseMixedis called for each node in the outer parse tree. ReturnNestedParseto parse that region with another parser, ornullto skip it. parseMixedreturns aParseWrapperthat you apply to the outer parser to produce a combined parser.- Nested parsers run incrementally — only the changed regions are re-parsed on edits.
Based on the CodeMirror Mixed-Language Parsing example.