@singapore-editor/minimap 0.1.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 (56) hide show
  1. package/README.md +29 -0
  2. package/dist/assets/minimap.worker-i4S59IOV.js +1616 -0
  3. package/dist/assets/minimap.worker-i4S59IOV.js.map +1 -0
  4. package/dist/color.d.ts +11 -0
  5. package/dist/color.d.ts.map +1 -0
  6. package/dist/color.js +83 -0
  7. package/dist/color.js.map +1 -0
  8. package/dist/decorationMerge.d.ts +10 -0
  9. package/dist/decorationMerge.d.ts.map +1 -0
  10. package/dist/decorationMerge.js +65 -0
  11. package/dist/decorationMerge.js.map +1 -0
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +3 -0
  15. package/dist/layout.d.ts +43 -0
  16. package/dist/layout.d.ts.map +1 -0
  17. package/dist/layout.js +137 -0
  18. package/dist/layout.js.map +1 -0
  19. package/dist/minimap.worker.d.ts +2 -0
  20. package/dist/minimap.worker.d.ts.map +1 -0
  21. package/dist/minimapCharRenderer.d.ts +13 -0
  22. package/dist/minimapCharRenderer.d.ts.map +1 -0
  23. package/dist/minimapCharRendererFactory.d.ts +14 -0
  24. package/dist/minimapCharRendererFactory.d.ts.map +1 -0
  25. package/dist/minimapCharSheet.d.ts +15 -0
  26. package/dist/minimapCharSheet.d.ts.map +1 -0
  27. package/dist/minimapCharSheet.js +24 -0
  28. package/dist/minimapCharSheet.js.map +1 -0
  29. package/dist/options.d.ts +3 -0
  30. package/dist/options.d.ts.map +1 -0
  31. package/dist/options.js +65 -0
  32. package/dist/options.js.map +1 -0
  33. package/dist/plugin.d.ts +4 -0
  34. package/dist/plugin.d.ts.map +1 -0
  35. package/dist/plugin.js +415 -0
  36. package/dist/plugin.js.map +1 -0
  37. package/dist/raster.d.ts +10 -0
  38. package/dist/raster.d.ts.map +1 -0
  39. package/dist/renderer.d.ts +58 -0
  40. package/dist/renderer.d.ts.map +1 -0
  41. package/dist/sectionHeaders.d.ts +13 -0
  42. package/dist/sectionHeaders.d.ts.map +1 -0
  43. package/dist/style.css +97 -0
  44. package/dist/types.d.ts +211 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +11 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/viewportGeometry.d.ts +28 -0
  49. package/dist/viewportGeometry.d.ts.map +1 -0
  50. package/dist/viewportGeometry.js +22 -0
  51. package/dist/viewportGeometry.js.map +1 -0
  52. package/dist/workerClient.d.ts +131 -0
  53. package/dist/workerClient.d.ts.map +1 -0
  54. package/dist/workerClient.js +1337 -0
  55. package/dist/workerClient.js.map +1 -0
  56. package/package.json +50 -0
