mini-shiki 3.23.0 → 4.0.1

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.
Files changed (2) hide show
  1. package/dist/shiki.js +454 -693
  2. package/package.json +5 -5
package/dist/shiki.js CHANGED
@@ -3,9 +3,9 @@ import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { createOnigurumaEngine, loadWasm } from "@shikijs/engine-oniguruma";
5
5
  import { ShikiError } from "@shikijs/types";
6
- import { EncodedTokenMetadata, FontStyle, INITIAL, Registry, Theme } from "@shikijs/vscode-textmate";
6
+ import { EncodedTokenMetadata, INITIAL, Registry, Theme } from "@shikijs/vscode-textmate";
7
7
 
8
- //#region node_modules/@shikijs/core/dist/index.mjs
8
+ //#region node_modules/@shikijs/primitive/dist/index.mjs
9
9
  function resolveColorReplacements(theme, options) {
10
10
  const replacements = typeof theme === "string" ? {} : { ...theme.colorReplacements };
11
11
  const themeName = typeof theme === "string" ? theme : theme.name;
@@ -20,9 +20,17 @@ function applyColorReplacements(color, replacements) {
20
20
  function toArray(x) {
21
21
  return Array.isArray(x) ? x : [x];
22
22
  }
23
+ /**
24
+ * Normalize a getter to a promise.
25
+ */
23
26
  async function normalizeGetter(p) {
24
27
  return Promise.resolve(typeof p === "function" ? p() : p).then((r) => r.default || r);
25
28
  }
29
+ /**
30
+ * Check if the language is plaintext that is ignored by Shiki.
31
+ *
32
+ * Hard-coded plain text languages: `plaintext`, `txt`, `text`, `plain`.
33
+ */
26
34
  function isPlainLang(lang) {
27
35
  return !lang || [
28
36
  "plaintext",
@@ -31,15 +39,46 @@ function isPlainLang(lang) {
31
39
  "plain"
32
40
  ].includes(lang);
33
41
  }
42
+ /**
43
+ * Check if the language is specially handled or bypassed by Shiki.
44
+ *
45
+ * Hard-coded languages: `ansi` and plaintexts like `plaintext`, `txt`, `text`, `plain`.
46
+ */
34
47
  function isSpecialLang(lang) {
35
48
  return lang === "ansi" || isPlainLang(lang);
36
49
  }
50
+ /**
51
+ * Check if the theme is specially handled or bypassed by Shiki.
52
+ *
53
+ * Hard-coded themes: `none`.
54
+ */
37
55
  function isNoneTheme(theme) {
38
56
  return theme === "none";
39
57
  }
58
+ /**
59
+ * Check if the theme is specially handled or bypassed by Shiki.
60
+ *
61
+ * Hard-coded themes: `none`.
62
+ */
40
63
  function isSpecialTheme(theme) {
41
64
  return isNoneTheme(theme);
42
65
  }
66
+ /**
67
+ * Split a string into lines, each line preserves the line ending.
68
+ *
69
+ * @param code - The code string to split into lines
70
+ * @param preserveEnding - Whether to preserve line endings in the result
71
+ * @returns Array of tuples containing [line content, offset index]
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * splitLines('hello\nworld', false)
76
+ * // => [['hello', 0], ['world', 6]]
77
+ *
78
+ * splitLines('hello\nworld', true)
79
+ * // => [['hello\n', 0], ['world', 6]]
80
+ * ```
81
+ */
43
82
  function splitLines(code, preserveEnding = false) {
44
83
  if (code.length === 0) return [["", 0]];
45
84
  const parts = code.split(/(\r?\n)/g);
@@ -53,616 +92,9 @@ function splitLines(code, preserveEnding = false) {
53
92
  }
54
93
  return lines;
55
94
  }
56
- const _grammarStateMap = /* @__PURE__ */ new WeakMap();
57
- function setLastGrammarStateToMap(keys, state) {
58
- _grammarStateMap.set(keys, state);
59
- }
60
- function getLastGrammarStateFromMap(keys) {
61
- return _grammarStateMap.get(keys);
62
- }
63
- var GrammarState = class GrammarState {
64
- /**
65
- * Theme to Stack mapping
66
- */
67
- _stacks = {};
68
- lang;
69
- get themes() {
70
- return Object.keys(this._stacks);
71
- }
72
- get theme() {
73
- return this.themes[0];
74
- }
75
- get _stack() {
76
- return this._stacks[this.theme];
77
- }
78
- /**
79
- * Static method to create a initial grammar state.
80
- */
81
- static initial(lang, themes) {
82
- return new GrammarState(Object.fromEntries(toArray(themes).map((theme) => [theme, INITIAL])), lang);
83
- }
84
- constructor(...args) {
85
- if (args.length === 2) {
86
- const [stacksMap, lang] = args;
87
- this.lang = lang;
88
- this._stacks = stacksMap;
89
- } else {
90
- const [stack, lang, theme] = args;
91
- this.lang = lang;
92
- this._stacks = { [theme]: stack };
93
- }
94
- }
95
- /**
96
- * Get the internal stack object.
97
- * @internal
98
- */
99
- getInternalStack(theme = this.theme) {
100
- return this._stacks[theme];
101
- }
102
- getScopes(theme = this.theme) {
103
- return getScopes(this._stacks[theme]);
104
- }
105
- toJSON() {
106
- return {
107
- lang: this.lang,
108
- theme: this.theme,
109
- themes: this.themes,
110
- scopes: this.getScopes()
111
- };
112
- }
113
- };
114
- function getScopes(stack) {
115
- const scopes = [];
116
- const visited = /* @__PURE__ */ new Set();
117
- function pushScope(stack2) {
118
- if (visited.has(stack2)) return;
119
- visited.add(stack2);
120
- const name = stack2?.nameScopesList?.scopeName;
121
- if (name) scopes.push(name);
122
- if (stack2.parent) pushScope(stack2.parent);
123
- }
124
- pushScope(stack);
125
- return scopes;
126
- }
127
- function getGrammarStack(state, theme) {
128
- if (!(state instanceof GrammarState)) throw new ShikiError("Invalid grammar state");
129
- return state.getInternalStack(theme);
130
- }
131
- var namedColors = [
132
- "black",
133
- "red",
134
- "green",
135
- "yellow",
136
- "blue",
137
- "magenta",
138
- "cyan",
139
- "white",
140
- "brightBlack",
141
- "brightRed",
142
- "brightGreen",
143
- "brightYellow",
144
- "brightBlue",
145
- "brightMagenta",
146
- "brightCyan",
147
- "brightWhite"
148
- ];
149
- var decorations = {
150
- 1: "bold",
151
- 2: "dim",
152
- 3: "italic",
153
- 4: "underline",
154
- 7: "reverse",
155
- 8: "hidden",
156
- 9: "strikethrough"
157
- };
158
- function findSequence(value, position) {
159
- const nextEscape = value.indexOf("\x1B", position);
160
- if (nextEscape !== -1) {
161
- if (value[nextEscape + 1] === "[") {
162
- const nextClose = value.indexOf("m", nextEscape);
163
- if (nextClose !== -1) return {
164
- sequence: value.substring(nextEscape + 2, nextClose).split(";"),
165
- startPosition: nextEscape,
166
- position: nextClose + 1
167
- };
168
- }
169
- }
170
- return { position: value.length };
171
- }
172
- function parseColor(sequence) {
173
- const colorMode = sequence.shift();
174
- if (colorMode === "2") {
175
- const rgb = sequence.splice(0, 3).map((x) => Number.parseInt(x));
176
- if (rgb.length !== 3 || rgb.some((x) => Number.isNaN(x))) return;
177
- return {
178
- type: "rgb",
179
- rgb
180
- };
181
- } else if (colorMode === "5") {
182
- const index = sequence.shift();
183
- if (index) return {
184
- type: "table",
185
- index: Number(index)
186
- };
187
- }
188
- }
189
- function parseSequence(sequence) {
190
- const commands = [];
191
- while (sequence.length > 0) {
192
- const code = sequence.shift();
193
- if (!code) continue;
194
- const codeInt = Number.parseInt(code);
195
- if (Number.isNaN(codeInt)) continue;
196
- if (codeInt === 0) commands.push({ type: "resetAll" });
197
- else if (codeInt <= 9) {
198
- const decoration = decorations[codeInt];
199
- if (decoration) commands.push({
200
- type: "setDecoration",
201
- value: decorations[codeInt]
202
- });
203
- } else if (codeInt <= 29) {
204
- const decoration = decorations[codeInt - 20];
205
- if (decoration) {
206
- commands.push({
207
- type: "resetDecoration",
208
- value: decoration
209
- });
210
- if (decoration === "dim") commands.push({
211
- type: "resetDecoration",
212
- value: "bold"
213
- });
214
- }
215
- } else if (codeInt <= 37) commands.push({
216
- type: "setForegroundColor",
217
- value: {
218
- type: "named",
219
- name: namedColors[codeInt - 30]
220
- }
221
- });
222
- else if (codeInt === 38) {
223
- const color = parseColor(sequence);
224
- if (color) commands.push({
225
- type: "setForegroundColor",
226
- value: color
227
- });
228
- } else if (codeInt === 39) commands.push({ type: "resetForegroundColor" });
229
- else if (codeInt <= 47) commands.push({
230
- type: "setBackgroundColor",
231
- value: {
232
- type: "named",
233
- name: namedColors[codeInt - 40]
234
- }
235
- });
236
- else if (codeInt === 48) {
237
- const color = parseColor(sequence);
238
- if (color) commands.push({
239
- type: "setBackgroundColor",
240
- value: color
241
- });
242
- } else if (codeInt === 49) commands.push({ type: "resetBackgroundColor" });
243
- else if (codeInt === 53) commands.push({
244
- type: "setDecoration",
245
- value: "overline"
246
- });
247
- else if (codeInt === 55) commands.push({
248
- type: "resetDecoration",
249
- value: "overline"
250
- });
251
- else if (codeInt >= 90 && codeInt <= 97) commands.push({
252
- type: "setForegroundColor",
253
- value: {
254
- type: "named",
255
- name: namedColors[codeInt - 90 + 8]
256
- }
257
- });
258
- else if (codeInt >= 100 && codeInt <= 107) commands.push({
259
- type: "setBackgroundColor",
260
- value: {
261
- type: "named",
262
- name: namedColors[codeInt - 100 + 8]
263
- }
264
- });
265
- }
266
- return commands;
267
- }
268
- function createAnsiSequenceParser() {
269
- let foreground = null;
270
- let background = null;
271
- let decorations2 = /* @__PURE__ */ new Set();
272
- return { parse(value) {
273
- const tokens = [];
274
- let position = 0;
275
- do {
276
- const findResult = findSequence(value, position);
277
- const text = findResult.sequence ? value.substring(position, findResult.startPosition) : value.substring(position);
278
- if (text.length > 0) tokens.push({
279
- value: text,
280
- foreground,
281
- background,
282
- decorations: new Set(decorations2)
283
- });
284
- if (findResult.sequence) {
285
- const commands = parseSequence(findResult.sequence);
286
- for (const styleToken of commands) if (styleToken.type === "resetAll") {
287
- foreground = null;
288
- background = null;
289
- decorations2.clear();
290
- } else if (styleToken.type === "resetForegroundColor") foreground = null;
291
- else if (styleToken.type === "resetBackgroundColor") background = null;
292
- else if (styleToken.type === "resetDecoration") decorations2.delete(styleToken.value);
293
- for (const styleToken of commands) if (styleToken.type === "setForegroundColor") foreground = styleToken.value;
294
- else if (styleToken.type === "setBackgroundColor") background = styleToken.value;
295
- else if (styleToken.type === "setDecoration") decorations2.add(styleToken.value);
296
- }
297
- position = findResult.position;
298
- } while (position < value.length);
299
- return tokens;
300
- } };
301
- }
302
- var defaultNamedColorsMap = {
303
- black: "#000000",
304
- red: "#bb0000",
305
- green: "#00bb00",
306
- yellow: "#bbbb00",
307
- blue: "#0000bb",
308
- magenta: "#ff00ff",
309
- cyan: "#00bbbb",
310
- white: "#eeeeee",
311
- brightBlack: "#555555",
312
- brightRed: "#ff5555",
313
- brightGreen: "#00ff00",
314
- brightYellow: "#ffff55",
315
- brightBlue: "#5555ff",
316
- brightMagenta: "#ff55ff",
317
- brightCyan: "#55ffff",
318
- brightWhite: "#ffffff"
319
- };
320
- function createColorPalette(namedColorsMap = defaultNamedColorsMap) {
321
- function namedColor(name) {
322
- return namedColorsMap[name];
323
- }
324
- function rgbColor(rgb) {
325
- return `#${rgb.map((x) => Math.max(0, Math.min(x, 255)).toString(16).padStart(2, "0")).join("")}`;
326
- }
327
- let colorTable;
328
- function getColorTable() {
329
- if (colorTable) return colorTable;
330
- colorTable = [];
331
- for (let i = 0; i < namedColors.length; i++) colorTable.push(namedColor(namedColors[i]));
332
- let levels = [
333
- 0,
334
- 95,
335
- 135,
336
- 175,
337
- 215,
338
- 255
339
- ];
340
- for (let r = 0; r < 6; r++) for (let g = 0; g < 6; g++) for (let b = 0; b < 6; b++) colorTable.push(rgbColor([
341
- levels[r],
342
- levels[g],
343
- levels[b]
344
- ]));
345
- let level = 8;
346
- for (let i = 0; i < 24; i++, level += 10) colorTable.push(rgbColor([
347
- level,
348
- level,
349
- level
350
- ]));
351
- return colorTable;
352
- }
353
- function tableColor(index) {
354
- return getColorTable()[index];
355
- }
356
- function value(color) {
357
- switch (color.type) {
358
- case "named": return namedColor(color.name);
359
- case "rgb": return rgbColor(color.rgb);
360
- case "table": return tableColor(color.index);
361
- }
362
- }
363
- return { value };
364
- }
365
- const defaultAnsiColors = {
366
- black: "#000000",
367
- red: "#cd3131",
368
- green: "#0DBC79",
369
- yellow: "#E5E510",
370
- blue: "#2472C8",
371
- magenta: "#BC3FBC",
372
- cyan: "#11A8CD",
373
- white: "#E5E5E5",
374
- brightBlack: "#666666",
375
- brightRed: "#F14C4C",
376
- brightGreen: "#23D18B",
377
- brightYellow: "#F5F543",
378
- brightBlue: "#3B8EEA",
379
- brightMagenta: "#D670D6",
380
- brightCyan: "#29B8DB",
381
- brightWhite: "#FFFFFF"
382
- };
383
- function tokenizeAnsiWithTheme(theme, fileContents, options) {
384
- const colorReplacements = resolveColorReplacements(theme, options);
385
- const lines = splitLines(fileContents);
386
- const ansiPalette = Object.fromEntries(namedColors.map((name) => {
387
- const key = `terminal.ansi${name[0].toUpperCase()}${name.substring(1)}`;
388
- const themeColor = theme.colors?.[key];
389
- return [name, themeColor || defaultAnsiColors[name]];
390
- }));
391
- const colorPalette = createColorPalette(ansiPalette);
392
- const parser = createAnsiSequenceParser();
393
- return lines.map((line) => parser.parse(line[0]).map((token) => {
394
- let color;
395
- let bgColor;
396
- if (token.decorations.has("reverse")) {
397
- color = token.background ? colorPalette.value(token.background) : theme.bg;
398
- bgColor = token.foreground ? colorPalette.value(token.foreground) : theme.fg;
399
- } else {
400
- color = token.foreground ? colorPalette.value(token.foreground) : theme.fg;
401
- bgColor = token.background ? colorPalette.value(token.background) : void 0;
402
- }
403
- color = applyColorReplacements(color, colorReplacements);
404
- bgColor = applyColorReplacements(bgColor, colorReplacements);
405
- if (token.decorations.has("dim")) color = dimColor(color);
406
- let fontStyle = FontStyle.None;
407
- if (token.decorations.has("bold")) fontStyle |= FontStyle.Bold;
408
- if (token.decorations.has("italic")) fontStyle |= FontStyle.Italic;
409
- if (token.decorations.has("underline")) fontStyle |= FontStyle.Underline;
410
- if (token.decorations.has("strikethrough")) fontStyle |= FontStyle.Strikethrough;
411
- return {
412
- content: token.value,
413
- offset: line[1],
414
- color,
415
- bgColor,
416
- fontStyle
417
- };
418
- }));
419
- }
420
- function dimColor(color) {
421
- const hexMatch = color.match(/#([0-9a-f]{3,8})/i);
422
- if (hexMatch) {
423
- const hex = hexMatch[1];
424
- if (hex.length === 8) {
425
- const alpha = Math.round(Number.parseInt(hex.slice(6, 8), 16) / 2).toString(16).padStart(2, "0");
426
- return `#${hex.slice(0, 6)}${alpha}`;
427
- } else if (hex.length === 6) return `#${hex}80`;
428
- else if (hex.length === 4) {
429
- const r = hex[0];
430
- const g = hex[1];
431
- const b = hex[2];
432
- const a = hex[3];
433
- const alpha = Math.round(Number.parseInt(`${a}${a}`, 16) / 2).toString(16).padStart(2, "0");
434
- return `#${r}${r}${g}${g}${b}${b}${alpha}`;
435
- } else if (hex.length === 3) {
436
- const r = hex[0];
437
- const g = hex[1];
438
- const b = hex[2];
439
- return `#${r}${r}${g}${g}${b}${b}80`;
440
- }
441
- }
442
- const cssVarMatch = color.match(/var\((--[\w-]+-ansi-[\w-]+)\)/);
443
- if (cssVarMatch) return `var(${cssVarMatch[1]}-dim)`;
444
- return color;
445
- }
446
- function codeToTokensBase(internal, code, options = {}) {
447
- const { theme: themeName = internal.getLoadedThemes()[0] } = options;
448
- const lang = internal.resolveLangAlias(options.lang || "text");
449
- if (isPlainLang(lang) || isNoneTheme(themeName)) return splitLines(code).map((line) => [{
450
- content: line[0],
451
- offset: line[1]
452
- }]);
453
- const { theme, colorMap } = internal.setTheme(themeName);
454
- if (lang === "ansi") return tokenizeAnsiWithTheme(theme, code, options);
455
- const _grammar = internal.getLanguage(options.lang || "text");
456
- if (options.grammarState) {
457
- if (options.grammarState.lang !== _grammar.name) throw new ShikiError(`Grammar state language "${options.grammarState.lang}" does not match highlight language "${_grammar.name}"`);
458
- if (!options.grammarState.themes.includes(theme.name)) throw new ShikiError(`Grammar state themes "${options.grammarState.themes}" do not contain highlight theme "${theme.name}"`);
459
- }
460
- return tokenizeWithTheme(code, _grammar, theme, colorMap, options);
461
- }
462
- function tokenizeWithTheme(code, grammar, theme, colorMap, options) {
463
- const result = _tokenizeWithTheme(code, grammar, theme, colorMap, options);
464
- const grammarState = new GrammarState(result.stateStack, grammar.name, theme.name);
465
- setLastGrammarStateToMap(result.tokens, grammarState);
466
- return result.tokens;
467
- }
468
- function _tokenizeWithTheme(code, grammar, theme, colorMap, options) {
469
- const colorReplacements = resolveColorReplacements(theme, options);
470
- const { tokenizeMaxLineLength = 0, tokenizeTimeLimit = 500 } = options;
471
- const lines = splitLines(code);
472
- let stateStack = options.grammarState ? getGrammarStack(options.grammarState, theme.name) ?? INITIAL : options.grammarContextCode != null ? _tokenizeWithTheme(options.grammarContextCode, grammar, theme, colorMap, {
473
- ...options,
474
- grammarState: void 0,
475
- grammarContextCode: void 0
476
- }).stateStack : INITIAL;
477
- let actual = [];
478
- const final = [];
479
- for (let i = 0, len = lines.length; i < len; i++) {
480
- const [line, lineOffset] = lines[i];
481
- if (line === "") {
482
- actual = [];
483
- final.push([]);
484
- continue;
485
- }
486
- if (tokenizeMaxLineLength > 0 && line.length >= tokenizeMaxLineLength) {
487
- actual = [];
488
- final.push([{
489
- content: line,
490
- offset: lineOffset,
491
- color: "",
492
- fontStyle: 0
493
- }]);
494
- continue;
495
- }
496
- let resultWithScopes;
497
- let tokensWithScopes;
498
- let tokensWithScopesIndex;
499
- if (options.includeExplanation) {
500
- resultWithScopes = grammar.tokenizeLine(line, stateStack, tokenizeTimeLimit);
501
- tokensWithScopes = resultWithScopes.tokens;
502
- tokensWithScopesIndex = 0;
503
- }
504
- const result = grammar.tokenizeLine2(line, stateStack, tokenizeTimeLimit);
505
- const tokensLength = result.tokens.length / 2;
506
- for (let j = 0; j < tokensLength; j++) {
507
- const startIndex = result.tokens[2 * j];
508
- const nextStartIndex = j + 1 < tokensLength ? result.tokens[2 * j + 2] : line.length;
509
- if (startIndex === nextStartIndex) continue;
510
- const metadata = result.tokens[2 * j + 1];
511
- const color = applyColorReplacements(colorMap[EncodedTokenMetadata.getForeground(metadata)], colorReplacements);
512
- const fontStyle = EncodedTokenMetadata.getFontStyle(metadata);
513
- const token = {
514
- content: line.substring(startIndex, nextStartIndex),
515
- offset: lineOffset + startIndex,
516
- color,
517
- fontStyle
518
- };
519
- if (options.includeExplanation) {
520
- const themeSettingsSelectors = [];
521
- if (options.includeExplanation !== "scopeName") for (const setting of theme.settings) {
522
- let selectors;
523
- switch (typeof setting.scope) {
524
- case "string":
525
- selectors = setting.scope.split(/,/).map((scope) => scope.trim());
526
- break;
527
- case "object":
528
- selectors = setting.scope;
529
- break;
530
- default: continue;
531
- }
532
- themeSettingsSelectors.push({
533
- settings: setting,
534
- selectors: selectors.map((selector) => selector.split(/ /))
535
- });
536
- }
537
- token.explanation = [];
538
- let offset = 0;
539
- while (startIndex + offset < nextStartIndex) {
540
- const tokenWithScopes = tokensWithScopes[tokensWithScopesIndex];
541
- const tokenWithScopesText = line.substring(tokenWithScopes.startIndex, tokenWithScopes.endIndex);
542
- offset += tokenWithScopesText.length;
543
- token.explanation.push({
544
- content: tokenWithScopesText,
545
- scopes: options.includeExplanation === "scopeName" ? explainThemeScopesNameOnly(tokenWithScopes.scopes) : explainThemeScopesFull(themeSettingsSelectors, tokenWithScopes.scopes)
546
- });
547
- tokensWithScopesIndex += 1;
548
- }
549
- }
550
- actual.push(token);
551
- }
552
- final.push(actual);
553
- actual = [];
554
- stateStack = result.ruleStack;
555
- }
556
- return {
557
- tokens: final,
558
- stateStack
559
- };
560
- }
561
- function explainThemeScopesNameOnly(scopes) {
562
- return scopes.map((scope) => ({ scopeName: scope }));
563
- }
564
- function explainThemeScopesFull(themeSelectors, scopes) {
565
- const result = [];
566
- for (let i = 0, len = scopes.length; i < len; i++) {
567
- const scope = scopes[i];
568
- result[i] = {
569
- scopeName: scope,
570
- themeMatches: explainThemeScope(themeSelectors, scope, scopes.slice(0, i))
571
- };
572
- }
573
- return result;
574
- }
575
- function matchesOne(selector, scope) {
576
- return selector === scope || scope.substring(0, selector.length) === selector && scope[selector.length] === ".";
577
- }
578
- function matches(selectors, scope, parentScopes) {
579
- if (!matchesOne(selectors[selectors.length - 1], scope)) return false;
580
- let selectorParentIndex = selectors.length - 2;
581
- let parentIndex = parentScopes.length - 1;
582
- while (selectorParentIndex >= 0 && parentIndex >= 0) {
583
- if (matchesOne(selectors[selectorParentIndex], parentScopes[parentIndex])) selectorParentIndex -= 1;
584
- parentIndex -= 1;
585
- }
586
- if (selectorParentIndex === -1) return true;
587
- return false;
588
- }
589
- function explainThemeScope(themeSettingsSelectors, scope, parentScopes) {
590
- const result = [];
591
- for (const { selectors, settings } of themeSettingsSelectors) for (const selectorPieces of selectors) if (matches(selectorPieces, scope, parentScopes)) {
592
- result.push(settings);
593
- break;
594
- }
595
- return result;
596
- }
597
- function codeToTokensWithThemes(internal, code, options) {
598
- const themes = Object.entries(options.themes).filter((i) => i[1]).map((i) => ({
599
- color: i[0],
600
- theme: i[1]
601
- }));
602
- const themedTokens = themes.map((t) => {
603
- const tokens2 = codeToTokensBase(internal, code, {
604
- ...options,
605
- theme: t.theme
606
- });
607
- const state = getLastGrammarStateFromMap(tokens2);
608
- const theme = typeof t.theme === "string" ? t.theme : t.theme.name;
609
- return {
610
- tokens: tokens2,
611
- state,
612
- theme
613
- };
614
- });
615
- const tokens = syncThemesTokenization(...themedTokens.map((i) => i.tokens));
616
- const mergedTokens = tokens[0].map((line, lineIdx) => line.map((_token, tokenIdx) => {
617
- const mergedToken = {
618
- content: _token.content,
619
- variants: {},
620
- offset: _token.offset
621
- };
622
- if ("includeExplanation" in options && options.includeExplanation) mergedToken.explanation = _token.explanation;
623
- tokens.forEach((t, themeIdx) => {
624
- const { content: _, explanation: __, offset: ___,...styles } = t[lineIdx][tokenIdx];
625
- mergedToken.variants[themes[themeIdx].color] = styles;
626
- });
627
- return mergedToken;
628
- }));
629
- const mergedGrammarState = themedTokens[0].state ? new GrammarState(Object.fromEntries(themedTokens.map((s) => [s.theme, s.state?.getInternalStack(s.theme)])), themedTokens[0].state.lang) : void 0;
630
- if (mergedGrammarState) setLastGrammarStateToMap(mergedTokens, mergedGrammarState);
631
- return mergedTokens;
632
- }
633
- function syncThemesTokenization(...themes) {
634
- const outThemes = themes.map(() => []);
635
- const count = themes.length;
636
- for (let i = 0; i < themes[0].length; i++) {
637
- const lines = themes.map((t) => t[i]);
638
- const outLines = outThemes.map(() => []);
639
- outThemes.forEach((t, i2) => t.push(outLines[i2]));
640
- const indexes = lines.map(() => 0);
641
- const current = lines.map((l) => l[0]);
642
- while (current.every((t) => t)) {
643
- const minLength = Math.min(...current.map((t) => t.content.length));
644
- for (let n = 0; n < count; n++) {
645
- const token = current[n];
646
- if (token.content.length === minLength) {
647
- outLines[n].push(token);
648
- indexes[n] += 1;
649
- current[n] = lines[n][indexes[n]];
650
- } else {
651
- outLines[n].push({
652
- ...token,
653
- content: token.content.slice(0, minLength)
654
- });
655
- current[n] = {
656
- ...token,
657
- content: token.content.slice(minLength),
658
- offset: token.offset + minLength
659
- };
660
- }
661
- }
662
- }
663
- }
664
- return outThemes;
665
- }
95
+ /**
96
+ * https://github.com/microsoft/vscode/blob/f7f05dee53fb33fe023db2e06e30a89d3094488f/src/vs/platform/theme/common/colorRegistry.ts#L258-L268
97
+ */
666
98
  const VSCODE_FALLBACK_EDITOR_FG = {
667
99
  light: "#333333",
668
100
  dark: "#bbbbbb"
@@ -672,6 +104,9 @@ const VSCODE_FALLBACK_EDITOR_BG = {
672
104
  dark: "#1e1e1e"
673
105
  };
674
106
  const RESOLVED_KEY = "__shiki_resolved";
107
+ /**
108
+ * Normalize a textmate theme to shiki theme
109
+ */
675
110
  function normalizeTheme(rawTheme) {
676
111
  if (rawTheme?.[RESOLVED_KEY]) return rawTheme;
677
112
  const theme = { ...rawTheme };
@@ -684,11 +119,25 @@ function normalizeTheme(rawTheme) {
684
119
  theme.settings ||= [];
685
120
  let { bg, fg } = theme;
686
121
  if (!bg || !fg) {
122
+ /**
123
+ * First try:
124
+ * Theme might contain a global `tokenColor` without `name` or `scope`
125
+ * Used as default value for foreground/background
126
+ */
687
127
  const globalSetting = theme.settings ? theme.settings.find((s) => !s.name && !s.scope) : void 0;
688
128
  if (globalSetting?.settings?.foreground) fg = globalSetting.settings.foreground;
689
129
  if (globalSetting?.settings?.background) bg = globalSetting.settings.background;
130
+ /**
131
+ * Second try:
132
+ * If there's no global `tokenColor` without `name` or `scope`
133
+ * Use `editor.foreground` and `editor.background`
134
+ */
690
135
  if (!fg && theme?.colors?.["editor.foreground"]) fg = theme.colors["editor.foreground"];
691
136
  if (!bg && theme?.colors?.["editor.background"]) bg = theme.colors["editor.background"];
137
+ /**
138
+ * Last try:
139
+ * If there's no fg/bg color specified in theme, use default
140
+ */
692
141
  if (!fg) fg = theme.type === "light" ? VSCODE_FALLBACK_EDITOR_FG.light : VSCODE_FALLBACK_EDITOR_FG.dark;
693
142
  if (!bg) bg = theme.type === "light" ? VSCODE_FALLBACK_EDITOR_BG.light : VSCODE_FALLBACK_EDITOR_BG.dark;
694
143
  theme.fg = fg;
@@ -742,40 +191,35 @@ function normalizeTheme(rawTheme) {
742
191
  });
743
192
  return theme;
744
193
  }
194
+ /**
195
+ * Resolve
196
+ */
745
197
  async function resolveLangs(langs) {
746
198
  return Array.from(new Set((await Promise.all(langs.filter((l) => !isSpecialLang(l)).map(async (lang) => await normalizeGetter(lang).then((r) => Array.isArray(r) ? r : [r])))).flat()));
747
199
  }
748
200
  async function resolveThemes(themes) {
749
- const resolved = await Promise.all(themes.map(async (theme) => isSpecialTheme(theme) ? null : normalizeTheme(await normalizeGetter(theme))));
750
- return resolved.filter((i) => !!i);
751
- }
752
- let _emitDeprecation = 3;
753
- let _emitError = false;
754
- function warnDeprecated(message, version = 3) {
755
- if (!_emitDeprecation) return;
756
- if (typeof _emitDeprecation === "number" && version > _emitDeprecation) return;
757
- if (_emitError) throw new Error(`[SHIKI DEPRECATE]: ${message}`);
758
- else console.trace(`[SHIKI DEPRECATE]: ${message}`);
201
+ return (await Promise.all(themes.map(async (theme) => isSpecialTheme(theme) ? null : normalizeTheme(await normalizeGetter(theme))))).filter((i) => !!i);
759
202
  }
760
- var ShikiError$1 = class extends Error {
761
- constructor(message) {
762
- super(message);
763
- this.name = "ShikiError";
764
- }
765
- };
766
203
  function resolveLangAlias(name, alias) {
767
204
  if (!alias) return name;
768
205
  if (alias[name]) {
769
- const resolved = /* @__PURE__ */ new Set([name]);
206
+ const resolved = new Set([name]);
770
207
  while (alias[name]) {
771
208
  name = alias[name];
772
- if (resolved.has(name)) throw new ShikiError$1(`Circular alias \`${Array.from(resolved).join(" -> ")} -> ${name}\``);
209
+ if (resolved.has(name)) throw new ShikiError(`Circular alias \`${Array.from(resolved).join(" -> ")} -> ${name}\``);
773
210
  resolved.add(name);
774
211
  }
775
212
  }
776
213
  return name;
777
214
  }
778
215
  var Registry$1 = class extends Registry {
216
+ _resolvedThemes = /* @__PURE__ */ new Map();
217
+ _resolvedGrammars = /* @__PURE__ */ new Map();
218
+ _langMap = /* @__PURE__ */ new Map();
219
+ _langGraph = /* @__PURE__ */ new Map();
220
+ _textmateThemeCache = /* @__PURE__ */ new WeakMap();
221
+ _loadedThemesCache = null;
222
+ _loadedLanguagesCache = null;
779
223
  constructor(_resolver, _themes, _langs, _alias = {}) {
780
224
  super(_resolver);
781
225
  this._resolver = _resolver;
@@ -785,13 +229,6 @@ var Registry$1 = class extends Registry {
785
229
  this._themes.map((t) => this.loadTheme(t));
786
230
  this.loadLanguages(this._langs);
787
231
  }
788
- _resolvedThemes = /* @__PURE__ */ new Map();
789
- _resolvedGrammars = /* @__PURE__ */ new Map();
790
- _langMap = /* @__PURE__ */ new Map();
791
- _langGraph = /* @__PURE__ */ new Map();
792
- _textmateThemeCache = /* @__PURE__ */ new WeakMap();
793
- _loadedThemesCache = null;
794
- _loadedLanguagesCache = null;
795
232
  getTheme(theme) {
796
233
  if (typeof theme === "string") return this._resolvedThemes.get(theme);
797
234
  else return this.loadTheme(theme);
@@ -859,16 +296,15 @@ var Registry$1 = class extends Registry {
859
296
  if (missingLangs.length) {
860
297
  const dependents = langsGraphArray.filter(([_, lang]) => {
861
298
  if (!lang) return false;
862
- const embedded = lang.embeddedLanguages || lang.embeddedLangs;
863
- return embedded?.some((l) => missingLangs.map(([name]) => name).includes(l));
299
+ return (lang.embeddedLanguages || lang.embeddedLangs)?.some((l) => missingLangs.map(([name]) => name).includes(l));
864
300
  }).filter((lang) => !missingLangs.includes(lang));
865
- throw new ShikiError$1(`Missing languages ${missingLangs.map(([name]) => `\`${name}\``).join(", ")}, required by ${dependents.map(([name]) => `\`${name}\``).join(", ")}`);
301
+ throw new ShikiError(`Missing languages ${missingLangs.map(([name]) => `\`${name}\``).join(", ")}, required by ${dependents.map(([name]) => `\`${name}\``).join(", ")}`);
866
302
  }
867
303
  for (const [_, lang] of langsGraphArray) this._resolver.addLanguage(lang);
868
304
  for (const [_, lang] of langsGraphArray) this.loadLanguage(lang);
869
305
  }
870
306
  getLoadedLanguages() {
871
- if (!this._loadedLanguagesCache) this._loadedLanguagesCache = [.../* @__PURE__ */ new Set([...this._resolvedGrammars.keys(), ...Object.keys(this._alias)])];
307
+ if (!this._loadedLanguagesCache) this._loadedLanguagesCache = [...new Set([...this._resolvedGrammars.keys(), ...Object.keys(this._alias)])];
872
308
  return this._loadedLanguagesCache;
873
309
  }
874
310
  resolveEmbeddedLanguages(lang) {
@@ -921,15 +357,19 @@ var Resolver = class {
921
357
  }
922
358
  };
923
359
  let instancesCount = 0;
924
- function createShikiInternalSync(options) {
360
+ /**
361
+ * Get the minimal shiki primitive instance.
362
+ *
363
+ * Requires to provide the engine and all themes and languages upfront.
364
+ */
365
+ function createShikiPrimitive(options) {
925
366
  instancesCount += 1;
926
367
  if (options.warnings !== false && instancesCount >= 10 && instancesCount % 10 === 0) console.warn(`[Shiki] ${instancesCount} instances have been created. Shiki is supposed to be used as a singleton, consider refactoring your code to cache your highlighter instance; Or call \`highlighter.dispose()\` to release unused instances.`);
927
368
  let isDisposed = false;
928
- if (!options.engine) throw new ShikiError$1("`engine` option is required for synchronous mode");
369
+ if (!options.engine) throw new ShikiError("`engine` option is required for synchronous mode");
929
370
  const langs = (options.langs || []).flat(1);
930
371
  const themes = (options.themes || []).flat(1).map(normalizeTheme);
931
- const resolver = new Resolver(options.engine, langs);
932
- const _registry = new Registry$1(resolver, themes, langs, options.langAlias);
372
+ const _registry = new Registry$1(new Resolver(options.engine, langs), themes, langs, options.langAlias);
933
373
  let _lastTheme;
934
374
  function resolveLangAlias$1(name) {
935
375
  return resolveLangAlias(name, options.langAlias);
@@ -937,7 +377,7 @@ function createShikiInternalSync(options) {
937
377
  function getLanguage(name) {
938
378
  ensureNotDisposed();
939
379
  const _lang = _registry.getGrammar(typeof name === "string" ? name : name.name);
940
- if (!_lang) throw new ShikiError$1(`Language \`${name}\` not found, you may need to load it first`);
380
+ if (!_lang) throw new ShikiError(`Language \`${name}\` not found, you may need to load it first`);
941
381
  return _lang;
942
382
  }
943
383
  function getTheme(name) {
@@ -950,7 +390,7 @@ function createShikiInternalSync(options) {
950
390
  };
951
391
  ensureNotDisposed();
952
392
  const _theme = _registry.getTheme(name);
953
- if (!_theme) throw new ShikiError$1(`Theme \`${name}\` not found, you may need to load it first`);
393
+ if (!_theme) throw new ShikiError(`Theme \`${name}\` not found, you may need to load it first`);
954
394
  return _theme;
955
395
  }
956
396
  function setTheme(name) {
@@ -960,10 +400,9 @@ function createShikiInternalSync(options) {
960
400
  _registry.setTheme(theme);
961
401
  _lastTheme = name;
962
402
  }
963
- const colorMap = _registry.getColorMap();
964
403
  return {
965
404
  theme,
966
- colorMap
405
+ colorMap: _registry.getColorMap()
967
406
  };
968
407
  }
969
408
  function getLoadedThemes() {
@@ -974,62 +413,384 @@ function createShikiInternalSync(options) {
974
413
  ensureNotDisposed();
975
414
  return _registry.getLoadedLanguages();
976
415
  }
977
- function loadLanguageSync(...langs2) {
978
- ensureNotDisposed();
979
- _registry.loadLanguages(langs2.flat(1));
416
+ function loadLanguageSync(...langs$1) {
417
+ ensureNotDisposed();
418
+ _registry.loadLanguages(langs$1.flat(1));
419
+ }
420
+ async function loadLanguage(...langs$1) {
421
+ return loadLanguageSync(await resolveLangs(langs$1));
422
+ }
423
+ function loadThemeSync(...themes$1) {
424
+ ensureNotDisposed();
425
+ for (const theme of themes$1.flat(1)) _registry.loadTheme(theme);
426
+ }
427
+ async function loadTheme(...themes$1) {
428
+ ensureNotDisposed();
429
+ return loadThemeSync(await resolveThemes(themes$1));
430
+ }
431
+ function ensureNotDisposed() {
432
+ if (isDisposed) throw new ShikiError("Shiki instance has been disposed");
433
+ }
434
+ function dispose() {
435
+ if (isDisposed) return;
436
+ isDisposed = true;
437
+ _registry.dispose();
438
+ instancesCount -= 1;
439
+ }
440
+ return {
441
+ setTheme,
442
+ getTheme,
443
+ getLanguage,
444
+ getLoadedThemes,
445
+ getLoadedLanguages,
446
+ resolveLangAlias: resolveLangAlias$1,
447
+ loadLanguage,
448
+ loadLanguageSync,
449
+ loadTheme,
450
+ loadThemeSync,
451
+ dispose,
452
+ [Symbol.dispose]: dispose
453
+ };
454
+ }
455
+ /**
456
+ * Get the minimal shiki primitive instance.
457
+ */
458
+ async function createShikiPrimitiveAsync(options) {
459
+ if (!options.engine) console.warn("`engine` option is required. Use `createOnigurumaEngine` or `createJavaScriptRegexEngine` to create an engine.");
460
+ const [themes, langs, engine] = await Promise.all([
461
+ resolveThemes(options.themes || []),
462
+ resolveLangs(options.langs || []),
463
+ options.engine
464
+ ]);
465
+ return createShikiPrimitive({
466
+ ...options,
467
+ themes,
468
+ langs,
469
+ engine
470
+ });
471
+ }
472
+ /**
473
+ * @deprecated Use `createShikiPrimitiveAsync` instead.
474
+ */
475
+ const createShikiInternal = createShikiPrimitiveAsync;
476
+ const _grammarStateMap = /* @__PURE__ */ new WeakMap();
477
+ function setLastGrammarStateToMap(keys, state) {
478
+ _grammarStateMap.set(keys, state);
479
+ }
480
+ function getLastGrammarStateFromMap(keys) {
481
+ return _grammarStateMap.get(keys);
482
+ }
483
+ /**
484
+ * GrammarState is a special reference object that holds the state of a grammar.
485
+ *
486
+ * It's used to highlight code snippets that are part of the target language.
487
+ */
488
+ var GrammarState = class GrammarState$1 {
489
+ /**
490
+ * Theme to Stack mapping
491
+ */
492
+ _stacks = {};
493
+ lang;
494
+ get themes() {
495
+ return Object.keys(this._stacks);
496
+ }
497
+ get theme() {
498
+ return this.themes[0];
499
+ }
500
+ get _stack() {
501
+ return this._stacks[this.theme];
502
+ }
503
+ /**
504
+ * Static method to create a initial grammar state.
505
+ */
506
+ static initial(lang, themes) {
507
+ return new GrammarState$1(Object.fromEntries(toArray(themes).map((theme) => [theme, INITIAL])), lang);
508
+ }
509
+ constructor(...args) {
510
+ if (args.length === 2) {
511
+ const [stacksMap, lang] = args;
512
+ this.lang = lang;
513
+ this._stacks = stacksMap;
514
+ } else {
515
+ const [stack, lang, theme] = args;
516
+ this.lang = lang;
517
+ this._stacks = { [theme]: stack };
518
+ }
519
+ }
520
+ /**
521
+ * Get the internal stack object.
522
+ * @internal
523
+ */
524
+ getInternalStack(theme = this.theme) {
525
+ return this._stacks[theme];
980
526
  }
981
- async function loadLanguage(...langs2) {
982
- return loadLanguageSync(await resolveLangs(langs2));
527
+ getScopes(theme = this.theme) {
528
+ return getScopes(this._stacks[theme]);
983
529
  }
984
- function loadThemeSync(...themes2) {
985
- ensureNotDisposed();
986
- for (const theme of themes2.flat(1)) _registry.loadTheme(theme);
530
+ toJSON() {
531
+ return {
532
+ lang: this.lang,
533
+ theme: this.theme,
534
+ themes: this.themes,
535
+ scopes: this.getScopes()
536
+ };
987
537
  }
988
- async function loadTheme(...themes2) {
989
- ensureNotDisposed();
990
- return loadThemeSync(await resolveThemes(themes2));
538
+ };
539
+ function getScopes(stack) {
540
+ const scopes = [];
541
+ const visited = /* @__PURE__ */ new Set();
542
+ function pushScope(stack$1) {
543
+ if (visited.has(stack$1)) return;
544
+ visited.add(stack$1);
545
+ const name = stack$1?.nameScopesList?.scopeName;
546
+ if (name) scopes.push(name);
547
+ if (stack$1.parent) pushScope(stack$1.parent);
991
548
  }
992
- function ensureNotDisposed() {
993
- if (isDisposed) throw new ShikiError$1("Shiki instance has been disposed");
549
+ pushScope(stack);
550
+ return scopes;
551
+ }
552
+ function getGrammarStack(state, theme) {
553
+ if (!(state instanceof GrammarState)) throw new ShikiError("Invalid grammar state");
554
+ return state.getInternalStack(theme);
555
+ }
556
+ /**
557
+ * Code to tokens, with a simple theme.
558
+ */
559
+ function codeToTokensBase(primitive, code, options = {}) {
560
+ const { theme: themeName = primitive.getLoadedThemes()[0] } = options;
561
+ if (isPlainLang(primitive.resolveLangAlias(options.lang || "text")) || isNoneTheme(themeName)) return splitLines(code).map((line) => [{
562
+ content: line[0],
563
+ offset: line[1]
564
+ }]);
565
+ const { theme, colorMap } = primitive.setTheme(themeName);
566
+ const _grammar = primitive.getLanguage(options.lang || "text");
567
+ if (options.grammarState) {
568
+ if (options.grammarState.lang !== _grammar.name) throw new ShikiError(`Grammar state language "${options.grammarState.lang}" does not match highlight language "${_grammar.name}"`);
569
+ if (!options.grammarState.themes.includes(theme.name)) throw new ShikiError(`Grammar state themes "${options.grammarState.themes}" do not contain highlight theme "${theme.name}"`);
994
570
  }
995
- function dispose() {
996
- if (isDisposed) return;
997
- isDisposed = true;
998
- _registry.dispose();
999
- instancesCount -= 1;
571
+ return tokenizeWithTheme(code, _grammar, theme, colorMap, options);
572
+ }
573
+ function tokenizeWithTheme(code, grammar, theme, colorMap, options) {
574
+ const result = _tokenizeWithTheme(code, grammar, theme, colorMap, options);
575
+ const grammarState = new GrammarState(result.stateStack, grammar.name, theme.name);
576
+ setLastGrammarStateToMap(result.tokens, grammarState);
577
+ return result.tokens;
578
+ }
579
+ function _tokenizeWithTheme(code, grammar, theme, colorMap, options) {
580
+ const colorReplacements = resolveColorReplacements(theme, options);
581
+ const { tokenizeMaxLineLength = 0, tokenizeTimeLimit = 500 } = options;
582
+ const lines = splitLines(code);
583
+ let stateStack = options.grammarState ? getGrammarStack(options.grammarState, theme.name) ?? INITIAL : options.grammarContextCode != null ? _tokenizeWithTheme(options.grammarContextCode, grammar, theme, colorMap, {
584
+ ...options,
585
+ grammarState: void 0,
586
+ grammarContextCode: void 0
587
+ }).stateStack : INITIAL;
588
+ let actual = [];
589
+ const final = [];
590
+ for (let i = 0, len = lines.length; i < len; i++) {
591
+ const [line, lineOffset] = lines[i];
592
+ if (line === "") {
593
+ actual = [];
594
+ final.push([]);
595
+ continue;
596
+ }
597
+ if (tokenizeMaxLineLength > 0 && line.length >= tokenizeMaxLineLength) {
598
+ actual = [];
599
+ final.push([{
600
+ content: line,
601
+ offset: lineOffset,
602
+ color: "",
603
+ fontStyle: 0
604
+ }]);
605
+ continue;
606
+ }
607
+ let resultWithScopes;
608
+ let tokensWithScopes;
609
+ let tokensWithScopesIndex;
610
+ if (options.includeExplanation) {
611
+ resultWithScopes = grammar.tokenizeLine(line, stateStack, tokenizeTimeLimit);
612
+ tokensWithScopes = resultWithScopes.tokens;
613
+ tokensWithScopesIndex = 0;
614
+ }
615
+ const result = grammar.tokenizeLine2(line, stateStack, tokenizeTimeLimit);
616
+ const tokensLength = result.tokens.length / 2;
617
+ for (let j = 0; j < tokensLength; j++) {
618
+ const startIndex = result.tokens[2 * j];
619
+ const nextStartIndex = j + 1 < tokensLength ? result.tokens[2 * j + 2] : line.length;
620
+ if (startIndex === nextStartIndex) continue;
621
+ const metadata = result.tokens[2 * j + 1];
622
+ const color = applyColorReplacements(colorMap[EncodedTokenMetadata.getForeground(metadata)], colorReplacements);
623
+ const fontStyle = EncodedTokenMetadata.getFontStyle(metadata);
624
+ const token = {
625
+ content: line.substring(startIndex, nextStartIndex),
626
+ offset: lineOffset + startIndex,
627
+ color,
628
+ fontStyle
629
+ };
630
+ if (options.includeExplanation) {
631
+ const themeSettingsSelectors = [];
632
+ if (options.includeExplanation !== "scopeName") for (const setting of theme.settings) {
633
+ let selectors;
634
+ switch (typeof setting.scope) {
635
+ case "string":
636
+ selectors = setting.scope.split(/,/).map((scope) => scope.trim());
637
+ break;
638
+ case "object":
639
+ selectors = setting.scope;
640
+ break;
641
+ default: continue;
642
+ }
643
+ themeSettingsSelectors.push({
644
+ settings: setting,
645
+ selectors: selectors.map((selector) => selector.split(/ /))
646
+ });
647
+ }
648
+ token.explanation = [];
649
+ let offset = 0;
650
+ while (startIndex + offset < nextStartIndex) {
651
+ const tokenWithScopes = tokensWithScopes[tokensWithScopesIndex];
652
+ const tokenWithScopesText = line.substring(tokenWithScopes.startIndex, tokenWithScopes.endIndex);
653
+ offset += tokenWithScopesText.length;
654
+ token.explanation.push({
655
+ content: tokenWithScopesText,
656
+ scopes: options.includeExplanation === "scopeName" ? explainThemeScopesNameOnly(tokenWithScopes.scopes) : explainThemeScopesFull(themeSettingsSelectors, tokenWithScopes.scopes)
657
+ });
658
+ tokensWithScopesIndex += 1;
659
+ }
660
+ }
661
+ actual.push(token);
662
+ }
663
+ final.push(actual);
664
+ actual = [];
665
+ stateStack = result.ruleStack;
1000
666
  }
1001
667
  return {
1002
- setTheme,
1003
- getTheme,
1004
- getLanguage,
1005
- getLoadedThemes,
1006
- getLoadedLanguages,
1007
- resolveLangAlias: resolveLangAlias$1,
1008
- loadLanguage,
1009
- loadLanguageSync,
1010
- loadTheme,
1011
- loadThemeSync,
1012
- dispose,
1013
- [Symbol.dispose]: dispose
668
+ tokens: final,
669
+ stateStack
1014
670
  };
1015
671
  }
1016
- async function createShikiInternal(options) {
1017
- if (!options.engine) warnDeprecated("`engine` option is required. Use `createOnigurumaEngine` or `createJavaScriptRegexEngine` to create an engine.");
1018
- const [themes, langs, engine] = await Promise.all([
1019
- resolveThemes(options.themes || []),
1020
- resolveLangs(options.langs || []),
1021
- options.engine
1022
- ]);
1023
- return createShikiInternalSync({
1024
- ...options,
1025
- themes,
1026
- langs,
1027
- engine
672
+ function explainThemeScopesNameOnly(scopes) {
673
+ return scopes.map((scope) => ({ scopeName: scope }));
674
+ }
675
+ function explainThemeScopesFull(themeSelectors, scopes) {
676
+ const result = [];
677
+ for (let i = 0, len = scopes.length; i < len; i++) {
678
+ const scope = scopes[i];
679
+ result[i] = {
680
+ scopeName: scope,
681
+ themeMatches: explainThemeScope(themeSelectors, scope, scopes.slice(0, i))
682
+ };
683
+ }
684
+ return result;
685
+ }
686
+ function matchesOne(selector, scope) {
687
+ return selector === scope || scope.substring(0, selector.length) === selector && scope[selector.length] === ".";
688
+ }
689
+ function matches(selectors, scope, parentScopes) {
690
+ if (!matchesOne(selectors[selectors.length - 1], scope)) return false;
691
+ let selectorParentIndex = selectors.length - 2;
692
+ let parentIndex = parentScopes.length - 1;
693
+ while (selectorParentIndex >= 0 && parentIndex >= 0) {
694
+ if (matchesOne(selectors[selectorParentIndex], parentScopes[parentIndex])) selectorParentIndex -= 1;
695
+ parentIndex -= 1;
696
+ }
697
+ if (selectorParentIndex === -1) return true;
698
+ return false;
699
+ }
700
+ function explainThemeScope(themeSettingsSelectors, scope, parentScopes) {
701
+ const result = [];
702
+ for (const { selectors, settings } of themeSettingsSelectors) for (const selectorPieces of selectors) if (matches(selectorPieces, scope, parentScopes)) {
703
+ result.push(settings);
704
+ break;
705
+ }
706
+ return result;
707
+ }
708
+ /**
709
+ * Get tokens with multiple themes
710
+ */
711
+ function codeToTokensWithThemes(primitive, code, options) {
712
+ const themes = Object.entries(options.themes).filter((i) => i[1]).map((i) => ({
713
+ color: i[0],
714
+ theme: i[1]
715
+ }));
716
+ const themedTokens = themes.map((t) => {
717
+ const tokens$1 = codeToTokensBase(primitive, code, {
718
+ ...options,
719
+ theme: t.theme
720
+ });
721
+ return {
722
+ tokens: tokens$1,
723
+ state: getLastGrammarStateFromMap(tokens$1),
724
+ theme: typeof t.theme === "string" ? t.theme : t.theme.name
725
+ };
1028
726
  });
727
+ const tokens = alignThemesTokenization(...themedTokens.map((i) => i.tokens));
728
+ const mergedTokens = tokens[0].map((line, lineIdx) => line.map((_token, tokenIdx) => {
729
+ const mergedToken = {
730
+ content: _token.content,
731
+ variants: {},
732
+ offset: _token.offset
733
+ };
734
+ if ("includeExplanation" in options && options.includeExplanation) mergedToken.explanation = _token.explanation;
735
+ tokens.forEach((t, themeIdx) => {
736
+ const { content: _, explanation: __, offset: ___,...styles } = t[lineIdx][tokenIdx];
737
+ mergedToken.variants[themes[themeIdx].color] = styles;
738
+ });
739
+ return mergedToken;
740
+ }));
741
+ const mergedGrammarState = themedTokens[0].state ? new GrammarState(Object.fromEntries(themedTokens.map((s) => [s.theme, s.state?.getInternalStack(s.theme)])), themedTokens[0].state.lang) : void 0;
742
+ if (mergedGrammarState) setLastGrammarStateToMap(mergedTokens, mergedGrammarState);
743
+ return mergedTokens;
744
+ }
745
+ /**
746
+ * Break tokens from multiple themes into same tokenization.
747
+ *
748
+ * For example, given two themes that tokenize `console.log("hello")` as:
749
+ *
750
+ * - `console . log (" hello ")` (6 tokens)
751
+ * - `console .log ( "hello" )` (5 tokens)
752
+ *
753
+ * This function will return:
754
+ *
755
+ * - `console . log ( " hello " )` (8 tokens)
756
+ * - `console . log ( " hello " )` (8 tokens)
757
+ */
758
+ function alignThemesTokenization(...themes) {
759
+ const outThemes = themes.map(() => []);
760
+ const count = themes.length;
761
+ for (let i = 0; i < themes[0].length; i++) {
762
+ const lines = themes.map((t) => t[i]);
763
+ const outLines = outThemes.map(() => []);
764
+ outThemes.forEach((t, i$1) => t.push(outLines[i$1]));
765
+ const indexes = lines.map(() => 0);
766
+ const current = lines.map((l) => l[0]);
767
+ while (current.every((t) => t)) {
768
+ const minLength = Math.min(...current.map((t) => t.content.length));
769
+ for (let n = 0; n < count; n++) {
770
+ const token = current[n];
771
+ if (token.content.length === minLength) {
772
+ outLines[n].push(token);
773
+ indexes[n] += 1;
774
+ current[n] = lines[n][indexes[n]];
775
+ } else {
776
+ outLines[n].push({
777
+ ...token,
778
+ content: token.content.slice(0, minLength)
779
+ });
780
+ current[n] = {
781
+ ...token,
782
+ content: token.content.slice(minLength),
783
+ offset: token.offset + minLength
784
+ };
785
+ }
786
+ }
787
+ }
788
+ }
789
+ return outThemes;
1029
790
  }
1030
791
 
1031
792
  //#endregion
1032
- //#region node_modules/shiki/dist/langs.mjs
793
+ //#region node_modules/shiki/dist/langs-bundle-full-CQWtMIqW.mjs
1033
794
  const bundledLanguagesInfo = [
1034
795
  {
1035
796
  "id": "abap",
@@ -2485,7 +2246,7 @@ const bundledThemesInfo = [
2485
2246
  {
2486
2247
  "id": "horizon-bright",
2487
2248
  "displayName": "Horizon Bright",
2488
- "type": "dark",
2249
+ "type": "light",
2489
2250
  "import": () => import("@shikijs/themes/horizon-bright")
2490
2251
  },
2491
2252
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mini-shiki",
3
- "version": "3.23.0",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "repository": "git+https://github.com/un-ts/mini-shiki.git",
6
6
  "homepage": "https://github.com/un-ts/mini-shiki#readme",
@@ -21,10 +21,10 @@
21
21
  "static"
22
22
  ],
23
23
  "dependencies": {
24
- "@shikijs/engine-oniguruma": "^3.23.0",
25
- "@shikijs/langs": "^3.23.0",
26
- "@shikijs/themes": "^3.23.0",
27
- "@shikijs/types": "^3.23.0",
24
+ "@shikijs/engine-oniguruma": "^4.0.1",
25
+ "@shikijs/langs": "^4.0.1",
26
+ "@shikijs/themes": "^4.0.1",
27
+ "@shikijs/types": "^4.0.1",
28
28
  "@shikijs/vscode-textmate": "^10.0.2"
29
29
  }
30
30
  }