@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.
@@ -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,11 @@
1
+ import {
2
+ applyCustomHighlights,
3
+ clearCustomHighlights,
4
+ observeCustomHighlights,
5
+ } from '../fence/render-api-runtime.js'
6
+
7
+ export {
8
+ applyCustomHighlights,
9
+ clearCustomHighlights,
10
+ observeCustomHighlights,
11
+ }
@@ -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
+ }