@@ -0,0 +1,1616 @@
1
+ //#region ../editor/dist/logging/evlog.js
2
+ var evlogErrorInternalKey = Symbol.for("editor.evlog.error.internal");
3
+ var EditorEvlogError = class extends Error {
4
+ [evlogErrorInternalKey];
5
+ code;
6
+ fix;
7
+ link;
8
+ status;
9
+ why;
10
+ constructor(options) {
11
+ const resolved = typeof options === "string" ? { message: options } : options;
12
+ super(resolved.message, { cause: resolved.cause });
13
+ this.name = "EvlogError";
14
+ this.code = resolved.code;
15
+ this.fix = resolved.fix;
16
+ this.link = resolved.link;
17
+ this.status = resolved.status ?? 500;
18
+ this.why = resolved.why;
19
+ if (resolved.internal !== void 0) this.defineInternal(resolved.internal);
20
+ }
21
+ get internal() {
22
+ return this[evlogErrorInternalKey];
23
+ }
24
+ get statusText() {
25
+ return this.message;
26
+ }
27
+ get statusCode() {
28
+ return this.status;
29
+ }
30
+ get statusMessage() {
31
+ return this.message;
32
+ }
33
+ get data() {
34
+ if (!this.code && !this.why && !this.fix && !this.link) return void 0;
35
+ return {
36
+ code: this.code,
37
+ why: this.why,
38
+ fix: this.fix,
39
+ link: this.link
40
+ };
41
+ }
42
+ toString() {
43
+ return evlogErrorLines(this).join("\n");
44
+ }
45
+ toJSON() {
46
+ const serialized = {
47
+ message: this.message,
48
+ name: this.name,
49
+ status: this.status
50
+ };
51
+ if (this.data) serialized.data = this.data;
52
+ if (this.cause instanceof Error) serialized.cause = {
53
+ message: this.cause.message,
54
+ name: this.cause.name
55
+ };
56
+ return serialized;
57
+ }
58
+ defineInternal(internal) {
59
+ Object.defineProperty(this, evlogErrorInternalKey, {
60
+ configurable: true,
61
+ enumerable: false,
62
+ value: internal,
63
+ writable: false
64
+ });
65
+ }
66
+ };
67
+ function createError(options) {
68
+ return new EditorEvlogError(options);
69
+ }
70
+ function evlogErrorLines(error) {
71
+ const lines = [`Error: ${error.message}`];
72
+ if (error.code) lines.push(`Code: ${error.code}`);
73
+ if (error.why) lines.push(`Why: ${error.why}`);
74
+ if (error.fix) lines.push(`Fix: ${error.fix}`);
75
+ if (error.link) lines.push(`More info: ${error.link}`);
76
+ if (error.cause instanceof Error) lines.push(`Caused by: ${error.cause.message}`);
77
+ return lines;
78
+ }
79
+ //#endregion
80
+ //#region src/color.ts
81
+ const RGBA_EMPTY = {
82
+ r: 0,
83
+ g: 0,
84
+ b: 0,
85
+ a: 0
86
+ };
87
+ function transparent(color, ratio) {
88
+ return {
89
+ ...color,
90
+ a: clamp8(color.a * ratio)
91
+ };
92
+ }
93
+ function rgbaToCss(color) {
94
+ return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a / 255})`;
95
+ }
96
+ function parseCssColor(value, fallback = RGBA_EMPTY) {
97
+ if (!value) return fallback;
98
+ const trimmed = value.trim();
99
+ if (trimmed.startsWith("#")) return parseHexColor(trimmed, fallback);
100
+ if (trimmed.startsWith("rgb")) return parseRgbColor(trimmed, fallback);
101
+ return fallback;
102
+ }
103
+ function relativeLuminance(color) {
104
+ const r = linearRgb(color.r / 255);
105
+ const g = linearRgb(color.g / 255);
106
+ const b = linearRgb(color.b / 255);
107
+ return .2126 * r + .7152 * g + .0722 * b;
108
+ }
109
+ function clamp8(value) {
110
+ return Math.min(255, Math.max(0, Math.round(value)));
111
+ }
112
+ function parseHexColor(value, fallback) {
113
+ const hex = value.slice(1);
114
+ if (hex.length === 3) return shortHex(hex, fallback);
115
+ if (hex.length === 4) return shortHexAlpha(hex, fallback);
116
+ if (hex.length === 6) return longHex(hex, "ff", fallback);
117
+ if (hex.length === 8) return longHex(hex.slice(0, 6), hex.slice(6), fallback);
118
+ return fallback;
119
+ }
120
+ function shortHex(hex, fallback) {
121
+ return longHex(hex.split("").map((char) => char + char).join(""), "ff", fallback);
122
+ }
123
+ function shortHexAlpha(hex, fallback) {
124
+ const rgb = hex.slice(0, 3);
125
+ const alpha = hex[3] ?? "f";
126
+ return longHex(rgb.split("").map((char) => char + char).join(""), alpha + alpha, fallback);
127
+ }
128
+ function longHex(rgb, alpha, fallback) {
129
+ const value = Number.parseInt(rgb + alpha, 16);
130
+ if (!Number.isFinite(value)) return fallback;
131
+ return {
132
+ r: value >> 24 & 255,
133
+ g: value >> 16 & 255,
134
+ b: value >> 8 & 255,
135
+ a: value & 255
136
+ };
137
+ }
138
+ function parseRgbColor(value, fallback) {
139
+ const parts = value.slice(value.indexOf("(") + 1, value.lastIndexOf(")")).split(/[,\s/]+/).filter(Boolean);
140
+ if (parts.length < 3) return fallback;
141
+ return {
142
+ r: clamp8(Number(parts[0])),
143
+ g: clamp8(Number(parts[1])),
144
+ b: clamp8(Number(parts[2])),
145
+ a: alphaPart(parts[3])
146
+ };
147
+ }
148
+ function alphaPart(value) {
149
+ if (!value) return 255;
150
+ if (value.endsWith("%")) return clamp8(Number.parseFloat(value) / 100 * 255);
151
+ const number = Number(value);
152
+ if (!Number.isFinite(number)) return 255;
153
+ if (number <= 1) return clamp8(number * 255);
154
+ return clamp8(number);
155
+ }
156
+ function linearRgb(value) {
157
+ if (value <= .04045) return value / 12.92;
158
+ return ((value + .055) / 1.055) ** 2.4;
159
+ }
160
+ //#endregion
161
+ //#region src/minimapCharSheet.ts
162
+ let Constants = /* @__PURE__ */ function(Constants) {
163
+ Constants[Constants["START_CH_CODE"] = 32] = "START_CH_CODE";
164
+ Constants[Constants["END_CH_CODE"] = 126] = "END_CH_CODE";
165
+ Constants[Constants["UNKNOWN_CODE"] = 65533] = "UNKNOWN_CODE";
166
+ Constants[Constants["CHAR_COUNT"] = 96] = "CHAR_COUNT";
167
+ Constants[Constants["SAMPLED_CHAR_HEIGHT"] = 16] = "SAMPLED_CHAR_HEIGHT";
168
+ Constants[Constants["SAMPLED_CHAR_WIDTH"] = 10] = "SAMPLED_CHAR_WIDTH";
169
+ Constants[Constants["BASE_CHAR_HEIGHT"] = 2] = "BASE_CHAR_HEIGHT";
170
+ Constants[Constants["BASE_CHAR_WIDTH"] = 1] = "BASE_CHAR_WIDTH";
171
+ Constants[Constants["RGBA_CHANNELS_CNT"] = 4] = "RGBA_CHANNELS_CNT";
172
+ Constants[Constants["RGBA_SAMPLED_ROW_WIDTH"] = 3840] = "RGBA_SAMPLED_ROW_WIDTH";
173
+ return Constants;
174
+ }({});
175
+ const allCharCodes = (() => {
176
+ const values = [];
177
+ for (let index = 32; index <= 126; index += 1) values.push(index);
178
+ values.push(65533);
179
+ return values;
180
+ })();
181
+ const getCharIndex = (chCode, fontScale) => {
182
+ const index = chCode - 32;
183
+ if (index >= 0 && index <= 96) return index;
184
+ if (fontScale <= 2) return (index + 96) % 96;
185
+ return 95;
186
+ };
187
+ //#endregion
188
+ //#region src/types.ts
189
+ let RenderMinimap = /* @__PURE__ */ function(RenderMinimap) {
190
+ RenderMinimap[RenderMinimap["None"] = 0] = "None";
191
+ RenderMinimap[RenderMinimap["Text"] = 1] = "Text";
192
+ RenderMinimap[RenderMinimap["Blocks"] = 2] = "Blocks";
193
+ return RenderMinimap;
194
+ }({});
195
+ function computeRenderLayout(options) {
196
+ const pixelRatio = Math.max(1, options.metrics.devicePixelRatio);
197
+ const height = Math.max(0, options.viewport.minimapHeight);
198
+ const baseCharHeight = options.minimap.renderCharacters ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1;
199
+ const configuredScale = pixelRatio >= 2 ? Math.round(options.minimap.scale * 2) : options.minimap.scale;
200
+ const baseCanvasInnerHeight = Math.floor(pixelRatio * height);
201
+ const fitted = computeFittedScale({
202
+ baseCharHeight,
203
+ canvasInnerHeight: baseCanvasInnerHeight,
204
+ configuredScale,
205
+ height,
206
+ lineCount: options.lineCount,
207
+ minimap: (options.viewport.visibleEnd - options.viewport.visibleStart) * baseCharHeight * configuredScale > baseCanvasInnerHeight ? {
208
+ ...options.minimap,
209
+ size: "fill"
210
+ } : options.minimap,
211
+ pixelRatio,
212
+ rowHeight: options.metrics.rowHeight
213
+ });
214
+ const width = computeMinimapWidth({
215
+ charWidth: fitted.scale / pixelRatio / fitted.widthMultiplier,
216
+ maxColumn: options.minimap.maxColumn,
217
+ viewportWidth: options.viewport.clientWidth + Math.max(0, options.viewport.reservedWidth),
218
+ characterWidth: options.metrics.characterWidth
219
+ });
220
+ const baseCanvasInnerWidth = Math.floor(pixelRatio * width);
221
+ return {
222
+ width,
223
+ height,
224
+ canvasInnerWidth: Math.floor(baseCanvasInnerWidth * fitted.widthMultiplier),
225
+ canvasInnerHeight: fitted.heightIsEditorHeight ? Math.ceil(Math.max(baseCanvasInnerHeight, fitted.documentMinimapHeight)) : baseCanvasInnerHeight,
226
+ canvasOuterWidth: baseCanvasInnerWidth / pixelRatio,
227
+ canvasOuterHeight: baseCanvasInnerHeight / pixelRatio,
228
+ lineHeight: fitted.lineHeight,
229
+ charWidth: Constants.BASE_CHAR_WIDTH * fitted.scale,
230
+ scale: fitted.scale,
231
+ isSampling: fitted.isSampling,
232
+ heightIsEditorHeight: fitted.heightIsEditorHeight,
233
+ renderMinimap: options.minimap.enabled ? options.minimap.renderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks : RenderMinimap.None
234
+ };
235
+ }
236
+ function computeFrameLayout(options) {
237
+ const { renderLayout: layout, viewport, realLineCount } = options;
238
+ const pixelRatio = layout.canvasInnerHeight / Math.max(1, layout.canvasOuterHeight);
239
+ const documentLineHeight = layout.isSampling ? layout.height / Math.max(1, realLineCount) : layout.lineHeight / pixelRatio;
240
+ const start = Math.min(realLineCount, Math.max(0, viewport.visibleStart));
241
+ const visibleCount = Math.min(realLineCount, Math.max(start, viewport.visibleEnd)) - start;
242
+ const linesFitting = layout.height / documentLineHeight;
243
+ const travel = Math.max(0, realLineCount - linesFitting);
244
+ const progress = start / Math.max(1, realLineCount - visibleCount);
245
+ const documentStart = Math.min(start, travel * progress);
246
+ const rasterStart = layout.isSampling ? 0 : documentStart;
247
+ const startLineNumber = Math.floor(rasterStart) + 1;
248
+ const startLineFraction = rasterStart - Math.floor(rasterStart);
249
+ return {
250
+ scrollTop: viewport.scrollTop,
251
+ scrollHeight: viewport.scrollHeight,
252
+ sliderNeeded: visibleCount < realLineCount,
253
+ documentLineHeight,
254
+ sliderLineHeight: travel > 0 ? Math.max(0, layout.height - visibleCount * documentLineHeight) / Math.max(1, realLineCount - visibleCount) : documentLineHeight,
255
+ documentStart,
256
+ sliderTop: (start - documentStart) * documentLineHeight,
257
+ sliderHeight: Math.min(layout.height, visibleCount * documentLineHeight),
258
+ topPaddingLineCount: 0,
259
+ startLineNumber,
260
+ startLineFraction,
261
+ endLineNumber: Math.min(options.lineCount, startLineNumber + Math.ceil(layout.canvasInnerHeight / layout.lineHeight + startLineFraction) - 1)
262
+ };
263
+ }
264
+ function yForLineNumber(frame, lineNumber, minimapLineHeight) {
265
+ return (lineNumber - frame.startLineNumber + frame.topPaddingLineCount) * minimapLineHeight;
266
+ }
267
+ function computeFittedScale(options) {
268
+ const lineHeight = options.baseCharHeight * options.configuredScale;
269
+ if (options.minimap.size === "proportional") return fittedScale(options.configuredScale, lineHeight, 1, false, false, 0);
270
+ const desiredRatio = options.lineCount / Math.max(1, options.canvasInnerHeight);
271
+ if (desiredRatio > 1) return fittedScale(1, 1, 1, true, true, options.canvasInnerHeight);
272
+ if (options.minimap.size === "fit") {
273
+ const documentHeight = Math.ceil(options.lineCount * lineHeight);
274
+ if (documentHeight <= options.canvasInnerHeight) return fittedScale(options.configuredScale, lineHeight, 1, false, false, documentHeight);
275
+ }
276
+ const maxScale = options.configuredScale + 1;
277
+ const fillLineHeight = Math.min(options.rowHeight * options.pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
278
+ const scale = Math.min(maxScale, Math.max(1, Math.floor(fillLineHeight / options.baseCharHeight)));
279
+ const widthMultiplier = scale > options.configuredScale ? Math.min(2, scale / options.configuredScale) : 1;
280
+ const typicalViewportLineCount = options.height / Math.max(1, options.rowHeight);
281
+ return fittedScale(scale, fillLineHeight, widthMultiplier, false, true, Math.ceil(Math.max(typicalViewportLineCount, options.lineCount) * fillLineHeight));
282
+ }
283
+ function fittedScale(scale, lineHeight, widthMultiplier, isSampling, heightIsEditorHeight, documentMinimapHeight) {
284
+ return {
285
+ scale,
286
+ lineHeight,
287
+ widthMultiplier,
288
+ isSampling,
289
+ heightIsEditorHeight,
290
+ documentMinimapHeight
291
+ };
292
+ }
293
+ function computeMinimapWidth(options) {
294
+ const availableWidth = Math.max(0, options.viewportWidth);
295
+ const minimapMaxWidth = Math.floor(options.maxColumn * options.charWidth);
296
+ const proportionalWidth = Math.floor((availableWidth - 2) * options.charWidth / (options.characterWidth + options.charWidth));
297
+ return Math.min(minimapMaxWidth, Math.max(0, proportionalWidth) + 2 + 8);
298
+ }
299
+ //#endregion
300
+ //#region src/minimapCharRenderer.ts
301
+ var MinimapCharRenderer = class MinimapCharRenderer {
302
+ scale;
303
+ charDataNormal;
304
+ charDataLight;
305
+ constructor(charData, scale) {
306
+ this.scale = scale;
307
+ this.charDataNormal = MinimapCharRenderer.soften(charData, 12 / 15);
308
+ this.charDataLight = MinimapCharRenderer.soften(charData, 50 / 60);
309
+ }
310
+ renderChar(target, dx, dy, chCode, color, foregroundAlpha, backgroundColor, backgroundAlpha, fontScale, useLighterFont, force1pxHeight) {
311
+ const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
312
+ const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
313
+ const renderHeight = force1pxHeight ? 1 : charHeight;
314
+ if (dx + charWidth > target.width || dy + renderHeight > target.height) return;
315
+ this.renderCharData({
316
+ target,
317
+ dx,
318
+ dy,
319
+ charWidth,
320
+ renderHeight,
321
+ charIndex: getCharIndex(chCode, fontScale),
322
+ color,
323
+ foregroundAlpha,
324
+ backgroundColor,
325
+ backgroundAlpha,
326
+ charData: useLighterFont ? this.charDataLight : this.charDataNormal
327
+ });
328
+ }
329
+ blockRenderChar(target, dx, dy, color, foregroundAlpha, backgroundColor, backgroundAlpha, force1pxHeight) {
330
+ const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
331
+ const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
332
+ const renderHeight = force1pxHeight ? 1 : charHeight;
333
+ if (dx + charWidth > target.width || dy + renderHeight > target.height) return;
334
+ const foregroundRatio = .5 * (foregroundAlpha / 255);
335
+ const alpha = sourceOverAlpha(backgroundAlpha / 255, foregroundRatio);
336
+ const blended = sourceOverColor(backgroundColor, color, backgroundAlpha / 255, foregroundRatio, alpha);
337
+ const destWidth = target.width * Constants.RGBA_CHANNELS_CNT;
338
+ let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
339
+ for (let y = 0; y < renderHeight; y += 1) {
340
+ writeBlockRow(target.data, row, charWidth, blended, blended.a);
341
+ row += destWidth;
342
+ }
343
+ }
344
+ renderCharData(options) {
345
+ if (options.backgroundAlpha === 0) {
346
+ this.renderTransparentCharData(options);
347
+ return;
348
+ }
349
+ const dest = options.target.data;
350
+ const destWidth = options.target.width * Constants.RGBA_CHANNELS_CNT;
351
+ let sourceOffset = options.charIndex * options.charWidth * Constants.BASE_CHAR_HEIGHT * this.scale;
352
+ let row = options.dy * destWidth + options.dx * Constants.RGBA_CHANNELS_CNT;
353
+ for (let y = 0; y < options.renderHeight; y += 1) {
354
+ sourceOffset = writeCharRow(dest, row, sourceOffset, {
355
+ charWidth: options.charWidth,
356
+ charData: options.charData,
357
+ foregroundAlpha: options.foregroundAlpha,
358
+ color: options.color,
359
+ backgroundColor: options.backgroundColor,
360
+ backgroundAlpha: options.backgroundAlpha
361
+ });
362
+ row += destWidth;
363
+ }
364
+ }
365
+ renderTransparentCharData(options) {
366
+ const dest = options.target.data;
367
+ const destWidth = options.target.width * Constants.RGBA_CHANNELS_CNT;
368
+ let sourceOffset = options.charIndex * options.charWidth * Constants.BASE_CHAR_HEIGHT * this.scale;
369
+ let row = options.dy * destWidth + options.dx * Constants.RGBA_CHANNELS_CNT;
370
+ for (let y = 0; y < options.renderHeight; y += 1) {
371
+ sourceOffset = writeTransparentCharRow(dest, row, sourceOffset, {
372
+ charWidth: options.charWidth,
373
+ charData: options.charData,
374
+ foregroundAlpha: options.foregroundAlpha,
375
+ color: options.color
376
+ });
377
+ row += destWidth;
378
+ }
379
+ }
380
+ static soften(input, ratio) {
381
+ const result = new Uint8ClampedArray(input.length);
382
+ for (let index = 0; index < input.length; index += 1) result[index] = clamp8(input[index] * ratio);
383
+ return result;
384
+ }
385
+ };
386
+ function writeCharRow(dest, row, sourceOffset, options) {
387
+ let column = row;
388
+ let source = sourceOffset;
389
+ for (let x = 0; x < options.charWidth; x += 1) {
390
+ const c = (options.charData[source] ?? 0) / 255 * (options.foregroundAlpha / 255);
391
+ source += 1;
392
+ const alpha = sourceOverAlpha(options.backgroundAlpha / 255, c);
393
+ const color = sourceOverColor(options.backgroundColor, options.color, options.backgroundAlpha / 255, c, alpha);
394
+ dest[column++] = color.r;
395
+ dest[column++] = color.g;
396
+ dest[column++] = color.b;
397
+ dest[column++] = clamp8(alpha * 255);
398
+ }
399
+ return source;
400
+ }
401
+ function writeTransparentCharRow(dest, row, sourceOffset, options) {
402
+ let column = row;
403
+ let source = sourceOffset;
404
+ for (let x = 0; x < options.charWidth; x += 1) {
405
+ const coverage = (options.charData[source] ?? 0) / 255 * (options.foregroundAlpha / 255);
406
+ source += 1;
407
+ dest[column++] = options.color.r;
408
+ dest[column++] = options.color.g;
409
+ dest[column++] = options.color.b;
410
+ dest[column++] = clamp8(coverage * 255);
411
+ }
412
+ return source;
413
+ }
414
+ function sourceOverAlpha(backgroundAlpha, foregroundAlpha) {
415
+ return foregroundAlpha + backgroundAlpha * (1 - foregroundAlpha);
416
+ }
417
+ function sourceOverColor(background, foreground, backgroundAlpha, foregroundAlpha, alpha) {
418
+ if (alpha <= 0) return {
419
+ r: 0,
420
+ g: 0,
421
+ b: 0,
422
+ a: 0
423
+ };
424
+ const backgroundRatio = backgroundAlpha * (1 - foregroundAlpha) / alpha;
425
+ const foregroundRatio = foregroundAlpha / alpha;
426
+ return {
427
+ r: clamp8(background.r * backgroundRatio + foreground.r * foregroundRatio),
428
+ g: clamp8(background.g * backgroundRatio + foreground.g * foregroundRatio),
429
+ b: clamp8(background.b * backgroundRatio + foreground.b * foregroundRatio),
430
+ a: clamp8(alpha * 255)
431
+ };
432
+ }
433
+ function writeBlockRow(dest, row, width, color, alpha) {
434
+ let column = row;
435
+ for (let x = 0; x < width; x += 1) {
436
+ dest[column++] = color.r;
437
+ dest[column++] = color.g;
438
+ dest[column++] = color.b;
439
+ dest[column++] = alpha;
440
+ }
441
+ }
442
+ //#endregion
443
+ //#region src/minimapCharRendererFactory.ts
444
+ /**
445
+ * Creates minimap character renderers by sampling a normal canvas once and
446
+ * downsampling the glyph data for tiny minimap text rendering.
447
+ */
448
+ var MinimapCharRendererFactory = class MinimapCharRendererFactory {
449
+ static lastCreated;
450
+ static lastFontFamily;
451
+ static create(scale, fontFamily) {
452
+ if (this.lastCreated && scale === this.lastCreated.scale && fontFamily === this.lastFontFamily) return this.lastCreated;
453
+ const renderer = this.createFromSampleData(this.createSampleData(fontFamily).data, scale);
454
+ this.lastFontFamily = fontFamily;
455
+ this.lastCreated = renderer;
456
+ return renderer;
457
+ }
458
+ static createSampleData(fontFamily) {
459
+ const context = createSamplingCanvas().getContext("2d");
460
+ context.fillStyle = "#ffffff";
461
+ context.font = `bold ${Constants.SAMPLED_CHAR_HEIGHT}px ${fontFamily}`;
462
+ context.textBaseline = "middle";
463
+ let x = 0;
464
+ for (const code of allCharCodes) {
465
+ context.fillText(String.fromCharCode(code), x, Constants.SAMPLED_CHAR_HEIGHT / 2);
466
+ x += Constants.SAMPLED_CHAR_WIDTH;
467
+ }
468
+ return context.getImageData(0, 0, Constants.CHAR_COUNT * Constants.SAMPLED_CHAR_WIDTH, Constants.SAMPLED_CHAR_HEIGHT);
469
+ }
470
+ static createFromSampleData(source, scale) {
471
+ const expectedLength = Constants.SAMPLED_CHAR_HEIGHT * Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT;
472
+ if (source.length !== expectedLength) throw new Error("Unexpected source in MinimapCharRenderer");
473
+ return new MinimapCharRenderer(MinimapCharRendererFactory.downsample(source, scale), scale);
474
+ }
475
+ static downsample(data, scale) {
476
+ const pixelsPerCharacter = Constants.BASE_CHAR_HEIGHT * scale * Constants.BASE_CHAR_WIDTH * scale;
477
+ const resultLength = pixelsPerCharacter * Constants.CHAR_COUNT;
478
+ const result = new Uint8ClampedArray(resultLength);
479
+ let resultOffset = 0;
480
+ let sourceOffset = 0;
481
+ let brightest = 0;
482
+ for (let index = 0; index < Constants.CHAR_COUNT; index += 1) {
483
+ brightest = Math.max(brightest, downsampleChar(data, sourceOffset, result, resultOffset, scale));
484
+ resultOffset += pixelsPerCharacter;
485
+ sourceOffset += Constants.SAMPLED_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT;
486
+ }
487
+ boostIntensity(result, brightest);
488
+ return result;
489
+ }
490
+ };
491
+ function createSamplingCanvas() {
492
+ return new OffscreenCanvas(Constants.CHAR_COUNT * Constants.SAMPLED_CHAR_WIDTH, Constants.SAMPLED_CHAR_HEIGHT);
493
+ }
494
+ function downsampleChar(source, sourceOffset, dest, destOffset, scale) {
495
+ const width = Constants.BASE_CHAR_WIDTH * scale;
496
+ const height = Constants.BASE_CHAR_HEIGHT * scale;
497
+ let targetIndex = destOffset;
498
+ let brightest = 0;
499
+ for (let y = 0; y < height; y += 1) for (let x = 0; x < width; x += 1) {
500
+ const final = samplePixel(source, sourceOffset, x, y, width, height);
501
+ brightest = Math.max(brightest, final);
502
+ dest[targetIndex++] = clamp8(final);
503
+ }
504
+ return brightest;
505
+ }
506
+ function samplePixel(source, sourceOffset, x, y, width, height) {
507
+ const sourceY1 = y / height * Constants.SAMPLED_CHAR_HEIGHT;
508
+ const sourceY2 = (y + 1) / height * Constants.SAMPLED_CHAR_HEIGHT;
509
+ const sample = sampleSourceRect(source, sourceOffset, x / width * Constants.SAMPLED_CHAR_WIDTH, (x + 1) / width * Constants.SAMPLED_CHAR_WIDTH, sourceY1, sourceY2);
510
+ return sample.value / sample.samples;
511
+ }
512
+ function sampleSourceRect(source, sourceOffset, sourceX1, sourceX2, sourceY1, sourceY2) {
513
+ let value = 0;
514
+ let samples = 0;
515
+ for (let sy = sourceY1; sy < sourceY2; sy += 1) {
516
+ const sampled = sampleSourceRow(source, sourceOffset + Math.floor(sy) * Constants.RGBA_SAMPLED_ROW_WIDTH, sourceX1, sourceX2, 1 - (sy - Math.floor(sy)));
517
+ value += sampled.value;
518
+ samples += sampled.samples;
519
+ }
520
+ return {
521
+ value,
522
+ samples
523
+ };
524
+ }
525
+ function sampleSourceRow(source, row, sourceX1, sourceX2, yBalance) {
526
+ let value = 0;
527
+ let samples = 0;
528
+ for (let sx = sourceX1; sx < sourceX2; sx += 1) {
529
+ const xBalance = 1 - (sx - Math.floor(sx));
530
+ const sourceIndex = row + Math.floor(sx) * Constants.RGBA_CHANNELS_CNT;
531
+ const weight = xBalance * yBalance;
532
+ samples += weight;
533
+ value += (source[sourceIndex] ?? 0) * (source[sourceIndex + 3] ?? 0) / 255 * weight;
534
+ }
535
+ return {
536
+ value,
537
+ samples
538
+ };
539
+ }
540
+ function boostIntensity(result, brightest) {
541
+ if (brightest <= 0) return;
542
+ const adjust = 255 / brightest;
543
+ for (let index = 0; index < result.length; index += 1) result[index] = clamp8(result[index] * adjust);
544
+ }
545
+ //#endregion
546
+ //#region src/raster.ts
547
+ var RasterBuffer = class {
548
+ imageData;
549
+ pixels;
550
+ background;
551
+ constructor(context, width, height, background) {
552
+ this.imageData = context.createImageData(Math.max(1, width), Math.max(1, height));
553
+ this.pixels = new Uint32Array(this.imageData.data.buffer);
554
+ const channels = Uint8ClampedArray.of(background.r, background.g, background.b, background.a);
555
+ this.background = new Uint32Array(channels.buffer)[0];
556
+ }
557
+ clearRows(start, end) {
558
+ this.pixels.fill(this.background, start * this.imageData.width, end * this.imageData.width);
559
+ }
560
+ copyRows(source, destination, count) {
561
+ const width = this.imageData.width;
562
+ this.pixels.copyWithin(destination * width, source * width, (source + count) * width);
563
+ }
564
+ };
565
+ //#endregion
566
+ //#region src/sectionHeaders.ts
567
+ const CHUNK_SIZE = 100;
568
+ function findSectionHeaderDecorations(lines, options) {
569
+ if (!options.showMarkSectionHeaders) return [];
570
+ return collectMarkHeaders(lines, options.markSectionHeaderRegex).map(headerToDecoration);
571
+ }
572
+ function findSectionHeaderDecorationsInRange(lines, startLineNumber, options) {
573
+ return findSectionHeaderDecorations(lines, options).map((decoration) => shiftDecorationLineNumbers(decoration, startLineNumber - 1));
574
+ }
575
+ function collectMarkHeaders(lines, markSectionHeaderRegex) {
576
+ if (markSectionHeaderRegex.trim() === "") return [];
577
+ const regex = createHeaderRegex(markSectionHeaderRegex);
578
+ if (!regex || regExpLeadsToEndlessLoop(regex)) return [];
579
+ const headers = [];
580
+ for (let startLine = 1; startLine <= lines.length; startLine += 95) collectHeadersInChunk(headers, lines, startLine, regex);
581
+ return headers;
582
+ }
583
+ function collectHeadersInChunk(headers, lines, startLine, regex) {
584
+ const endLine = Math.min(startLine + CHUNK_SIZE - 1, lines.length);
585
+ const text = lines.slice(startLine - 1, endLine).join("\n");
586
+ regex.lastIndex = 0;
587
+ let match;
588
+ while ((match = regex.exec(text)) !== null) {
589
+ pushHeader(headers, startLine, text, match);
590
+ regex.lastIndex = match.index + match[0].length;
591
+ }
592
+ }
593
+ function pushHeader(headers, startLine, chunkText, match) {
594
+ const header = matchToSectionHeader(startLine, chunkText, match);
595
+ if (!header.text && !header.hasSeparatorLine) return;
596
+ const previous = headers.at(-1);
597
+ if (previous && previous.endLineNumber >= header.startLineNumber) return;
598
+ headers.push(header);
599
+ }
600
+ function matchToSectionHeader(startLine, chunkText, match) {
601
+ const precedingText = chunkText.substring(0, match.index);
602
+ const lineNumber = startLine + (precedingText.match(/\n/g) || []).length;
603
+ const matchLines = match[0].split("\n");
604
+ const matchHeight = matchLines.length;
605
+ const lineStartIndex = precedingText.lastIndexOf("\n") + 1;
606
+ const startColumn = match.index - lineStartIndex + 1;
607
+ const lastMatchLine = matchLines[matchLines.length - 1] ?? "";
608
+ return {
609
+ startLineNumber: lineNumber,
610
+ startColumn,
611
+ endLineNumber: lineNumber + matchHeight - 1,
612
+ endColumn: matchHeight === 1 ? startColumn + match[0].length : lastMatchLine.length + 1,
613
+ text: (match.groups ?? {})["label"] ?? "",
614
+ hasSeparatorLine: ((match.groups ?? {})["separator"] ?? "") !== ""
615
+ };
616
+ }
617
+ function headerToDecoration(header) {
618
+ return {
619
+ startLineNumber: header.startLineNumber,
620
+ startColumn: header.startColumn,
621
+ endLineNumber: header.endLineNumber,
622
+ endColumn: header.endColumn,
623
+ position: "inline",
624
+ sectionHeaderStyle: headerStyle(header.hasSeparatorLine),
625
+ sectionHeaderText: header.text
626
+ };
627
+ }
628
+ function shiftDecorationLineNumbers(decoration, lineDelta) {
629
+ if (lineDelta === 0) return decoration;
630
+ return {
631
+ ...decoration,
632
+ startLineNumber: decoration.startLineNumber + lineDelta,
633
+ endLineNumber: decoration.endLineNumber + lineDelta
634
+ };
635
+ }
636
+ function headerStyle(hasSeparatorLine) {
637
+ return hasSeparatorLine ? "underlined" : "normal";
638
+ }
639
+ function createHeaderRegex(source) {
640
+ try {
641
+ return new RegExp(source, `gdm${isMultilineRegexSource(source) ? "s" : ""}`);
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+ function isMultilineRegexSource(source) {
647
+ return source.includes("\\n") || source.includes("\n");
648
+ }
649
+ function regExpLeadsToEndlessLoop(regexp) {
650
+ regexp.lastIndex = 0;
651
+ const match = regexp.exec("");
652
+ regexp.lastIndex = 0;
653
+ return match !== null && match[0].length === 0;
654
+ }
655
+ //#endregion
656
+ //#region src/renderer.ts
657
+ const EMPTY_DOCUMENT = {
658
+ textLength: 0,
659
+ lineStarts: [0],
660
+ lines: [{
661
+ text: "",
662
+ length: 0
663
+ }],
664
+ tokens: [],
665
+ selections: [],
666
+ decorations: [],
667
+ externalDecorations: []
668
+ };
669
+ var MinimapWorkerRenderer = class {
670
+ state = null;
671
+ init(options) {
672
+ const mainContext = options.mainCanvas.getContext("2d");
673
+ const decorationsContext = options.decorationsCanvas.getContext("2d");
674
+ if (!mainContext || !decorationsContext) throw new Error("Unable to create minimap canvas context");
675
+ this.state = {
676
+ mainCanvas: options.mainCanvas,
677
+ decorationsCanvas: options.decorationsCanvas,
678
+ mainContext,
679
+ decorationsContext,
680
+ options: options.options,
681
+ styles: options.styles,
682
+ document: EMPTY_DOCUMENT,
683
+ externalDecorations: [],
684
+ metrics: defaultMetrics(),
685
+ viewport: defaultViewport(),
686
+ layout: null,
687
+ previousFrame: null,
688
+ raster: null,
689
+ tokenIndex: null,
690
+ surface: null,
691
+ linesDirty: true,
692
+ decorationsDirty: true
693
+ };
694
+ }
695
+ setDocument(document) {
696
+ if (!this.state) return;
697
+ const externalDecorations = document.externalDecorations ?? externalDecorationsFrom(document.decorations);
698
+ const sectionHeaders = findSectionHeaderDecorations(documentLines(document), this.state.options);
699
+ this.state.externalDecorations = externalDecorations;
700
+ this.state.document = {
701
+ ...document,
702
+ decorations: sectionHeaders.concat(externalDecorations),
703
+ externalDecorations
704
+ };
705
+ this.state.previousFrame = null;
706
+ this.state.linesDirty = true;
707
+ this.state.decorationsDirty = true;
708
+ }
709
+ applyEdit(edit, document) {
710
+ this.applyEdits([edit], document);
711
+ }
712
+ applyEdits(edits, document) {
713
+ if (!this.state) return;
714
+ if (edits.length === 0) {
715
+ this.setSelections(document.selections);
716
+ return;
717
+ }
718
+ const previous = this.state.document;
719
+ const next = applyTextEditsToMinimapDocument(previous, edits, document.summaryPatch);
720
+ this.setEditedDocument(previous, next, edits, document.selections);
721
+ }
722
+ setExternalDecorations(decorations) {
723
+ if (!this.state) return;
724
+ this.state.externalDecorations = decorations;
725
+ this.state.document = {
726
+ ...this.state.document,
727
+ externalDecorations: decorations,
728
+ decorations: sectionHeaderDecorationsFrom(this.state.document.decorations).concat(decorations)
729
+ };
730
+ this.state.decorationsDirty = true;
731
+ }
732
+ setEditedDocument(previous, document, edits, selections) {
733
+ const state = this.requireState();
734
+ const decorations = updateSectionHeaderDecorations(previous, document, edits, state.options).concat(state.externalDecorations);
735
+ state.document = {
736
+ textLength: document.textLength,
737
+ lineStarts: document.lineStarts,
738
+ lines: document.lines,
739
+ tokens: document.tokens,
740
+ selections,
741
+ decorations,
742
+ externalDecorations: state.externalDecorations
743
+ };
744
+ state.linesDirty = true;
745
+ state.decorationsDirty = true;
746
+ }
747
+ setBaseStyles(styles) {
748
+ if (!this.state) return;
749
+ this.state.styles = styles;
750
+ this.state.raster = null;
751
+ this.state.linesDirty = true;
752
+ this.state.decorationsDirty = true;
753
+ }
754
+ setTokens(tokens) {
755
+ if (!this.state) return;
756
+ this.state.document = {
757
+ ...this.state.document,
758
+ tokens
759
+ };
760
+ this.state.linesDirty = true;
761
+ }
762
+ updateTokenRange(patch) {
763
+ if (!this.state) return;
764
+ const tokens = replaceTokenRange(this.state.document.tokens, patch);
765
+ this.state.document = {
766
+ ...this.state.document,
767
+ tokens
768
+ };
769
+ this.state.linesDirty = true;
770
+ }
771
+ setSelections(selections) {
772
+ if (!this.state) return;
773
+ this.state.document = {
774
+ ...this.state.document,
775
+ selections
776
+ };
777
+ this.state.decorationsDirty = true;
778
+ }
779
+ setDecorations(decorations) {
780
+ if (!this.state) return;
781
+ this.state.externalDecorations = externalDecorationsFrom(decorations);
782
+ this.state.document = {
783
+ ...this.state.document,
784
+ decorations
785
+ };
786
+ this.state.decorationsDirty = true;
787
+ }
788
+ updateLayout(metrics, viewport) {
789
+ if (!this.state) return null;
790
+ this.state.metrics = metrics;
791
+ this.state.viewport = viewport;
792
+ const nextLayout = this.createRenderLayout();
793
+ const layoutChanged = !this.state.layout || !renderLayoutsEqual(this.state.layout, nextLayout);
794
+ this.state.layout = nextLayout;
795
+ if (layoutChanged) {
796
+ this.state.linesDirty = true;
797
+ this.state.decorationsDirty = true;
798
+ this.state.previousFrame = null;
799
+ }
800
+ this.resizeCanvases(this.state.layout);
801
+ return this.state.layout;
802
+ }
803
+ updateViewport(viewport) {
804
+ if (!this.state) return null;
805
+ const previous = this.state.layout;
806
+ const next = this.updateLayout(this.state.metrics, viewport);
807
+ if (!next || previous && renderLayoutsEqual(previous, next)) return null;
808
+ return next;
809
+ }
810
+ render() {
811
+ if (!this.state) return null;
812
+ const layout = this.state.layout ?? this.createRenderLayout();
813
+ this.state.layout = layout;
814
+ this.resizeCanvases(layout);
815
+ if (layout.renderMinimap === RenderMinimap.None) return emptyRenderResult();
816
+ const frame = computeFrameLayout({
817
+ renderLayout: layout,
818
+ metrics: this.state.metrics,
819
+ viewport: this.state.viewport,
820
+ lineCount: this.minimapLineCount(layout),
821
+ realLineCount: this.state.document.lineStarts.length,
822
+ previous: this.state.previousFrame
823
+ });
824
+ const frameChanged = !this.state.previousFrame || !framesPaintSameWindow(this.state.previousFrame, frame);
825
+ const surface = this.surfaceForLayout(layout);
826
+ if (this.state.linesDirty || frameChanged) this.renderLines(layout, frame, surface.mainContext);
827
+ if (this.state.decorationsDirty || frameChanged) this.renderDecorations(layout, frame, surface.decorationsContext);
828
+ this.presentSurface(layout, frame, surface);
829
+ this.state.previousFrame = frame;
830
+ this.state.linesDirty = false;
831
+ this.state.decorationsDirty = false;
832
+ return {
833
+ sliderNeeded: frame.sliderNeeded,
834
+ sliderTop: frame.sliderTop,
835
+ sliderHeight: frame.sliderHeight,
836
+ shadowVisible: this.state.viewport.scrollLeft + this.state.viewport.clientWidth < this.state.viewport.scrollWidth
837
+ };
838
+ }
839
+ dispose() {
840
+ this.state = null;
841
+ }
842
+ createRenderLayout() {
843
+ const state = this.requireState();
844
+ return computeRenderLayout({
845
+ minimap: state.options,
846
+ metrics: state.metrics,
847
+ viewport: state.viewport,
848
+ lineCount: state.document.lineStarts.length
849
+ });
850
+ }
851
+ renderLines(layout, frame, context) {
852
+ const state = this.requireState();
853
+ const raster = this.rasterForLayout(layout, context);
854
+ const previous = state.linesDirty ? null : state.previousFrame;
855
+ const reusedStart = Math.max(frame.startLineNumber, previous?.startLineNumber ?? Infinity);
856
+ const reusedEnd = Math.min(frame.endLineNumber, previous?.endLineNumber ?? -Infinity);
857
+ if (previous && reusedStart <= reusedEnd) {
858
+ const sourceY = yForLineNumber(previous, reusedStart, layout.lineHeight);
859
+ const destinationY = yForLineNumber(frame, reusedStart, layout.lineHeight);
860
+ const reusedHeight = (reusedEnd - reusedStart + 1) * layout.lineHeight;
861
+ raster.copyRows(sourceY, destinationY, reusedHeight);
862
+ raster.clearRows(0, destinationY);
863
+ raster.clearRows(destinationY + reusedHeight, raster.imageData.height);
864
+ this.renderLineRange(layout, frame, raster.imageData, frame.startLineNumber, reusedStart - 1);
865
+ this.renderLineRange(layout, frame, raster.imageData, reusedEnd + 1, frame.endLineNumber);
866
+ } else {
867
+ raster.clearRows(0, raster.imageData.height);
868
+ this.renderLineRange(layout, frame, raster.imageData, frame.startLineNumber, frame.endLineNumber);
869
+ }
870
+ context.putImageData(raster.imageData, 0, 0);
871
+ }
872
+ rasterForLayout(layout, context) {
873
+ const state = this.requireState();
874
+ const width = Math.max(1, layout.canvasInnerWidth);
875
+ const height = rasterHeight(layout);
876
+ if (state.raster?.imageData.width === width && state.raster.imageData.height === height) return state.raster;
877
+ state.raster = new RasterBuffer(context, width, height, state.styles.minimapBackground);
878
+ return state.raster;
879
+ }
880
+ renderLineRange(layout, frame, imageData, startLineNumber, endLineNumber) {
881
+ if (startLineNumber > endLineNumber) return;
882
+ const state = this.requireState();
883
+ const charRenderer = MinimapCharRendererFactory.create(layout.scale, state.styles.fontFamily);
884
+ const useLighterFont = relativeLuminance(state.styles.background) >= .5;
885
+ const renderBackground = state.styles.background;
886
+ let tokenCursor = this.tokenCursorForOffset(this.lineStartOffset(this.documentLineForRasterLine(layout, startLineNumber)));
887
+ for (let line = startLineNumber; line <= endLineNumber; line += 1) {
888
+ const documentLine = this.documentLineForRasterLine(layout, line);
889
+ const text = this.lineText(documentLine);
890
+ const lineStart = this.lineStartOffset(documentLine);
891
+ const lineEnd = lineStart + text.length;
892
+ const lineTokens = tokensForLineFromCursor(state.document.tokens, lineStart, lineEnd, tokenCursor);
893
+ tokenCursor = lineTokens.cursor;
894
+ this.renderLine({
895
+ imageData,
896
+ layout,
897
+ frame,
898
+ line,
899
+ text,
900
+ lineStart,
901
+ tokens: state.document.tokens,
902
+ tokenEnd: lineTokens.end,
903
+ tokenStart: lineTokens.start,
904
+ charRenderer,
905
+ useLighterFont,
906
+ renderBackground,
907
+ renderBackgroundAlpha: state.styles.minimapBackground.a
908
+ });
909
+ }
910
+ }
911
+ tokenCursorForOffset(offset) {
912
+ const state = this.requireState();
913
+ const tokens = state.document.tokens;
914
+ if (state.tokenIndex?.tokens !== tokens) state.tokenIndex = {
915
+ tokens,
916
+ maxEnds: tokenMaxEnds(tokens)
917
+ };
918
+ return firstTokenEndingAfter(state.tokenIndex.maxEnds, offset);
919
+ }
920
+ renderLine(options) {
921
+ const state = this.requireState();
922
+ const y = yForLineNumber(options.frame, options.line, options.layout.lineHeight);
923
+ const maxDx = options.layout.canvasInnerWidth - options.layout.charWidth;
924
+ let dx = 2;
925
+ visitTokenSegments(options.text, options.lineStart, options.tokens, options.tokenStart, options.tokenEnd, state.styles.foreground, (text, color) => {
926
+ if (dx > maxDx) return false;
927
+ dx = renderSegment({
928
+ ...options,
929
+ text,
930
+ color,
931
+ dx,
932
+ y,
933
+ maxDx
934
+ });
935
+ return dx <= maxDx;
936
+ });
937
+ }
938
+ renderDecorations(layout, frame, context) {
939
+ context.clearRect(0, 0, layout.canvasInnerWidth, rasterHeight(layout));
940
+ const lineCount = this.requireState().document.lineStarts.length;
941
+ const decorationLayout = layout.isSampling ? {
942
+ ...layout,
943
+ lineHeight: layout.canvasInnerHeight / Math.max(1, lineCount)
944
+ } : layout;
945
+ const decorationFrame = layout.isSampling ? {
946
+ ...frame,
947
+ endLineNumber: lineCount
948
+ } : frame;
949
+ this.renderSelectionHighlights(decorationLayout, decorationFrame, context);
950
+ this.renderMinimapDecorations(decorationLayout, decorationFrame, context);
951
+ this.renderSectionHeaders(decorationLayout, decorationFrame, context);
952
+ }
953
+ renderSelectionHighlights(layout, frame, context) {
954
+ const state = this.requireState();
955
+ context.fillStyle = rgbaToCss(transparent(state.styles.selection, .5));
956
+ for (const selection of state.document.selections) {
957
+ const range = this.offsetRangeToLineRange(selection.startOffset, selection.endOffset);
958
+ fillLineRange(context, layout, frame, range.start, range.end);
959
+ }
960
+ }
961
+ renderMinimapDecorations(layout, frame, context) {
962
+ const state = this.requireState();
963
+ const decorations = state.document.decorations.filter((decoration) => !decoration.sectionHeaderStyle).toSorted((left, right) => (left.zIndex ?? 0) - (right.zIndex ?? 0));
964
+ for (const decoration of decorations) {
965
+ context.fillStyle = rgbaToCss(transparent(parseCssColor(decoration.color, state.styles.selection), .5));
966
+ fillDecorationRange(context, layout, frame, decoration);
967
+ }
968
+ }
969
+ renderSectionHeaders(layout, frame, context) {
970
+ const state = this.requireState();
971
+ const fontSize = state.options.sectionHeaderFontSize * state.metrics.devicePixelRatio;
972
+ context.font = `500 ${fontSize}px ${state.styles.fontFamily}`;
973
+ context.fillStyle = rgbaToCss(transparent(state.styles.minimapBackground, .7));
974
+ context.strokeStyle = rgbaToCss(state.styles.foreground);
975
+ context.lineWidth = .4;
976
+ for (const decoration of state.document.decorations) {
977
+ if (!decoration.sectionHeaderStyle) continue;
978
+ renderSectionHeader(context, decoration, layout, frame, fontSize, state.styles.foreground);
979
+ }
980
+ }
981
+ minimapLineCount(layout) {
982
+ const state = this.requireState();
983
+ if (!layout.isSampling) return state.document.lineStarts.length;
984
+ return Math.max(1, Math.min(state.document.lineStarts.length, layout.canvasInnerHeight));
985
+ }
986
+ documentLineForRasterLine(layout, line) {
987
+ if (!layout.isSampling) return line;
988
+ const count = this.requireState().document.lineStarts.length;
989
+ return Math.floor((line - 1) * count / this.minimapLineCount(layout)) + 1;
990
+ }
991
+ lineText(lineNumber) {
992
+ return this.requireState().document.lines[lineNumber - 1]?.text ?? "";
993
+ }
994
+ lineStartOffset(lineNumber) {
995
+ const state = this.requireState();
996
+ return state.document.lineStarts[lineNumber - 1] ?? state.document.textLength;
997
+ }
998
+ offsetRangeToLineRange(startOffset, endOffset) {
999
+ return {
1000
+ start: this.lineNumberForOffset(startOffset),
1001
+ end: this.lineNumberForOffset(endOffset)
1002
+ };
1003
+ }
1004
+ lineNumberForOffset(offset) {
1005
+ const state = this.requireState();
1006
+ let low = 0;
1007
+ let high = state.document.lineStarts.length - 1;
1008
+ const clamped = Math.max(0, Math.min(offset, state.document.textLength));
1009
+ while (low <= high) {
1010
+ const middle = Math.floor((low + high) / 2);
1011
+ const start = state.document.lineStarts[middle] ?? 0;
1012
+ const next = state.document.lineStarts[middle + 1] ?? Number.POSITIVE_INFINITY;
1013
+ if (clamped < start) high = middle - 1;
1014
+ else if (clamped >= next) low = middle + 1;
1015
+ else return middle + 1;
1016
+ }
1017
+ return state.document.lineStarts.length;
1018
+ }
1019
+ resizeCanvases(layout) {
1020
+ const state = this.requireState();
1021
+ resizeCanvas(state.mainCanvas, layout.canvasInnerWidth, layout.canvasInnerHeight);
1022
+ resizeCanvas(state.decorationsCanvas, layout.canvasInnerWidth, layout.canvasInnerHeight);
1023
+ }
1024
+ surfaceForLayout(layout) {
1025
+ const state = this.requireState();
1026
+ state.surface ??= createRasterSurface();
1027
+ resizeCanvas(state.surface.mainCanvas, layout.canvasInnerWidth, rasterHeight(layout));
1028
+ resizeCanvas(state.surface.decorationsCanvas, layout.canvasInnerWidth, rasterHeight(layout));
1029
+ return state.surface;
1030
+ }
1031
+ presentSurface(layout, frame, surface) {
1032
+ const state = this.requireState();
1033
+ const offsetY = Math.round(frame.startLineFraction * layout.lineHeight);
1034
+ presentRaster(state.mainContext, surface.mainCanvas, state.mainCanvas, offsetY);
1035
+ presentRaster(state.decorationsContext, surface.decorationsCanvas, state.decorationsCanvas, offsetY);
1036
+ }
1037
+ requireState() {
1038
+ if (!this.state) throw new Error("Minimap renderer is not initialized");
1039
+ return this.state;
1040
+ }
1041
+ };
1042
+ function createRasterSurface() {
1043
+ const mainCanvas = new OffscreenCanvas(1, 1);
1044
+ const decorationsCanvas = new OffscreenCanvas(1, 1);
1045
+ const mainContext = mainCanvas.getContext("2d");
1046
+ const decorationsContext = decorationsCanvas.getContext("2d");
1047
+ if (!mainContext || !decorationsContext) throw createError({
1048
+ message: "Unable to create minimap raster surface",
1049
+ code: "MINIMAP_RASTER_CONTEXT",
1050
+ status: 500,
1051
+ why: "The browser did not provide a 2D context for the minimap raster.",
1052
+ fix: "Check OffscreenCanvas 2D support and canvas resource limits."
1053
+ });
1054
+ return {
1055
+ mainCanvas,
1056
+ decorationsCanvas,
1057
+ mainContext,
1058
+ decorationsContext
1059
+ };
1060
+ }
1061
+ function rasterHeight(layout) {
1062
+ return Math.max(1, (Math.ceil(layout.canvasInnerHeight / layout.lineHeight) + 1) * layout.lineHeight);
1063
+ }
1064
+ function presentRaster(context, source, target, offsetY) {
1065
+ context.globalCompositeOperation = "copy";
1066
+ context.imageSmoothingEnabled = true;
1067
+ context.drawImage(source, 0, offsetY, target.width, target.height, 0, 0, target.width, target.height);
1068
+ }
1069
+ function renderSegment(options) {
1070
+ let dx = options.dx;
1071
+ for (let index = 0; index < options.text.length; index += 1) {
1072
+ if (dx > options.maxDx) return dx;
1073
+ dx = renderCharacter(options, dx, options.text.charCodeAt(index));
1074
+ }
1075
+ return dx;
1076
+ }
1077
+ function renderCharacter(options, dx, code) {
1078
+ if (code === 9) return dx + 4 * options.layout.charWidth;
1079
+ if (code === 32) return dx + options.layout.charWidth;
1080
+ if (options.layout.renderMinimap === RenderMinimap.Blocks) {
1081
+ options.charRenderer.blockRenderChar(options.imageData, dx, options.y, options.color, 255, options.renderBackground, options.renderBackgroundAlpha, options.layout.lineHeight === 1);
1082
+ return dx + options.layout.charWidth;
1083
+ }
1084
+ options.charRenderer.renderChar(options.imageData, dx, options.y, code, options.color, 255, options.renderBackground, options.renderBackgroundAlpha, options.layout.scale, options.useLighterFont, options.layout.lineHeight === 1);
1085
+ return dx + options.layout.charWidth;
1086
+ }
1087
+ function visitTokenSegments(text, lineStart, tokens, tokenStart, tokenEnd, fallback, visit) {
1088
+ let cursor = 0;
1089
+ for (let index = tokenStart; index < tokenEnd; index += 1) {
1090
+ const token = tokens[index];
1091
+ const start = Math.max(0, token.start - lineStart);
1092
+ const end = Math.min(text.length, token.end - lineStart);
1093
+ if (start > cursor && !visit(text.slice(cursor, start), fallback)) return;
1094
+ if (end > start && !visit(text.slice(start, end), token.color)) return;
1095
+ cursor = Math.max(cursor, end);
1096
+ }
1097
+ if (cursor < text.length) visit(text.slice(cursor), fallback);
1098
+ }
1099
+ function tokensForLineFromCursor(tokens, lineStart, lineEnd, cursor) {
1100
+ let index = cursor;
1101
+ while (index < tokens.length && tokens[index].end <= lineStart) index += 1;
1102
+ const start = index;
1103
+ while (index < tokens.length && tokens[index].start < lineEnd) index += 1;
1104
+ return {
1105
+ start,
1106
+ end: index,
1107
+ cursor: start
1108
+ };
1109
+ }
1110
+ function tokenMaxEnds(tokens) {
1111
+ const maxEnds = new Float64Array(tokens.length);
1112
+ let maxEnd = 0;
1113
+ for (let index = 0; index < tokens.length; index += 1) {
1114
+ maxEnd = Math.max(maxEnd, tokens[index].end);
1115
+ maxEnds[index] = maxEnd;
1116
+ }
1117
+ return maxEnds;
1118
+ }
1119
+ function firstTokenEndingAfter(maxEnds, offset) {
1120
+ let low = 0;
1121
+ let high = maxEnds.length;
1122
+ while (low < high) {
1123
+ const middle = Math.floor((low + high) / 2);
1124
+ if (maxEnds[middle] <= offset) {
1125
+ low = middle + 1;
1126
+ continue;
1127
+ }
1128
+ high = middle;
1129
+ }
1130
+ return low;
1131
+ }
1132
+ function fillLineRange(context, layout, frame, startLineNumber, endLineNumber) {
1133
+ const start = Math.max(frame.startLineNumber, startLineNumber);
1134
+ const end = Math.min(frame.endLineNumber, endLineNumber);
1135
+ if (start > end) return;
1136
+ const y = yForLineNumber(frame, start, layout.lineHeight);
1137
+ const height = Math.max(layout.lineHeight, yForLineNumber(frame, end, layout.lineHeight) - y + layout.lineHeight);
1138
+ context.fillRect(2, y, layout.canvasInnerWidth, height);
1139
+ }
1140
+ function fillDecorationRange(context, layout, frame, decoration) {
1141
+ if (decoration.position === "gutter") {
1142
+ fillGutterLineRange(context, layout, frame, decoration.startLineNumber, decoration.endLineNumber);
1143
+ return;
1144
+ }
1145
+ fillLineRange(context, layout, frame, decoration.startLineNumber, decoration.endLineNumber);
1146
+ }
1147
+ function fillGutterLineRange(context, layout, frame, startLineNumber, endLineNumber) {
1148
+ const range = visibleLineRange(frame, startLineNumber, endLineNumber);
1149
+ if (!range) return;
1150
+ const y = yForLineNumber(frame, range.start, layout.lineHeight);
1151
+ const height = lineRangeHeight(layout, frame, range.end, y);
1152
+ const width = minimapRightGutterWidth(layout);
1153
+ context.fillRect(layout.canvasInnerWidth - width, y, width, height);
1154
+ }
1155
+ function visibleLineRange(frame, startLineNumber, endLineNumber) {
1156
+ const start = Math.max(frame.startLineNumber, startLineNumber);
1157
+ const end = Math.min(frame.endLineNumber, endLineNumber);
1158
+ if (start > end) return null;
1159
+ return {
1160
+ start,
1161
+ end
1162
+ };
1163
+ }
1164
+ function lineRangeHeight(layout, frame, endLineNumber, y) {
1165
+ return Math.max(layout.lineHeight, yForLineNumber(frame, endLineNumber, layout.lineHeight) - y + layout.lineHeight);
1166
+ }
1167
+ function minimapRightGutterWidth(layout) {
1168
+ const horizontalScale = layout.canvasInnerWidth / Math.max(1, layout.canvasOuterWidth);
1169
+ return Math.max(2, Math.ceil(8 * horizontalScale));
1170
+ }
1171
+ function renderSectionHeader(context, decoration, layout, frame, fontSize, color) {
1172
+ if (decoration.startLineNumber < frame.startLineNumber || decoration.startLineNumber > frame.endLineNumber) return;
1173
+ const y = yForLineNumber(frame, decoration.startLineNumber, layout.lineHeight) + fontSize;
1174
+ context.fillRect(0, y - fontSize, layout.canvasInnerWidth, fontSize * 1.5);
1175
+ context.fillStyle = rgbaToCss(color);
1176
+ if (decoration.sectionHeaderText) context.fillText(decoration.sectionHeaderText, 2, y, layout.canvasInnerWidth);
1177
+ if (decoration.sectionHeaderStyle === "underlined") {
1178
+ context.beginPath();
1179
+ context.moveTo(0, y - fontSize + 2);
1180
+ context.lineTo(layout.canvasInnerWidth, y - fontSize + 2);
1181
+ context.stroke();
1182
+ }
1183
+ }
1184
+ function resizeCanvas(canvas, width, height) {
1185
+ const safeWidth = Math.max(1, width);
1186
+ const safeHeight = Math.max(1, height);
1187
+ if (canvas.width !== safeWidth) canvas.width = safeWidth;
1188
+ if (canvas.height !== safeHeight) canvas.height = safeHeight;
1189
+ }
1190
+ function renderLayoutsEqual(left, right) {
1191
+ return left.width === right.width && left.height === right.height && left.canvasInnerWidth === right.canvasInnerWidth && left.canvasInnerHeight === right.canvasInnerHeight && left.lineHeight === right.lineHeight && left.charWidth === right.charWidth && left.scale === right.scale && left.isSampling === right.isSampling && left.heightIsEditorHeight === right.heightIsEditorHeight && left.renderMinimap === right.renderMinimap;
1192
+ }
1193
+ function framesPaintSameWindow(left, right) {
1194
+ return left.startLineNumber === right.startLineNumber && left.endLineNumber === right.endLineNumber && left.topPaddingLineCount === right.topPaddingLineCount;
1195
+ }
1196
+ function defaultMetrics() {
1197
+ return {
1198
+ rowHeight: 20,
1199
+ characterWidth: 8,
1200
+ devicePixelRatio: 1
1201
+ };
1202
+ }
1203
+ function defaultViewport() {
1204
+ return {
1205
+ scrollTop: 0,
1206
+ scrollRow: 0,
1207
+ scrollLeft: 0,
1208
+ scrollHeight: 0,
1209
+ scrollWidth: 0,
1210
+ clientHeight: 0,
1211
+ clientWidth: 0,
1212
+ minimapHeight: 0,
1213
+ visibleStart: 0,
1214
+ reservedWidth: 0,
1215
+ visibleEnd: 1
1216
+ };
1217
+ }
1218
+ function emptyRenderResult() {
1219
+ return {
1220
+ sliderNeeded: false,
1221
+ sliderTop: 0,
1222
+ sliderHeight: 0,
1223
+ shadowVisible: false
1224
+ };
1225
+ }
1226
+ function applyTextEditsToMinimapDocument(document, edits, summaryPatch) {
1227
+ let tokens = document.tokens;
1228
+ for (const edit of edits) tokens = projectMinimapTokensThroughEdit(tokens, edit, tokenProjectionText(document));
1229
+ return {
1230
+ ...applyMinimapDocumentSummaryPatch(document, summaryPatch),
1231
+ tokens
1232
+ };
1233
+ }
1234
+ function applyMinimapDocumentSummaryPatch(document, patch) {
1235
+ const startLine = Math.min(Math.max(0, patch.startLine), document.lines.length);
1236
+ const deleteCount = Math.min(Math.max(0, patch.deleteCount), document.lines.length - startLine);
1237
+ return {
1238
+ textLength: patch.textLength,
1239
+ lineStarts: patch.lineStarts ?? splicedLineStarts(document, patch, startLine, deleteCount),
1240
+ lines: document.lines.slice(0, startLine).concat(patch.lines, document.lines.slice(startLine + deleteCount))
1241
+ };
1242
+ }
1243
+ function splicedLineStarts(document, patch, startLine, deleteCount) {
1244
+ const previous = document.lineStarts;
1245
+ const next = previous.slice(0, startLine);
1246
+ let cursor = lineStartAt(previous, document.lines, startLine);
1247
+ for (const line of patch.lines) {
1248
+ next.push(cursor);
1249
+ cursor += line.length + 1;
1250
+ }
1251
+ const delta = patch.textLength - document.textLength;
1252
+ for (let index = startLine + deleteCount; index < previous.length; index += 1) next.push(previous[index] + delta);
1253
+ return next;
1254
+ }
1255
+ function lineStartAt(lineStarts, lines, line) {
1256
+ const existing = lineStarts[line];
1257
+ if (existing !== void 0) return existing;
1258
+ if (line === 0) return 0;
1259
+ return (lineStarts[line - 1] ?? 0) + (lines[line - 1]?.length ?? 0) + 1;
1260
+ }
1261
+ function lineIndexForOffset(lineStarts, offset) {
1262
+ let low = 0;
1263
+ let high = lineStarts.length - 1;
1264
+ const clamped = Math.max(0, offset);
1265
+ while (low <= high) {
1266
+ const middle = Math.floor((low + high) / 2);
1267
+ const start = lineStarts[middle] ?? 0;
1268
+ const next = lineStarts[middle + 1] ?? Number.POSITIVE_INFINITY;
1269
+ if (clamped < start) {
1270
+ high = middle - 1;
1271
+ continue;
1272
+ }
1273
+ if (clamped >= next) {
1274
+ low = middle + 1;
1275
+ continue;
1276
+ }
1277
+ return middle;
1278
+ }
1279
+ return Math.max(0, lineStarts.length - 1);
1280
+ }
1281
+ const SECTION_HEADER_EDIT_CONTEXT_LINES = 10;
1282
+ function updateSectionHeaderDecorations(previous, next, edits, options) {
1283
+ if (!options.showMarkSectionHeaders) return [];
1284
+ if (edits.length === 0) return sectionHeaderDecorationsFrom(previous.decorations);
1285
+ const oldRange = affectedOldLineRange(previous.lineStarts, edits);
1286
+ const nextRange = affectedNextLineRange(next.lineStarts, edits);
1287
+ const lineDelta = next.lineStarts.length - previous.lineStarts.length;
1288
+ const shifted = shiftedSectionHeaders(previous.decorations, oldRange, nextRange, lineDelta);
1289
+ const rescanned = rescanSectionHeaders(next, nextRange, options);
1290
+ return shifted.concat(rescanned).sort(compareDecorationsByLine);
1291
+ }
1292
+ function affectedOldLineRange(lineStarts, edits) {
1293
+ let start = Number.POSITIVE_INFINITY;
1294
+ let end = 1;
1295
+ for (const edit of edits) {
1296
+ start = Math.min(start, lineIndexForOffset(lineStarts, edit.from) + 1);
1297
+ end = Math.max(end, lineIndexForOffset(lineStarts, edit.to) + 1);
1298
+ }
1299
+ return expandLineRange({
1300
+ start,
1301
+ end
1302
+ }, lineStarts.length);
1303
+ }
1304
+ function affectedNextLineRange(lineStarts, edits) {
1305
+ let start = Number.POSITIVE_INFINITY;
1306
+ let end = 1;
1307
+ for (const edit of edits) {
1308
+ start = Math.min(start, lineIndexForOffset(lineStarts, edit.from) + 1);
1309
+ end = Math.max(end, lineIndexForOffset(lineStarts, edit.from + edit.text.length) + 1);
1310
+ }
1311
+ return expandLineRange({
1312
+ start,
1313
+ end
1314
+ }, lineStarts.length);
1315
+ }
1316
+ function expandLineRange(range, lineCount) {
1317
+ return {
1318
+ start: Math.max(1, range.start - SECTION_HEADER_EDIT_CONTEXT_LINES),
1319
+ end: Math.min(lineCount, range.end + SECTION_HEADER_EDIT_CONTEXT_LINES)
1320
+ };
1321
+ }
1322
+ function shiftedSectionHeaders(decorations, oldRange, nextRange, lineDelta) {
1323
+ return sectionHeaderDecorationsFrom(decorations).flatMap((decoration) => shiftSectionHeaderDecoration(decoration, oldRange, nextRange, lineDelta));
1324
+ }
1325
+ function shiftSectionHeaderDecoration(decoration, oldRange, nextRange, lineDelta) {
1326
+ if (lineRangesIntersect(decoration, oldRange)) return [];
1327
+ if (decoration.startLineNumber <= oldRange.end) return lineRangesIntersect(decoration, nextRange) ? [] : [decoration];
1328
+ const shifted = shiftDecoration(decoration, lineDelta);
1329
+ return lineRangesIntersect(shifted, nextRange) ? [] : [shifted];
1330
+ }
1331
+ function shiftDecoration(decoration, lineDelta) {
1332
+ if (lineDelta === 0) return decoration;
1333
+ return {
1334
+ ...decoration,
1335
+ startLineNumber: decoration.startLineNumber + lineDelta,
1336
+ endLineNumber: decoration.endLineNumber + lineDelta
1337
+ };
1338
+ }
1339
+ function rescanSectionHeaders(document, range, options) {
1340
+ return findSectionHeaderDecorationsInRange(linesForRange(document.lines, range), range.start, options);
1341
+ }
1342
+ function linesForRange(summaries, range) {
1343
+ const lines = [];
1344
+ for (let lineNumber = range.start; lineNumber <= range.end; lineNumber += 1) lines.push(summaries[lineNumber - 1]?.text ?? "");
1345
+ return lines;
1346
+ }
1347
+ function lineRangesIntersect(decoration, range) {
1348
+ return decoration.startLineNumber <= range.end && decoration.endLineNumber >= range.start;
1349
+ }
1350
+ function compareDecorationsByLine(left, right) {
1351
+ return left.startLineNumber - right.startLineNumber || left.endLineNumber - right.endLineNumber;
1352
+ }
1353
+ function externalDecorationsFrom(decorations) {
1354
+ return decorations.filter((decoration) => !decoration.sectionHeaderStyle);
1355
+ }
1356
+ function sectionHeaderDecorationsFrom(decorations) {
1357
+ return decorations.filter((decoration) => decoration.sectionHeaderStyle);
1358
+ }
1359
+ function replaceTokenRange(tokens, patch) {
1360
+ const start = clampTokenIndex(patch.start, tokens.length);
1361
+ const deleteEnd = clampTokenIndex(start + patch.deleteCount, tokens.length);
1362
+ return tokens.slice(0, start).concat(patch.tokens, tokens.slice(deleteEnd));
1363
+ }
1364
+ function clampTokenIndex(index, length) {
1365
+ return Math.min(length, Math.max(0, index));
1366
+ }
1367
+ function projectMinimapTokensThroughEdit(tokens, edit, previousText) {
1368
+ const delta = edit.text.length - (edit.to - edit.from);
1369
+ const projected = [];
1370
+ for (const token of tokens) {
1371
+ const next = projectMinimapTokenThroughEdit(token, edit, previousText, delta);
1372
+ if (isRenderableMinimapToken(next)) projected.push(next);
1373
+ }
1374
+ return projected;
1375
+ }
1376
+ function projectMinimapTokenThroughEdit(token, edit, previousText, delta) {
1377
+ if (edit.from === edit.to) return projectMinimapTokenThroughInsertion(token, edit, previousText);
1378
+ if (token.end <= edit.from) return token;
1379
+ if (token.start >= edit.to) return shiftMinimapToken(token, delta);
1380
+ if (!canResizeMinimapTokenAcrossEdit(token, edit)) return null;
1381
+ return {
1382
+ ...token,
1383
+ end: token.end + delta
1384
+ };
1385
+ }
1386
+ function projectMinimapTokenThroughInsertion(token, edit, previousText) {
1387
+ if (shouldExpandMinimapTokenForInsertion(token, edit, previousText)) return {
1388
+ ...token,
1389
+ end: token.end + edit.text.length
1390
+ };
1391
+ if (token.start >= edit.from) return shiftMinimapToken(token, edit.text.length);
1392
+ return token;
1393
+ }
1394
+ function canResizeMinimapTokenAcrossEdit(token, edit) {
1395
+ if (edit.text.includes("\n")) return false;
1396
+ return token.start < edit.from && edit.to < token.end;
1397
+ }
1398
+ function shouldExpandMinimapTokenForInsertion(token, edit, previousText) {
1399
+ if (edit.text.length === 0) return false;
1400
+ if (edit.text.includes("\n")) return false;
1401
+ if (token.start < edit.from && edit.from < token.end) return true;
1402
+ if (!isWordLikeText(edit.text)) return false;
1403
+ if (token.end === edit.from) return textHasWordBeforeOffset(previousText, edit.from);
1404
+ if (token.start === edit.from) return !textHasWordBeforeOffset(previousText, edit.from) && textHasWordCodePointAt(previousText, edit.from);
1405
+ return false;
1406
+ }
1407
+ function shiftMinimapToken(token, delta) {
1408
+ return {
1409
+ ...token,
1410
+ start: token.start + delta,
1411
+ end: token.end + delta
1412
+ };
1413
+ }
1414
+ function isRenderableMinimapToken(token) {
1415
+ if (!token) return false;
1416
+ return token.end > token.start;
1417
+ }
1418
+ function isWordLikeText(text) {
1419
+ return /^[\p{L}\p{N}_]+$/u.test(text);
1420
+ }
1421
+ function tokenProjectionText(document) {
1422
+ return {
1423
+ isWordBeforeOffset: (offset) => summaryHasWordBeforeOffset(document, offset),
1424
+ isWordCodePointAt: (offset) => summaryHasWordCodePointAt(document, offset)
1425
+ };
1426
+ }
1427
+ function documentLines(document) {
1428
+ return document.lines.map((line) => line.text);
1429
+ }
1430
+ function textHasWordBeforeOffset(text, offset) {
1431
+ if (typeof text !== "string") return text.isWordBeforeOffset(offset);
1432
+ return stringHasWordBeforeOffset(text, offset);
1433
+ }
1434
+ function textHasWordCodePointAt(text, offset) {
1435
+ if (typeof text !== "string") return text.isWordCodePointAt(offset);
1436
+ return stringHasWordCodePointAt(text, offset);
1437
+ }
1438
+ function summaryHasWordBeforeOffset(document, offset) {
1439
+ const location = summaryOffsetLocation(document, offset);
1440
+ if (!location) return false;
1441
+ return stringHasWordBeforeOffset(location.text, location.localOffset);
1442
+ }
1443
+ function summaryHasWordCodePointAt(document, offset) {
1444
+ const location = summaryOffsetLocation(document, offset);
1445
+ if (!location) return false;
1446
+ return stringHasWordCodePointAt(location.text, location.localOffset);
1447
+ }
1448
+ function summaryOffsetLocation(document, offset) {
1449
+ const clamped = Math.max(0, Math.min(offset, document.textLength));
1450
+ const lineIndex = lineIndexForOffset(document.lineStarts, clamped);
1451
+ const lineStart = document.lineStarts[lineIndex] ?? 0;
1452
+ const text = document.lines[lineIndex]?.text ?? "";
1453
+ const localOffset = clamped - lineStart;
1454
+ if (localOffset < 0 || localOffset > text.length) return null;
1455
+ return {
1456
+ text,
1457
+ localOffset
1458
+ };
1459
+ }
1460
+ function stringHasWordBeforeOffset(text, offset) {
1461
+ const previous = previousCodePointStart(text, offset);
1462
+ if (previous === null) return false;
1463
+ return stringHasWordCodePointAt(text, previous);
1464
+ }
1465
+ function stringHasWordCodePointAt(text, offset) {
1466
+ const codePoint = text.codePointAt(offset);
1467
+ if (codePoint === void 0) return false;
1468
+ return /^[\p{L}\p{N}_]$/u.test(String.fromCodePoint(codePoint));
1469
+ }
1470
+ function previousCodePointStart(text, offset) {
1471
+ if (offset <= 0) return null;
1472
+ const previous = offset - 1;
1473
+ const codeUnit = text.charCodeAt(previous);
1474
+ const beforePrevious = previous - 1;
1475
+ if (!(codeUnit >= 56320 && codeUnit <= 57343) || beforePrevious < 0) return previous;
1476
+ const previousCodeUnit = text.charCodeAt(beforePrevious);
1477
+ return previousCodeUnit >= 55296 && previousCodeUnit <= 56319 ? beforePrevious : previous;
1478
+ }
1479
+ //#endregion
1480
+ //#region src/minimap.worker.ts
1481
+ const renderer = new MinimapWorkerRenderer();
1482
+ globalThis.onmessage = (event) => {
1483
+ const request = event.data;
1484
+ try {
1485
+ measureWorkerRequest(request, () => handleRequest(request));
1486
+ } catch (error) {
1487
+ post({
1488
+ type: "error",
1489
+ message: errorMessage(error)
1490
+ });
1491
+ }
1492
+ };
1493
+ function handleRequest(request) {
1494
+ switch (request.type) {
1495
+ case "init":
1496
+ renderer.init({
1497
+ mainCanvas: request.mainCanvas,
1498
+ decorationsCanvas: request.decorationsCanvas,
1499
+ options: request.options,
1500
+ styles: request.baseStyles
1501
+ });
1502
+ return;
1503
+ case "updateBaseStyles":
1504
+ renderer.setBaseStyles(request.baseStyles);
1505
+ return;
1506
+ case "openDocument":
1507
+ case "replaceDocument":
1508
+ renderer.setDocument(request.document);
1509
+ return;
1510
+ case "applyEdit":
1511
+ renderer.applyEdit(request.edit, request.document);
1512
+ return;
1513
+ case "applyEdits":
1514
+ renderer.applyEdits(request.edits, request.document);
1515
+ return;
1516
+ case "updateTokens":
1517
+ renderer.setTokens(request.tokens);
1518
+ return;
1519
+ case "updateTokenRange":
1520
+ renderer.updateTokenRange(request.patch);
1521
+ return;
1522
+ case "updateSelection":
1523
+ renderer.setSelections(request.selections);
1524
+ return;
1525
+ case "updateDecorations":
1526
+ renderer.setDecorations(request.decorations);
1527
+ return;
1528
+ case "updateExternalDecorations":
1529
+ renderer.setExternalDecorations(request.decorations);
1530
+ return;
1531
+ case "updateLayout": {
1532
+ const layout = renderer.updateLayout(request.metrics, request.viewport);
1533
+ if (layout) post({
1534
+ type: "layout",
1535
+ sequence: 0,
1536
+ layout
1537
+ });
1538
+ return;
1539
+ }
1540
+ case "updateViewport": {
1541
+ const layout = renderer.updateViewport(request.viewport);
1542
+ if (layout) post({
1543
+ type: "layout",
1544
+ sequence: 0,
1545
+ layout
1546
+ });
1547
+ return;
1548
+ }
1549
+ case "render":
1550
+ postRender(request.sequence);
1551
+ return;
1552
+ case "dispose":
1553
+ renderer.dispose();
1554
+ post({ type: "disposed" });
1555
+ return;
1556
+ }
1557
+ }
1558
+ function postRender(sequence) {
1559
+ const result = renderer.render();
1560
+ if (!result) return;
1561
+ post({
1562
+ type: "rendered",
1563
+ sequence,
1564
+ sliderNeeded: result.sliderNeeded,
1565
+ sliderTop: result.sliderTop,
1566
+ sliderHeight: result.sliderHeight,
1567
+ shadowVisible: result.shadowVisible
1568
+ });
1569
+ }
1570
+ function post(response) {
1571
+ globalThis.postMessage(response);
1572
+ }
1573
+ function errorMessage(error) {
1574
+ if (error instanceof Error) return error.message;
1575
+ return String(error);
1576
+ }
1577
+ function measureWorkerRequest(request, run) {
1578
+ const sink = minimapWorkerDiagnosticSink();
1579
+ if (!sink) {
1580
+ run();
1581
+ return;
1582
+ }
1583
+ const start = nowMs();
1584
+ try {
1585
+ run();
1586
+ } finally {
1587
+ recordMinimapWorkerDiagnostic(sink, {
1588
+ name: "minimap.worker.request",
1589
+ durationMs: nowMs() - start,
1590
+ detail: { request: request.type }
1591
+ });
1592
+ }
1593
+ }
1594
+ function minimapWorkerDiagnosticSink() {
1595
+ const sink = minimapWorkerDiagnosticGlobal().__EDITOR_PERFORMANCE_DIAGNOSTICS__;
1596
+ if (!sink) return null;
1597
+ if (typeof sink === "function") return sink;
1598
+ if (sink.enabled !== true && typeof sink.record !== "function") return null;
1599
+ return sink;
1600
+ }
1601
+ function recordMinimapWorkerDiagnostic(sink, diagnostic) {
1602
+ if (typeof sink === "function") {
1603
+ sink(diagnostic);
1604
+ return;
1605
+ }
1606
+ sink.record?.(diagnostic);
1607
+ }
1608
+ function minimapWorkerDiagnosticGlobal() {
1609
+ return globalThis;
1610
+ }
1611
+ function nowMs() {
1612
+ return globalThis.performance?.now() ?? Date.now();
1613
+ }
1614
+ //#endregion
1615
+
1616
+ //# sourceMappingURL=minimap.worker-i4S59IOV.js.map