@peaceroad/markdown-it-renderer-fence 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +440 -117
- package/THIRD_PARTY_NOTICES.md +56 -0
- package/index.js +33 -503
- package/package.json +28 -4
- package/src/custom-highlight/option-validator.js +152 -0
- package/src/custom-highlight/payload-utils.js +43 -0
- package/src/custom-highlight/runtime-utils.js +83 -0
- package/src/custom-highlight/shiki-keyword-rules.js +407 -0
- package/src/custom-highlight/shiki-keyword.js +417 -0
- package/src/entry/custom-highlight-runtime.js +11 -0
- package/src/entry/custom-highlight.js +25 -0
- package/src/entry/markup-highlight.js +40 -0
- package/src/fence/render-api-constants.js +34 -0
- package/src/fence/render-api-provider.js +810 -0
- package/src/fence/render-api-renderer.js +136 -0
- package/src/fence/render-api-runtime.js +712 -0
- package/src/fence/render-api.js +117 -0
- package/src/fence/render-markup.js +161 -0
- package/src/fence/render-shared.js +428 -0
- package/src/utils/attr-utils.js +120 -0
- package/src/utils/pre-code-wrapper-parser.js +114 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyKeywordV4Rules,
|
|
3
|
+
applyShikiKeywordLegacyPostRules,
|
|
4
|
+
} from './shiki-keyword-rules.js'
|
|
5
|
+
|
|
6
|
+
const shikiKeywordBucketScoreV3 = {
|
|
7
|
+
comment: 210,
|
|
8
|
+
'meta-shebang': 206,
|
|
9
|
+
'tag-delimiter': 202,
|
|
10
|
+
attribute: 200,
|
|
11
|
+
tag: 198,
|
|
12
|
+
keyword: 194,
|
|
13
|
+
'type-primitive': 192,
|
|
14
|
+
'type-name': 190,
|
|
15
|
+
type: 188,
|
|
16
|
+
number: 186,
|
|
17
|
+
literal: 184,
|
|
18
|
+
string: 182,
|
|
19
|
+
'string-unquoted': 180,
|
|
20
|
+
namespace: 176,
|
|
21
|
+
'title-function-builtin': 174,
|
|
22
|
+
'title-function': 172,
|
|
23
|
+
'title-class': 170,
|
|
24
|
+
'variable-this': 168,
|
|
25
|
+
'variable-const': 166,
|
|
26
|
+
'variable-member': 164,
|
|
27
|
+
'variable-parameter': 162,
|
|
28
|
+
'variable-property': 160,
|
|
29
|
+
'variable-plain': 158,
|
|
30
|
+
variable: 156,
|
|
31
|
+
punctuation: 140,
|
|
32
|
+
meta: 70,
|
|
33
|
+
text: 10,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const shikiGlobalLiteralTokenSet = new Set(['true', 'false', 'null', 'undefined', 'nan', 'inf', 'none', 'nil'])
|
|
37
|
+
const shikiShellKeywordTokenSet = new Set(['case', 'coproc', 'do', 'done', 'elif', 'else', 'esac', 'export', 'fi', 'for', 'function', 'if', 'in', 'local', 'readonly', 'select', 'then', 'time', 'typeset', 'until', 'while'])
|
|
38
|
+
const shikiHclKeywordTokenSet = new Set(['terraform', 'resource', 'data', 'module', 'provider', 'variable', 'output', 'locals', 'backend', 'dynamic', 'for_each', 'count', 'provisioner', 'connection', 'if', 'for', 'in'])
|
|
39
|
+
|
|
40
|
+
const shikiKeywordTokensByLang = {
|
|
41
|
+
javascript: new Set(['await', 'async', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'export', 'extends', 'finally', 'for', 'from', 'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'of', 'return', 'static', 'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']),
|
|
42
|
+
typescript: new Set(['abstract', 'as', 'asserts', 'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'declare', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'from', 'function', 'if', 'implements', 'import', 'in', 'infer', 'instanceof', 'interface', 'is', 'keyof', 'let', 'namespace', 'new', 'of', 'override', 'private', 'protected', 'public', 'readonly', 'return', 'satisfies', 'static', 'super', 'switch', 'this', 'throw', 'try', 'type', 'typeof', 'var', 'void', 'while', 'with', 'yield']),
|
|
43
|
+
python: new Set(['and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']),
|
|
44
|
+
bash: shikiShellKeywordTokenSet,
|
|
45
|
+
shellscript: shikiShellKeywordTokenSet,
|
|
46
|
+
sql: new Set(['all', 'alter', 'and', 'as', 'asc', 'between', 'by', 'create', 'delete', 'desc', 'distinct', 'drop', 'from', 'group', 'having', 'in', 'inner', 'insert', 'into', 'join', 'left', 'limit', 'not', 'null', 'offset', 'on', 'or', 'order', 'order by', 'outer', 'right', 'select', 'set', 'table', 'union', 'update', 'values', 'where', 'with']),
|
|
47
|
+
go: new Set(['break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else', 'fallthrough', 'for', 'func', 'go', 'goto', 'if', 'import', 'interface', 'map', 'package', 'range', 'return', 'select', 'struct', 'switch', 'type', 'var']),
|
|
48
|
+
rust: new Set(['as', 'async', 'await', 'break', 'const', 'continue', 'crate', 'dyn', 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'while']),
|
|
49
|
+
java: new Set(['abstract', 'assert', 'break', 'case', 'catch', 'class', 'continue', 'default', 'do', 'else', 'enum', 'extends', 'final', 'finally', 'for', 'if', 'implements', 'import', 'instanceof', 'interface', 'native', 'new', 'package', 'private', 'protected', 'public', 'return', 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'try', 'volatile', 'while']),
|
|
50
|
+
ruby: new Set(['alias', 'and', 'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', 'else', 'elsif', 'end', 'ensure', 'for', 'if', 'in', 'module', 'next', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super', 'then', 'undef', 'unless', 'until', 'when', 'while', 'yield']),
|
|
51
|
+
c: new Set(['auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum', 'extern', 'for', 'goto', 'if', 'inline', 'register', 'restrict', 'return', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'volatile', 'while']),
|
|
52
|
+
cpp: new Set(['alignas', 'alignof', 'auto', 'break', 'case', 'catch', 'class', 'const', 'consteval', 'constexpr', 'constinit', 'continue', 'decltype', 'default', 'delete', 'do', 'else', 'enum', 'explicit', 'export', 'extern', 'final', 'for', 'friend', 'goto', 'if', 'inline', 'mutable', 'namespace', 'new', 'noexcept', 'operator', 'override', 'private', 'protected', 'public', 'return', 'sizeof', 'static', 'struct', 'switch', 'template', 'this', 'throw', 'try', 'typedef', 'typename', 'union', 'using', 'virtual', 'volatile', 'while']),
|
|
53
|
+
csharp: new Set(['abstract', 'as', 'async', 'await', 'base', 'break', 'case', 'catch', 'checked', 'class', 'const', 'continue', 'default', 'delegate', 'do', 'else', 'enum', 'event', 'explicit', 'extern', 'finally', 'fixed', 'for', 'foreach', 'goto', 'if', 'implicit', 'in', 'interface', 'internal', 'is', 'lock', 'namespace', 'new', 'operator', 'out', 'override', 'params', 'private', 'protected', 'public', 'readonly', 'record', 'ref', 'return', 'sealed', 'sizeof', 'stackalloc', 'static', 'struct', 'switch', 'this', 'throw', 'try', 'typeof', 'unchecked', 'unsafe', 'using', 'virtual', 'volatile', 'while', 'var']),
|
|
54
|
+
php: new Set(['abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'do', 'echo', 'else', 'elseif', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'extends', 'final', 'finally', 'fn', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'interface', 'match', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'readonly', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'use', 'while', 'xor', 'yield']),
|
|
55
|
+
hcl: shikiHclKeywordTokenSet,
|
|
56
|
+
terraform: shikiHclKeywordTokenSet,
|
|
57
|
+
css: new Set(['from', 'to']),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const shikiNumberTokenReg = /^(?:0[xob][0-9a-f_]+|\d[\d_]*(?:\.\d[\d_]*)?(?:e[+-]?\d+)?)$/i
|
|
61
|
+
const shikiPunctuationTokenReg = /^[()[\]{}<>.,;:!?~`'"@#$%^&*+=|/\\-]+$/
|
|
62
|
+
const shikiSimpleIdentifierReg = /^[A-Za-z_$][\w$]*$/
|
|
63
|
+
const shikiScopeLangReg = /(?:^|\.)(?:source|text)\.([A-Za-z0-9_#+-]+)/g
|
|
64
|
+
const shikiKeywordLangSanitizeReg = /[^a-z0-9#+-]+/g
|
|
65
|
+
const hyphenMultiReg = /-+/g
|
|
66
|
+
const shikiScopeKeywordSingleV3Cache = new Map()
|
|
67
|
+
|
|
68
|
+
const normalizeShikiKeywordLangKey = (lang) => {
|
|
69
|
+
let key = String(lang || '').trim().toLowerCase()
|
|
70
|
+
if (!key) return ''
|
|
71
|
+
if (key.startsWith('language-')) key = key.slice('language-'.length)
|
|
72
|
+
key = key.replace(/\s+/g, '-').replace(/[./]+/g, '-').replace(/_+/g, '-')
|
|
73
|
+
key = key.replace(shikiKeywordLangSanitizeReg, '')
|
|
74
|
+
key = key.replace(hyphenMultiReg, '-').replace(/^-+|-+$/g, '')
|
|
75
|
+
return key
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const normalizeShikiKeywordLangAliasMap = (input) => {
|
|
79
|
+
if (!input || typeof input !== 'object') return null
|
|
80
|
+
const map = {}
|
|
81
|
+
for (const key of Object.keys(input)) {
|
|
82
|
+
const from = normalizeShikiKeywordLangKey(key)
|
|
83
|
+
const to = normalizeShikiKeywordLangKey(input[key])
|
|
84
|
+
if (!from || !to) continue
|
|
85
|
+
map[from] = to
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(map).length ? map : null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const getShikiRawScopeName = (tok) => {
|
|
91
|
+
if (Array.isArray(tok.scopes) && tok.scopes.length > 0) return tok.scopes[tok.scopes.length - 1]
|
|
92
|
+
if (typeof tok.scope === 'string' && tok.scope) return tok.scope
|
|
93
|
+
if (Array.isArray(tok.explanation) && tok.explanation.length > 0) {
|
|
94
|
+
for (let i = tok.explanation.length - 1; i >= 0; i--) {
|
|
95
|
+
const exp = tok.explanation[i]
|
|
96
|
+
if (!exp || !Array.isArray(exp.scopes) || exp.scopes.length === 0) continue
|
|
97
|
+
for (let j = exp.scopes.length - 1; j >= 0; j--) {
|
|
98
|
+
const scope = exp.scopes[j]
|
|
99
|
+
if (typeof scope === 'string' && scope) return scope
|
|
100
|
+
if (scope && typeof scope === 'object' && typeof scope.scopeName === 'string' && scope.scopeName) {
|
|
101
|
+
return scope.scopeName
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const extractShikiKeywordLangCandidatesFromScope = (scope) => {
|
|
110
|
+
const out = []
|
|
111
|
+
const text = String(scope || '')
|
|
112
|
+
if (!text) return out
|
|
113
|
+
shikiScopeLangReg.lastIndex = 0
|
|
114
|
+
let m
|
|
115
|
+
while ((m = shikiScopeLangReg.exec(text)) !== null) {
|
|
116
|
+
const raw = m[1]
|
|
117
|
+
if (raw) out.push(raw)
|
|
118
|
+
}
|
|
119
|
+
return out
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const getShikiScopeCandidates = (tok, rawScope) => {
|
|
123
|
+
const out = []
|
|
124
|
+
const seen = new Set()
|
|
125
|
+
const push = (scope) => {
|
|
126
|
+
if (typeof scope !== 'string' || !scope || seen.has(scope)) return
|
|
127
|
+
seen.add(scope)
|
|
128
|
+
out.push(scope)
|
|
129
|
+
}
|
|
130
|
+
push(rawScope)
|
|
131
|
+
if (Array.isArray(tok.scopes)) {
|
|
132
|
+
for (let i = tok.scopes.length - 1; i >= 0; i--) push(tok.scopes[i])
|
|
133
|
+
}
|
|
134
|
+
if (typeof tok.scope === 'string' && tok.scope) push(tok.scope)
|
|
135
|
+
if (Array.isArray(tok.explanation)) {
|
|
136
|
+
for (let i = 0; i < tok.explanation.length; i++) {
|
|
137
|
+
const exp = tok.explanation[i]
|
|
138
|
+
if (!exp || !Array.isArray(exp.scopes)) continue
|
|
139
|
+
for (let j = exp.scopes.length - 1; j >= 0; j--) {
|
|
140
|
+
const scope = exp.scopes[j]
|
|
141
|
+
if (typeof scope === 'string' && scope) push(scope)
|
|
142
|
+
else if (scope && typeof scope.scopeName === 'string' && scope.scopeName) push(scope.scopeName)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const toLowerScopeCandidates = (scopeCandidates) => {
|
|
150
|
+
if (!Array.isArray(scopeCandidates)) return []
|
|
151
|
+
const out = new Array(scopeCandidates.length)
|
|
152
|
+
for (let i = 0; i < scopeCandidates.length; i++) out[i] = String(scopeCandidates[i] || '').toLowerCase()
|
|
153
|
+
return out
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const hasAnyScopePattern = (lowerScopeCandidates, patterns) => {
|
|
157
|
+
if (!Array.isArray(lowerScopeCandidates) || lowerScopeCandidates.length === 0 || !Array.isArray(patterns) || patterns.length === 0) return false
|
|
158
|
+
for (let i = 0; i < lowerScopeCandidates.length; i++) {
|
|
159
|
+
const s = lowerScopeCandidates[i]
|
|
160
|
+
if (!s) continue
|
|
161
|
+
for (let j = 0; j < patterns.length; j++) {
|
|
162
|
+
if (s.includes(patterns[j])) return true
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const createScopePatternMatcher = (lowerScopeCandidates) => {
|
|
169
|
+
const cache = new Map()
|
|
170
|
+
return (patterns) => {
|
|
171
|
+
if (!Array.isArray(patterns) || patterns.length === 0) return false
|
|
172
|
+
const key = patterns.length === 1 ? patterns[0] : patterns.join('\u0001')
|
|
173
|
+
if (cache.has(key)) return cache.get(key)
|
|
174
|
+
const hit = hasAnyScopePattern(lowerScopeCandidates, patterns)
|
|
175
|
+
cache.set(key, hit)
|
|
176
|
+
return hit
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const classifyShikiScopeKeywordSingleV3 = (scope) => {
|
|
181
|
+
const s = String(scope || '').toLowerCase()
|
|
182
|
+
if (!s) return 'text'
|
|
183
|
+
const cached = shikiScopeKeywordSingleV3Cache.get(s)
|
|
184
|
+
if (cached) return cached
|
|
185
|
+
let bucket = 'text'
|
|
186
|
+
if (s.includes('comment')) return 'comment'
|
|
187
|
+
if (s.includes('meta.shebang')) return 'meta-shebang'
|
|
188
|
+
if (s.includes('punctuation.definition.tag.begin') || s.includes('punctuation.definition.tag.end') || s.includes('punctuation.separator.key-value.html')) return 'tag-delimiter'
|
|
189
|
+
if (s.includes('entity.other.attribute-name') || s.includes('attribute-name')) return 'attribute'
|
|
190
|
+
if (s.includes('entity.name.tag')) return 'tag'
|
|
191
|
+
if (s.includes('string.unquoted')) return 'string-unquoted'
|
|
192
|
+
if (s.includes('regexp') || s.includes('regex') || s.includes('string') || s.includes('punctuation.definition.string')) return 'string'
|
|
193
|
+
if (s.includes('constant.other.color') || s.includes('support.constant.property-value')) return 'number'
|
|
194
|
+
if (s.includes('constant.numeric') || s.includes('number')) return 'number'
|
|
195
|
+
if (s.includes('constant.language') || s.includes('boolean') || s.includes('null') || s.includes('undefined') || s.includes('none')) return 'literal'
|
|
196
|
+
if (s.includes('entity.name.scope-resolution') || s.includes('entity.name.namespace')) return 'namespace'
|
|
197
|
+
if (s.includes('storage.type.function.arrow') || s.includes('keyword.operator')) return 'keyword'
|
|
198
|
+
if (s.includes('variable.language.this')) return 'variable-this'
|
|
199
|
+
if (s.includes('constant.other.php') || s.includes('variable.other.constant') || s.includes('variable.other.enummember')) return 'variable-const'
|
|
200
|
+
if (s.includes('variable.object.property') || s.includes('variable.other.property') || s.includes('variable.other.member') || s.includes('variable.object.c') || s.includes('variable.ruby')) return 'variable-member'
|
|
201
|
+
if (s.includes('variable.parameter') || s.includes('entity.name.variable.parameter')) return 'variable-parameter'
|
|
202
|
+
if (s.includes('entity.name.variable.local') || s.includes('variable.other.readwrite') || s.includes('variable.other.normal') || s.includes('variable.other.php') || (s.includes('variable.other.') && !s.includes('variable.other.property') && !s.includes('variable.other.member'))) return 'variable-plain'
|
|
203
|
+
if (s.includes('support.function.builtin') || s.includes('support.function.kernel') || s.includes('support.function.construct')) return 'title-function-builtin'
|
|
204
|
+
if (s.includes('entity.name.function.macro')) return 'title-function'
|
|
205
|
+
if (s.includes('meta.function') && !s.includes('entity.name.function') && !s.includes('support.function')) return 'meta'
|
|
206
|
+
if (s.includes('entity.name.function') || s.includes('support.function')) return 'title-function'
|
|
207
|
+
if (s.includes('entity.name.class') || s.includes('entity.name.type.class')) return 'title-class'
|
|
208
|
+
if (s.includes('storage.type.built-in.primitive')) return 'type-primitive'
|
|
209
|
+
if (s.includes('entity.name.type.namespace') || s.includes('entity.name.type') || s.includes('support.class')) return 'type-name'
|
|
210
|
+
if (s.includes('support.type') || s.includes('storage.modifier') || s.includes('keyword.type')) return 'type'
|
|
211
|
+
if (s.includes('storage') || s.includes('keyword') || s.includes('operator') || s.includes('control') || s.includes('modifier')) return 'keyword'
|
|
212
|
+
if (s.includes('variable')) return 'variable'
|
|
213
|
+
if (s.includes('punctuation')) return 'punctuation'
|
|
214
|
+
if (s.includes('source.')) bucket = 'text'
|
|
215
|
+
else if (s.includes('meta')) bucket = 'meta'
|
|
216
|
+
shikiScopeKeywordSingleV3Cache.set(s, bucket)
|
|
217
|
+
if (shikiScopeKeywordSingleV3Cache.size > 8192) shikiScopeKeywordSingleV3Cache.clear()
|
|
218
|
+
return bucket
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const hasJsonYamlPropertyNameScope = (scopeCandidates) => {
|
|
222
|
+
if (!Array.isArray(scopeCandidates)) return false
|
|
223
|
+
for (let i = 0; i < scopeCandidates.length; i++) {
|
|
224
|
+
const s = String(scopeCandidates[i] || '').toLowerCase()
|
|
225
|
+
if (!s.includes('property-name') && !s.includes('dictionary-key')) continue
|
|
226
|
+
if (s.includes('.json') || s.includes('-json') || s.includes('.yaml') || s.includes('-yaml')) return true
|
|
227
|
+
}
|
|
228
|
+
return false
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const classifyShikiHclKeywordBucketV3 = (lowerScopeCandidates, hasAnyPattern) => {
|
|
232
|
+
const hasAny = (typeof hasAnyPattern === 'function') ? hasAnyPattern : (patterns) => hasAnyScopePattern(lowerScopeCandidates, patterns)
|
|
233
|
+
if (!Array.isArray(lowerScopeCandidates) || lowerScopeCandidates.length === 0) return null
|
|
234
|
+
if (hasAny(['entity.name.type.hcl', 'entity-name-type-hcl'])) return 'title-class'
|
|
235
|
+
if (hasAny(['variable.other.enummember.hcl', 'variable-other-enummember-hcl'])) return 'variable-const'
|
|
236
|
+
if (hasAny(['variable.declaration.hcl', 'variable-declaration-hcl'])) return 'variable-parameter'
|
|
237
|
+
if (hasAny(['variable.other.readwrite.hcl', 'variable-other-readwrite-hcl'])) return 'variable-plain'
|
|
238
|
+
if (hasAny(['storage-type-hcl', 'keyword-operator-assignment-hcl'])) return 'keyword'
|
|
239
|
+
if (hasAny(['constant-numeric', 'number'])) return 'number'
|
|
240
|
+
if (hasAny(['string'])) return 'string'
|
|
241
|
+
if (hasAny(['punctuation'])) return 'punctuation'
|
|
242
|
+
if (hasAny(['meta-block-hcl'])) return 'text'
|
|
243
|
+
return null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const resolveShikiKeywordLang = (lang, scopeCandidates, tok, opt) => {
|
|
247
|
+
const queue = []
|
|
248
|
+
const seen = new Set()
|
|
249
|
+
const customAliasMap = opt && opt.shikiKeywordLangAliases
|
|
250
|
+
const shikiInternalAliasMap = opt && opt._shikiInternalLangAliasMap
|
|
251
|
+
const resolveAlias = (key) => {
|
|
252
|
+
if (customAliasMap && customAliasMap[key]) return customAliasMap[key]
|
|
253
|
+
if (shikiInternalAliasMap && shikiInternalAliasMap[key]) return shikiInternalAliasMap[key]
|
|
254
|
+
return ''
|
|
255
|
+
}
|
|
256
|
+
const pushCandidate = (value) => {
|
|
257
|
+
const key = normalizeShikiKeywordLangKey(value)
|
|
258
|
+
if (!key || seen.has(key)) return
|
|
259
|
+
seen.add(key)
|
|
260
|
+
queue.push(key)
|
|
261
|
+
}
|
|
262
|
+
if (opt && typeof opt.shikiKeywordLangResolver === 'function') {
|
|
263
|
+
try {
|
|
264
|
+
const resolved = opt.shikiKeywordLangResolver(lang, scopeCandidates, tok)
|
|
265
|
+
if (resolved != null && resolved !== '') pushCandidate(resolved)
|
|
266
|
+
} catch (e) {}
|
|
267
|
+
}
|
|
268
|
+
pushCandidate(lang)
|
|
269
|
+
if (Array.isArray(scopeCandidates)) {
|
|
270
|
+
for (let i = 0; i < scopeCandidates.length; i++) {
|
|
271
|
+
const candidates = extractShikiKeywordLangCandidatesFromScope(scopeCandidates[i])
|
|
272
|
+
for (let j = 0; j < candidates.length; j++) pushCandidate(candidates[j])
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
for (let i = 0; i < queue.length; i++) {
|
|
276
|
+
const key = queue[i]
|
|
277
|
+
if (shikiKeywordTokensByLang[key]) return key
|
|
278
|
+
const aliased = resolveAlias(key)
|
|
279
|
+
if (aliased) {
|
|
280
|
+
if (shikiKeywordTokensByLang[aliased]) return aliased
|
|
281
|
+
pushCandidate(aliased)
|
|
282
|
+
}
|
|
283
|
+
const compact = key.replace(/-/g, '')
|
|
284
|
+
if (shikiKeywordTokensByLang[compact]) return compact
|
|
285
|
+
const compactAliased = resolveAlias(compact)
|
|
286
|
+
if (compactAliased) {
|
|
287
|
+
if (shikiKeywordTokensByLang[compactAliased]) return compactAliased
|
|
288
|
+
pushCandidate(compactAliased)
|
|
289
|
+
}
|
|
290
|
+
const parts = key.split('-')
|
|
291
|
+
for (let n = parts.length - 1; n >= 1; n--) {
|
|
292
|
+
const head = parts.slice(0, n).join('-')
|
|
293
|
+
if (shikiKeywordTokensByLang[head]) return head
|
|
294
|
+
const headAliased = resolveAlias(head)
|
|
295
|
+
if (headAliased) {
|
|
296
|
+
if (shikiKeywordTokensByLang[headAliased]) return headAliased
|
|
297
|
+
pushCandidate(headAliased)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return ''
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const classifyShikiKeywordByToken = (content, langKey) => {
|
|
305
|
+
const text = String(content || '')
|
|
306
|
+
const trimmed = text.trim()
|
|
307
|
+
if (!trimmed) return 'text'
|
|
308
|
+
const lower = trimmed.toLowerCase().replace(/\s+/g, ' ')
|
|
309
|
+
if (lower.startsWith('#!')) return 'meta'
|
|
310
|
+
if (langKey !== 'sql' && shikiGlobalLiteralTokenSet.has(lower)) return 'literal'
|
|
311
|
+
if (shikiNumberTokenReg.test(lower)) return 'number'
|
|
312
|
+
const set = shikiKeywordTokensByLang[langKey]
|
|
313
|
+
if (set && set.has(lower)) return 'keyword'
|
|
314
|
+
if (langKey === 'css' && lower.startsWith('@')) return 'keyword'
|
|
315
|
+
if (shikiPunctuationTokenReg.test(trimmed)) return 'punctuation'
|
|
316
|
+
return null
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const buildShikiKeywordContext = (rawScope, tok, lang, opt, preResolvedLangKey = '', tokenContentArg, tokenTrimArg, tokenLowerArg) => {
|
|
320
|
+
const token = (tok && typeof tok === 'object') ? tok : {}
|
|
321
|
+
const tokenContent = (typeof tokenContentArg === 'string') ? tokenContentArg : String(token.content || '')
|
|
322
|
+
const tokenTrim = (typeof tokenTrimArg === 'string') ? tokenTrimArg : tokenContent.trim()
|
|
323
|
+
const tokenLower = (typeof tokenLowerArg === 'string') ? tokenLowerArg : tokenTrim.toLowerCase()
|
|
324
|
+
const scopeCandidates = getShikiScopeCandidates(token, rawScope)
|
|
325
|
+
const langKey = preResolvedLangKey || resolveShikiKeywordLang(lang, scopeCandidates, token, opt)
|
|
326
|
+
const lowerScopeCandidates = toLowerScopeCandidates(scopeCandidates)
|
|
327
|
+
const hasAnyScopePatternCached = createScopePatternMatcher(lowerScopeCandidates)
|
|
328
|
+
return {
|
|
329
|
+
rawScope,
|
|
330
|
+
token,
|
|
331
|
+
lang: String(lang || ''),
|
|
332
|
+
langKey,
|
|
333
|
+
scopeCandidates,
|
|
334
|
+
lowerScopeCandidates,
|
|
335
|
+
tokenContent,
|
|
336
|
+
tokenTrim,
|
|
337
|
+
tokenLower,
|
|
338
|
+
isIdentifier: shikiSimpleIdentifierReg.test(tokenTrim),
|
|
339
|
+
isPunctuation: shikiPunctuationTokenReg.test(tokenTrim),
|
|
340
|
+
hasAnyScopePattern: hasAnyScopePatternCached,
|
|
341
|
+
hasScopePattern: (pattern) => hasAnyScopePatternCached([pattern]),
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const classifyShikiScopeKeywordBaseFromContext = (context) => {
|
|
346
|
+
if (!context || typeof context !== 'object') return 'text'
|
|
347
|
+
let best = 'text'
|
|
348
|
+
let bestScore = shikiKeywordBucketScoreV3.text
|
|
349
|
+
const candidates = Array.isArray(context.scopeCandidates) ? context.scopeCandidates : []
|
|
350
|
+
const lowerScopeCandidates = Array.isArray(context.lowerScopeCandidates) ? context.lowerScopeCandidates : []
|
|
351
|
+
const hasAnyPattern = (context && typeof context.hasAnyScopePattern === 'function')
|
|
352
|
+
? context.hasAnyScopePattern
|
|
353
|
+
: (patterns) => hasAnyScopePattern(lowerScopeCandidates, patterns)
|
|
354
|
+
const hasScope = (context && typeof context.hasScopePattern === 'function')
|
|
355
|
+
? context.hasScopePattern
|
|
356
|
+
: (pattern) => hasAnyPattern([pattern])
|
|
357
|
+
const langKey = String(context.langKey || '')
|
|
358
|
+
const tokenContent = context.tokenContent
|
|
359
|
+
if (langKey === 'hcl' || langKey === 'terraform') {
|
|
360
|
+
const hclBucket = classifyShikiHclKeywordBucketV3(lowerScopeCandidates, hasAnyPattern)
|
|
361
|
+
if (hclBucket) return hclBucket
|
|
362
|
+
}
|
|
363
|
+
if (hasJsonYamlPropertyNameScope(candidates)) return 'type'
|
|
364
|
+
if (langKey === 'yaml' && hasScope('entity.name.tag.yaml')) return 'tag'
|
|
365
|
+
for (let i = 0; i < lowerScopeCandidates.length; i++) {
|
|
366
|
+
const bucket = classifyShikiScopeKeywordSingleV3(lowerScopeCandidates[i])
|
|
367
|
+
const score = shikiKeywordBucketScoreV3[bucket] || shikiKeywordBucketScoreV3.text
|
|
368
|
+
if (score > bestScore) {
|
|
369
|
+
best = bucket
|
|
370
|
+
bestScore = score
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const tokenBucket = classifyShikiKeywordByToken(tokenContent, langKey)
|
|
374
|
+
if (tokenBucket) {
|
|
375
|
+
const tokenScore = (shikiKeywordBucketScoreV3[tokenBucket] || shikiKeywordBucketScoreV3.text) + 1
|
|
376
|
+
if (tokenScore > bestScore) best = tokenBucket
|
|
377
|
+
}
|
|
378
|
+
return best
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const resolveShikiKeywordLangForFence = (lang, opt) => {
|
|
382
|
+
return resolveShikiKeywordLang(lang, null, null, opt)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const classifyShikiScopeKeyword = (rawScope, tok, lang, opt, preResolvedKeywordLang = '') => {
|
|
386
|
+
const token = (tok && typeof tok === 'object') ? tok : {}
|
|
387
|
+
const tokenContent = String(token.content || '')
|
|
388
|
+
const tokenTrim = tokenContent.trim()
|
|
389
|
+
if (!tokenTrim) return 'text'
|
|
390
|
+
const tokenLower = tokenTrim.toLowerCase()
|
|
391
|
+
const context = buildShikiKeywordContext(
|
|
392
|
+
rawScope,
|
|
393
|
+
token,
|
|
394
|
+
lang,
|
|
395
|
+
opt,
|
|
396
|
+
preResolvedKeywordLang,
|
|
397
|
+
tokenContent,
|
|
398
|
+
tokenTrim,
|
|
399
|
+
tokenLower,
|
|
400
|
+
)
|
|
401
|
+
const baseBucket = classifyShikiScopeKeywordBaseFromContext(context)
|
|
402
|
+
const postHelpers = {
|
|
403
|
+
classifyToken: classifyShikiKeywordByToken,
|
|
404
|
+
hasAnyScopePattern: context.hasAnyScopePattern,
|
|
405
|
+
hasScopePattern: context.hasScopePattern,
|
|
406
|
+
}
|
|
407
|
+
const postBucket = applyShikiKeywordLegacyPostRules(baseBucket, context, postHelpers)
|
|
408
|
+
const finalBucket = applyKeywordV4Rules(postBucket, context)
|
|
409
|
+
return finalBucket || postBucket || baseBucket
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export {
|
|
413
|
+
classifyShikiScopeKeyword,
|
|
414
|
+
getShikiRawScopeName,
|
|
415
|
+
normalizeShikiKeywordLangAliasMap,
|
|
416
|
+
resolveShikiKeywordLangForFence,
|
|
417
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import mditRendererFenceCustomHighlight, {
|
|
2
|
+
applyCustomHighlights,
|
|
3
|
+
clearCustomHighlights,
|
|
4
|
+
customHighlightPayloadSchemaVersion,
|
|
5
|
+
customHighlightPayloadSupportedVersions,
|
|
6
|
+
getCustomHighlightPayloadMap,
|
|
7
|
+
observeCustomHighlights,
|
|
8
|
+
renderCustomHighlightPayloadScript,
|
|
9
|
+
renderCustomHighlightScopeStyleTag,
|
|
10
|
+
shouldRuntimeFallback,
|
|
11
|
+
} from '../fence/render-api.js'
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
applyCustomHighlights,
|
|
15
|
+
clearCustomHighlights,
|
|
16
|
+
customHighlightPayloadSchemaVersion,
|
|
17
|
+
customHighlightPayloadSupportedVersions,
|
|
18
|
+
getCustomHighlightPayloadMap,
|
|
19
|
+
observeCustomHighlights,
|
|
20
|
+
renderCustomHighlightPayloadScript,
|
|
21
|
+
renderCustomHighlightScopeStyleTag,
|
|
22
|
+
shouldRuntimeFallback,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default mditRendererFenceCustomHighlight
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyLineEndAlias,
|
|
3
|
+
finalizeCommonFenceOption,
|
|
4
|
+
prepareFenceRenderContext,
|
|
5
|
+
} from '../fence/render-shared.js'
|
|
6
|
+
import {
|
|
7
|
+
renderFenceMarkup,
|
|
8
|
+
} from '../fence/render-markup.js'
|
|
9
|
+
|
|
10
|
+
const mditRendererFenceMarkup = (md, option) => {
|
|
11
|
+
const opt = {
|
|
12
|
+
attrsOrder: ['class', 'id', 'data-*', 'style'],
|
|
13
|
+
setHighlight: true,
|
|
14
|
+
setLineNumber: true,
|
|
15
|
+
setEmphasizeLines: true,
|
|
16
|
+
lineEndSpanThreshold: 0,
|
|
17
|
+
lineEndSpanClass: 'pre-lineend-spacer',
|
|
18
|
+
setPreWrapStyle: true,
|
|
19
|
+
useHighlightPre: false,
|
|
20
|
+
onFenceDecision: null,
|
|
21
|
+
onFenceDecisionTiming: false,
|
|
22
|
+
sampLang: 'shell,console',
|
|
23
|
+
langPrefix: md.options.langPrefix || 'language-',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (option) {
|
|
27
|
+
Object.assign(opt, option)
|
|
28
|
+
applyLineEndAlias(opt, option)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
delete opt.customHighlight
|
|
32
|
+
finalizeCommonFenceOption(opt)
|
|
33
|
+
|
|
34
|
+
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
|
|
35
|
+
const context = prepareFenceRenderContext(tokens, idx, opt)
|
|
36
|
+
return renderFenceMarkup(context, md, opt, slf)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default mditRendererFenceMarkup
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fallbackOnDefault = ['api-unsupported', 'provider-error', 'range-invalid', 'apply-error']
|
|
2
|
+
|
|
3
|
+
const customHighlightDataEnvKey = 'rendererFenceCustomHighlights'
|
|
4
|
+
const customHighlightSeqEnvKey = '__rendererFenceCHSeq'
|
|
5
|
+
const customHighlightPreAttr = 'data-pre-highlight'
|
|
6
|
+
const customHighlightAppliedAttr = 'data-pre-highlight-applied'
|
|
7
|
+
const customHighlightPreSelector = `pre[${customHighlightPreAttr}]`
|
|
8
|
+
const customHighlightCodeSelector = `${customHighlightPreSelector} > code, ${customHighlightPreSelector} > samp`
|
|
9
|
+
const customHighlightAppliedSelector = `pre[${customHighlightAppliedAttr}]`
|
|
10
|
+
const customHighlightDataScriptId = 'pre-highlight-data'
|
|
11
|
+
const customHighlightStyleTagId = 'pre-highlight-style'
|
|
12
|
+
const customHighlightEnvInitRuleName = 'renderer_fence_custom_highlight_env_init'
|
|
13
|
+
|
|
14
|
+
const runtimeFallbackReasonSet = new Set(['api-unsupported', 'apply-error'])
|
|
15
|
+
|
|
16
|
+
const customHighlightPayloadSchemaVersion = 1
|
|
17
|
+
const customHighlightPayloadSupportedVersions = [customHighlightPayloadSchemaVersion]
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
customHighlightAppliedAttr,
|
|
21
|
+
customHighlightAppliedSelector,
|
|
22
|
+
customHighlightCodeSelector,
|
|
23
|
+
customHighlightDataEnvKey,
|
|
24
|
+
customHighlightDataScriptId,
|
|
25
|
+
customHighlightEnvInitRuleName,
|
|
26
|
+
customHighlightPayloadSchemaVersion,
|
|
27
|
+
customHighlightPayloadSupportedVersions,
|
|
28
|
+
customHighlightPreAttr,
|
|
29
|
+
customHighlightPreSelector,
|
|
30
|
+
customHighlightSeqEnvKey,
|
|
31
|
+
customHighlightStyleTagId,
|
|
32
|
+
fallbackOnDefault,
|
|
33
|
+
runtimeFallbackReasonSet,
|
|
34
|
+
}
|