Colour markdown, which is the one language that is not tokens
The token scanner asks what a character is; markdown's meaning is where it sits, so a `#` opens a heading at the start of a line and is an ordinary character three words in. `MarkdownSyntax.kt` reads structure a line at a time and then each line's prose left to right, and `spansOf` is the one entry point that hides which of the two scanners a language got. Conservative wherever a guess would be invisible: emphasis needs a closer on the same line with no space beside either marker, so the `*p = *q` of a C fragment opens nothing; an underscore may not start or end inside a word; and an indented code block is left plain, since four spaces after a blank line and four after a bullet are the same line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a401e6a7e3
commit
68c5180260
8 files changed
+476
-12
No files matched your search
@@ -1,11 +1,14 @@
|
||||
package com.example.aiapp
|
||||
|
||||
/**
|
||||
* A language the highlighter has rules for.
|
||||
* A language the highlighter can colour.
|
||||
*
|
||||
* The names the reader writes after the backticks are aliases onto these; [fenceLanguage] holds
|
||||
* that table. A word with no entry there is null, and null is drawn plain, because a fence coloured
|
||||
* by another language's rules looks highlighted and is wrong in a way the reader cannot see.
|
||||
*
|
||||
* Nearly all of them are a row of [RULES], read by one shared scanner. [MARKDOWN] is the one that
|
||||
* is not; see [spansOf].
|
||||
*/
|
||||
enum class Language {
|
||||
C,
|
||||
@@ -19,6 +22,7 @@ enum class Language {
|
||||
JAVASCRIPT,
|
||||
JSON,
|
||||
KOTLIN,
|
||||
MARKDOWN,
|
||||
PERL,
|
||||
PHP,
|
||||
PYTHON,
|
||||
@@ -83,8 +87,23 @@ enum class Attributes {
|
||||
LINE_BRACKET,
|
||||
}
|
||||
|
||||
/** The rules for [language]. */
|
||||
fun rulesOf(language: Language): Rules = RULES.getValue(language)
|
||||
/**
|
||||
* The spans [language] colours in [code] -- the one way to ask, whatever the language turns out to
|
||||
* be made of.
|
||||
*
|
||||
* Nearly every language here is tokens: keywords, strings and comments, which is a row of [RULES]
|
||||
* and the one shared scanner in [scan]. Markdown has none of those, and what a character means
|
||||
* there depends on where on the line it sits, so it brings a scanner of its own ([scanMarkdown]).
|
||||
* That is the whole extension point -- a new language is a row of rules or an entry in [SCANNERS],
|
||||
* and no caller learns which one it got.
|
||||
*/
|
||||
fun spansOf(code: String, language: Language): List<Span> = SCANNERS.getValue(language)(code)
|
||||
|
||||
// Lazy for the same reason [RULES] is, since it reads it.
|
||||
private val SCANNERS: Map<Language, (String) -> List<Span>> by lazy {
|
||||
RULES.mapValues { (_, rules) -> { code: String -> scan(code, rules) } } +
|
||||
mapOf(Language.MARKDOWN to ::scanMarkdown)
|
||||
}
|
||||
|
||||
private val C_STYLE = BlockComment("/*", "*/", nests = false)
|
||||
private val NESTING = BlockComment("/*", "*/", nests = true)
|
||||
|
||||
Reference in new issue
Block a user