@lvce-editor/editor-worker 19.71.5 → 19.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2023,13 +2023,6 @@ const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
2023
2023
  return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
2024
2024
  };
2025
2025
 
2026
- const getScrollBarWidth = (width, longestLineWidth) => {
2027
- if (width > longestLineWidth) {
2028
- return 0;
2029
- }
2030
- return width ** 2 / longestLineWidth;
2031
- };
2032
-
2033
2026
  const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
2034
2027
  const halfScrollBarHeight = scrollBarHeight / 2;
2035
2028
  if (relativeY <= halfScrollBarHeight) {
@@ -2364,6 +2357,190 @@ const getDecorationClassName = type => {
2364
2357
  }
2365
2358
  };
2366
2359
 
2360
+ const getHorizontalScrollDimensions = ({
2361
+ gutterWidth = 0,
2362
+ width,
2363
+ x = 0
2364
+ }) => {
2365
+ return {
2366
+ width: Math.max(0, width - gutterWidth),
2367
+ x: x + gutterWidth
2368
+ };
2369
+ };
2370
+
2371
+ const defaultThreshold = 500;
2372
+ const getSegments = line => {
2373
+ if ('Segmenter' in Intl) {
2374
+ // @ts-ignore
2375
+ return [...new Intl.Segmenter(undefined, {
2376
+ granularity: 'grapheme'
2377
+ }).segment(line)];
2378
+ }
2379
+ const segments = [];
2380
+ let index = 0;
2381
+ for (const segment of line) {
2382
+ segments.push({
2383
+ index,
2384
+ segment
2385
+ });
2386
+ index += segment.length;
2387
+ }
2388
+ return segments;
2389
+ };
2390
+ const getCharacterWidth = (character, tabSize) => {
2391
+ if (character === '\t') {
2392
+ return tabSize;
2393
+ }
2394
+ // Grapheme clusters such as combining characters and ZWJ emoji are indivisible.
2395
+ // Treat each cluster as one display column; the extra cluster below is a safe overscan.
2396
+ return 1;
2397
+ };
2398
+ const getHorizontalDisplayColumn = (line, sourceColumn, tabSize) => {
2399
+ let displayColumn = 0;
2400
+ for (const {
2401
+ index,
2402
+ segment
2403
+ } of getSegments(line)) {
2404
+ if (index + segment.length > sourceColumn) {
2405
+ break;
2406
+ }
2407
+ displayColumn += segment === '\t' ? tabSize : segment.length;
2408
+ }
2409
+ return displayColumn;
2410
+ };
2411
+ const getHorizontalVisibleRange = (line, deltaX, width, averageCharWidth, tabSize, threshold = defaultThreshold) => {
2412
+ if (line.length <= threshold) {
2413
+ return {
2414
+ end: line.length,
2415
+ start: 0
2416
+ };
2417
+ }
2418
+ const charWidth = averageCharWidth > 0 ? averageCharWidth : 1;
2419
+ const scrollOffset = Math.max(0, Number.isFinite(deltaX) ? deltaX : 0);
2420
+ const viewportWidth = Number.isFinite(width) ? Math.max(0, width) : Infinity;
2421
+ const targetStart = scrollOffset / charWidth;
2422
+ const targetEnd = (scrollOffset + viewportWidth) / charWidth;
2423
+ let column = 0;
2424
+ let index = 0;
2425
+ let start = 0;
2426
+ let started = false;
2427
+ const segments = getSegments(line);
2428
+ for (const {
2429
+ index: segmentIndex,
2430
+ segment
2431
+ } of segments) {
2432
+ const nextColumn = column + getCharacterWidth(segment, tabSize);
2433
+ if (!started && nextColumn <= targetStart) {
2434
+ column = nextColumn;
2435
+ index = segmentIndex + segment.length;
2436
+ start = index;
2437
+ continue;
2438
+ }
2439
+ if (!started) {
2440
+ start = segmentIndex;
2441
+ started = true;
2442
+ }
2443
+ index = segmentIndex + segment.length;
2444
+ column = nextColumn;
2445
+ if (column >= targetEnd) {
2446
+ break;
2447
+ }
2448
+ }
2449
+ if (!started) {
2450
+ start = index;
2451
+ }
2452
+ // Keep one extra complete grapheme to cover fractional offsets and avoid a blank edge.
2453
+ if (index < line.length) {
2454
+ const nextSegment = segments.find(({
2455
+ index: segmentIndex
2456
+ }) => segmentIndex >= index);
2457
+ if (nextSegment) {
2458
+ index = nextSegment.index + nextSegment.segment.length;
2459
+ }
2460
+ }
2461
+ return {
2462
+ end: index,
2463
+ start
2464
+ };
2465
+ };
2466
+ const getHorizontalVisibleRangeMeasured = async (line, deltaX, width, averageCharWidth, tabSize, threshold, measureWidth) => {
2467
+ const fallback = () => {
2468
+ const range = getHorizontalVisibleRange(line, deltaX, width, averageCharWidth, tabSize, threshold);
2469
+ return {
2470
+ ...range,
2471
+ difference: getHorizontalVisibleDifference(line, range.start, deltaX, averageCharWidth, tabSize)
2472
+ };
2473
+ };
2474
+ if (line.length <= threshold) {
2475
+ return fallback();
2476
+ }
2477
+ const scrollOffset = Math.max(0, Number.isFinite(deltaX) ? deltaX : 0);
2478
+ const viewportWidth = Number.isFinite(width) ? Math.max(0, width) : Infinity;
2479
+ const segments = getSegments(line);
2480
+ const boundaries = [0, ...segments.map(({
2481
+ index,
2482
+ segment
2483
+ }) => index + segment.length)];
2484
+ const measurements = new Map([[0, 0]]);
2485
+ const measureBoundary = async boundaryIndex => {
2486
+ const cached = measurements.get(boundaryIndex);
2487
+ if (cached !== undefined) {
2488
+ return cached;
2489
+ }
2490
+ const prefix = line.slice(0, boundaries[boundaryIndex]).replaceAll('\t', () => ' '.repeat(tabSize));
2491
+ const measured = await measureWidth(prefix);
2492
+ measurements.set(boundaryIndex, measured);
2493
+ return measured;
2494
+ };
2495
+ const findGreatestBoundaryAtMost = async target => {
2496
+ let low = 0;
2497
+ let high = boundaries.length;
2498
+ while (low < high) {
2499
+ const middle = Math.floor((low + high) / 2);
2500
+ if ((await measureBoundary(middle)) <= target) {
2501
+ low = middle + 1;
2502
+ } else {
2503
+ high = middle;
2504
+ }
2505
+ }
2506
+ return Math.max(0, low - 1);
2507
+ };
2508
+ const findFirstBoundaryAtLeast = async target => {
2509
+ let low = 0;
2510
+ let high = boundaries.length;
2511
+ while (low < high) {
2512
+ const middle = Math.floor((low + high) / 2);
2513
+ if ((await measureBoundary(middle)) < target) {
2514
+ low = middle + 1;
2515
+ } else {
2516
+ high = middle;
2517
+ }
2518
+ }
2519
+ return Math.min(boundaries.length - 1, low);
2520
+ };
2521
+ try {
2522
+ const startBoundary = await findGreatestBoundaryAtMost(scrollOffset);
2523
+ const endBoundary = await findFirstBoundaryAtLeast(scrollOffset + viewportWidth);
2524
+ const start = boundaries[startBoundary];
2525
+ let end = boundaries[endBoundary];
2526
+ if (end < line.length) {
2527
+ end = boundaries[Math.min(boundaries.length - 1, endBoundary + 1)];
2528
+ }
2529
+ return {
2530
+ difference: (await measureBoundary(startBoundary)) - scrollOffset,
2531
+ end,
2532
+ start
2533
+ };
2534
+ } catch {
2535
+ return fallback();
2536
+ }
2537
+ };
2538
+ const getHorizontalVisibleDifference = (line, start, deltaX, averageCharWidth, tabSize) => {
2539
+ const charWidth = averageCharWidth > 0 ? averageCharWidth : 1;
2540
+ const scrollOffset = Number.isFinite(deltaX) ? deltaX : 0;
2541
+ return getHorizontalDisplayColumn(line, start, tabSize) * charWidth - scrollOffset;
2542
+ };
2543
+
2367
2544
  // Large files have no token cache. Only materialize the text inside the horizontal viewport.
2368
2545
  const getLargeFileVisible = editor => {
2369
2546
  const {
@@ -2762,6 +2939,31 @@ const loadTokenizers = async languageIds => {
2762
2939
  }
2763
2940
  };
2764
2941
 
2942
+ const RE_ASCII = /^\p{ASCII}*$/u;
2943
+ const isAscii = line => {
2944
+ return RE_ASCII.test(line);
2945
+ };
2946
+
2947
+ const measureTextWidthFast = async (text, charWidth) => {
2948
+ return text.length * charWidth;
2949
+ };
2950
+
2951
+ const measureTextWidthSlow = async (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
2952
+ const width = await invoke$e('TextMeasurement.measureTextWidth', text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
2953
+ return width;
2954
+ };
2955
+
2956
+ const measureTextWidth = async (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
2957
+ // TODO maybe have a property for the whole text document
2958
+ // whether the document is ASCII or not
2959
+ // so that it doesn't need to be checked on every cursor change
2960
+ // or scroll position change
2961
+ if (isMonoSpaceFont && isAscii(text)) {
2962
+ return await measureTextWidthFast(text, charWidth);
2963
+ }
2964
+ return await measureTextWidthSlow(text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
2965
+ };
2966
+
2765
2967
  const Dot = '.';
2766
2968
  const DoubleQuote$1 = '"';
2767
2969
  const EmptyString = '';
@@ -2999,6 +3201,7 @@ const getStartDefaults = (tokens, minOffset) => {
2999
3201
  let start = 0;
3000
3202
  let end = 0;
3001
3203
  let startIndex = 0;
3204
+ let found = false;
3002
3205
  const tokensLength = tokens.length;
3003
3206
  for (let i = 0; i < tokensLength; i += 2) {
3004
3207
  const tokenLength = tokens[i + 1];
@@ -3008,11 +3211,17 @@ const getStartDefaults = (tokens, minOffset) => {
3008
3211
  start -= tokenLength;
3009
3212
  end -= tokenLength;
3010
3213
  startIndex = i;
3214
+ found = true;
3011
3215
  break;
3012
3216
  }
3013
3217
  }
3218
+ if (!found) {
3219
+ return {
3220
+ start: end,
3221
+ startIndex: tokensLength
3222
+ };
3223
+ }
3014
3224
  return {
3015
- end,
3016
3225
  start,
3017
3226
  startIndex
3018
3227
  };
@@ -3063,20 +3272,24 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, decoration
3063
3272
  const embeddedTokenMap = embeddedResult.TokenMap;
3064
3273
  const tokensLength = embeddedTokens.length;
3065
3274
  let {
3066
- end,
3067
3275
  start,
3068
3276
  startIndex
3069
3277
  } = getStartDefaults(embeddedTokens, minOffset);
3070
- const difference = getDifference(start, averageCharWidth, deltaX);
3278
+ const difference = getHorizontalVisibleDifference(line, Math.max(start, minOffset), deltaX, averageCharWidth, tabSize);
3071
3279
  for (let i = startIndex; i < tokensLength; i += 2) {
3072
3280
  const tokenType = embeddedTokens[i];
3073
3281
  const tokenLength = embeddedTokens[i + 1];
3074
3282
  const tokenEnd = start + tokenLength;
3075
- const hasOverlap = hasDecorationOverlap(decorationMap, start, tokenEnd);
3283
+ const tokenStart = Math.max(start, minOffset);
3284
+ const visibleTokenEnd = Math.min(tokenEnd, maxOffset);
3285
+ if (tokenStart >= visibleTokenEnd) {
3286
+ break;
3287
+ }
3288
+ const hasOverlap = hasDecorationOverlap(decorationMap, tokenStart, visibleTokenEnd);
3076
3289
  if (hasOverlap) {
3077
3290
  // Token has decoration overlap - split into parts
3078
- let currentPos = start;
3079
- while (currentPos < tokenEnd) {
3291
+ let currentPos = tokenStart;
3292
+ while (currentPos < visibleTokenEnd) {
3080
3293
  // Find if current position is inside a decoration
3081
3294
  const activeDecoration = getActiveDecoration(decorationMap, currentPos);
3082
3295
  let partEnd;
@@ -3084,15 +3297,15 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, decoration
3084
3297
  let className;
3085
3298
  if (activeDecoration) {
3086
3299
  // Render decorated part
3087
- partEnd = Math.min(tokenEnd, activeDecoration.end);
3300
+ partEnd = Math.min(visibleTokenEnd, activeDecoration.end);
3088
3301
  text = line.slice(currentPos, partEnd);
3089
3302
  const baseTokenClass = embeddedTokenMap[tokenType] || 'Unknown';
3090
3303
  className = `Token ${baseTokenClass} ${activeDecoration.className}`;
3091
3304
  } else {
3092
3305
  // Find next decoration start or token end
3093
- let nextDecorationStart = tokenEnd;
3306
+ let nextDecorationStart = visibleTokenEnd;
3094
3307
  for (const [decorationStart] of decorationMap) {
3095
- if (decorationStart > currentPos && decorationStart < tokenEnd) {
3308
+ if (decorationStart > currentPos && decorationStart < visibleTokenEnd) {
3096
3309
  nextDecorationStart = Math.min(nextDecorationStart, decorationStart);
3097
3310
  }
3098
3311
  }
@@ -3108,14 +3321,13 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, decoration
3108
3321
  }
3109
3322
  } else {
3110
3323
  // No decoration overlap - render token normally
3111
- const text = line.slice(start, tokenEnd);
3324
+ const text = line.slice(tokenStart, visibleTokenEnd);
3112
3325
  const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
3113
3326
  const normalizedText = normalizeText(text, normalize, tabSize);
3114
3327
  lineInfo.push(normalizedText, className);
3115
3328
  }
3116
3329
  start = tokenEnd;
3117
- end = tokenEnd;
3118
- if (end >= maxOffset) {
3330
+ if (start >= maxOffset) {
3119
3331
  break;
3120
3332
  }
3121
3333
  }
@@ -3124,17 +3336,6 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, decoration
3124
3336
  lineInfo
3125
3337
  };
3126
3338
  };
3127
- const getOffsets = () => {
3128
- return {
3129
- maxOffset: Infinity,
3130
- minOffset: 0
3131
- };
3132
- };
3133
- const getDifference = (start, averageCharWidth, deltaX) => {
3134
- const beforeWidth = start * averageCharWidth;
3135
- const difference = beforeWidth - deltaX;
3136
- return difference;
3137
- };
3138
3339
  const appendTokenRange = (resultTokens, resultTokenMap, tokens, tokenMap, rangeStart, rangeEnd) => {
3139
3340
  let tokenStart = 0;
3140
3341
  for (let i = 0; i < tokens.length; i += 2) {
@@ -3190,21 +3391,25 @@ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, To
3190
3391
  tokens
3191
3392
  } = tokenResults;
3192
3393
  let {
3193
- end,
3194
3394
  start,
3195
3395
  startIndex
3196
3396
  } = getStartDefaults(tokens, minOffset);
3197
- const difference = getDifference(start, averageCharWidth, deltaX);
3397
+ const difference = getHorizontalVisibleDifference(line, Math.max(start, minOffset), deltaX, averageCharWidth, tabSize);
3198
3398
  const tokensLength = tokens.length;
3199
3399
  for (let i = startIndex; i < tokensLength; i += 2) {
3200
3400
  const tokenType = tokens[i];
3201
3401
  const tokenLength = tokens[i + 1];
3202
3402
  const tokenEnd = start + tokenLength;
3203
- const hasOverlap = hasDecorationOverlap(decorationMap, start, tokenEnd);
3403
+ const tokenStart = Math.max(start, minOffset);
3404
+ const visibleTokenEnd = Math.min(tokenEnd, maxOffset);
3405
+ if (tokenStart >= visibleTokenEnd) {
3406
+ break;
3407
+ }
3408
+ const hasOverlap = hasDecorationOverlap(decorationMap, tokenStart, visibleTokenEnd);
3204
3409
  if (hasOverlap) {
3205
3410
  // Token has decoration overlap - split into parts
3206
- let currentPos = start;
3207
- while (currentPos < tokenEnd) {
3411
+ let currentPos = tokenStart;
3412
+ while (currentPos < visibleTokenEnd) {
3208
3413
  // Find if current position is inside a decoration
3209
3414
  const activeDecoration = getActiveDecoration(decorationMap, currentPos);
3210
3415
  let partEnd;
@@ -3212,15 +3417,15 @@ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, To
3212
3417
  let className;
3213
3418
  if (activeDecoration) {
3214
3419
  // Render decorated part
3215
- partEnd = Math.min(tokenEnd, activeDecoration.end);
3420
+ partEnd = Math.min(visibleTokenEnd, activeDecoration.end);
3216
3421
  text = line.slice(currentPos, partEnd);
3217
3422
  const baseTokenClass = TokenMap[tokenType] || 'Unknown';
3218
3423
  className = `Token ${baseTokenClass} ${activeDecoration.className}`;
3219
3424
  } else {
3220
3425
  // Find next decoration start or token end
3221
- let nextDecorationStart = tokenEnd;
3426
+ let nextDecorationStart = visibleTokenEnd;
3222
3427
  for (const [decorationStart] of decorationMap) {
3223
- if (decorationStart > currentPos && decorationStart < tokenEnd) {
3428
+ if (decorationStart > currentPos && decorationStart < visibleTokenEnd) {
3224
3429
  nextDecorationStart = Math.min(nextDecorationStart, decorationStart);
3225
3430
  }
3226
3431
  }
@@ -3236,14 +3441,13 @@ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, To
3236
3441
  }
3237
3442
  } else {
3238
3443
  // No decoration overlap - render token normally
3239
- const text = line.slice(start, tokenEnd);
3444
+ const text = line.slice(tokenStart, visibleTokenEnd);
3240
3445
  const className = `Token ${TokenMap[tokenType] || 'Unknown'}`;
3241
3446
  const normalizedText = normalizeText(text, normalize, tabSize);
3242
3447
  lineInfo.push(normalizedText, className);
3243
3448
  }
3244
3449
  start = tokenEnd;
3245
- end = tokenEnd;
3246
- if (end >= maxOffset) {
3450
+ if (start >= maxOffset) {
3247
3451
  break;
3248
3452
  }
3249
3453
  }
@@ -3252,11 +3456,11 @@ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, To
3252
3456
  lineInfo
3253
3457
  };
3254
3458
  };
3255
- const getLineInfo$1 = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
3459
+ const getLineInfo$1 = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, measuredRange) => {
3256
3460
  const {
3257
- maxOffset,
3258
- minOffset
3259
- } = getOffsets();
3461
+ end: maxOffset,
3462
+ start: minOffset
3463
+ } = measuredRange;
3260
3464
  if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
3261
3465
  const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
3262
3466
  if (embeddedResult?.isFull) {
@@ -3271,9 +3475,10 @@ const getLineInfo$1 = (line, tokenResults, embeddedResults, decorations, TokenMa
3271
3475
  };
3272
3476
 
3273
3477
  // TODO need lots of tests for this
3274
- const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
3478
+ const getLineInfosViewport = async (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth, horizontalVirtualizationThreshold) => {
3275
3479
  const result = [];
3276
3480
  const differences = [];
3481
+ const horizontalVisibleRanges = [];
3277
3482
  const {
3278
3483
  decorations,
3279
3484
  languageId,
@@ -3281,10 +3486,11 @@ const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLine
3281
3486
  } = editor;
3282
3487
  const tokenMap = get$5(languageId);
3283
3488
  let offset = minLineOffset;
3284
- const tabSize = 2;
3489
+ const tabSize = editor.tabSize ?? 2;
3285
3490
  for (let i = minLineY; i < maxLineY; i++) {
3286
3491
  const line = lines[i];
3287
3492
  const normalize = shouldNormalizeText(line);
3493
+ const measuredRange = await getHorizontalVisibleRangeMeasured(line, deltaX, width, averageCharWidth, tabSize, horizontalVirtualizationThreshold, text => measureTextWidth(text, editor.fontWeight, editor.fontSize, editor.fontFamily, editor.letterSpacing, editor.isMonospaceFont, editor.charWidth));
3288
3494
 
3289
3495
  // Use decorations that were pre-computed (includes links and diagnostics)
3290
3496
  // Filter decorations to only include those for this line
@@ -3301,15 +3507,20 @@ const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLine
3301
3507
  }
3302
3508
  }
3303
3509
  const {
3304
- difference,
3305
3510
  lineInfo
3306
- } = getLineInfo$1(line, tokens[i - minLineY], embeddedResults, lineDecorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
3511
+ } = getLineInfo$1(line, tokens[i - minLineY], embeddedResults, lineDecorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth, measuredRange);
3307
3512
  result.push(lineInfo);
3308
- differences.push(difference);
3513
+ differences.push(measuredRange.difference);
3514
+ horizontalVisibleRanges.push({
3515
+ end: measuredRange.end,
3516
+ rowIndex: i,
3517
+ start: measuredRange.start
3518
+ });
3309
3519
  offset += line.length + 1;
3310
3520
  }
3311
3521
  return {
3312
3522
  differences,
3523
+ horizontalVisibleRanges,
3313
3524
  result
3314
3525
  };
3315
3526
  };
@@ -3317,11 +3528,15 @@ const getVisible$1 = async (editor, syncIncremental) => {
3317
3528
  if (editor.lifecycle?.disposed) {
3318
3529
  return {
3319
3530
  differences: [],
3531
+ horizontalVisibleRanges: [],
3320
3532
  textInfos: []
3321
3533
  };
3322
3534
  }
3323
3535
  if (editor.largeFile) {
3324
- return getLargeFileVisible(editor);
3536
+ return {
3537
+ ...(await getLargeFileVisible(editor)),
3538
+ horizontalVisibleRanges: []
3539
+ };
3325
3540
  }
3326
3541
  // TODO should separate rendering from business logic somehow
3327
3542
  // currently hard to test because need to mock editor height, top, left,
@@ -3331,15 +3546,19 @@ const getVisible$1 = async (editor, syncIncremental) => {
3331
3546
  const {
3332
3547
  charWidth,
3333
3548
  deltaX,
3334
- lines,
3335
- width
3549
+ horizontalVirtualizationThreshold = 500,
3550
+ lines
3336
3551
  } = editor;
3552
+ const {
3553
+ width
3554
+ } = getHorizontalScrollDimensions(editor);
3337
3555
  const visibleLineIndices = (editor.visibleLineIndices ?? Array.from({
3338
3556
  length: Math.max(0, Math.min(editor.maxLineY ?? editor.minLineY + editor.numberOfVisibleLines, lines.length) - editor.minLineY)
3339
3557
  }, (_, index) => editor.minLineY + index)).filter(rowIndex => rowIndex >= 0 && rowIndex < lines.length);
3340
3558
  if (visibleLineIndices.length === 0) {
3341
3559
  return {
3342
3560
  differences: [],
3561
+ horizontalVisibleRanges: [],
3343
3562
  textInfos: []
3344
3563
  };
3345
3564
  }
@@ -3356,6 +3575,7 @@ const getVisible$1 = async (editor, syncIncremental) => {
3356
3575
  if (editor.lifecycle?.disposed) {
3357
3576
  return {
3358
3577
  differences: [],
3578
+ horizontalVisibleRanges: [],
3359
3579
  textInfos: []
3360
3580
  };
3361
3581
  }
@@ -3370,6 +3590,7 @@ const getVisible$1 = async (editor, syncIncremental) => {
3370
3590
  if (editor.lifecycle?.disposed) {
3371
3591
  return {
3372
3592
  differences: [],
3593
+ horizontalVisibleRanges: [],
3373
3594
  textInfos: []
3374
3595
  };
3375
3596
  }
@@ -3377,11 +3598,15 @@ const getVisible$1 = async (editor, syncIncremental) => {
3377
3598
  const averageCharWidth = charWidth;
3378
3599
  const {
3379
3600
  differences: allDifferences,
3601
+ horizontalVisibleRanges: allHorizontalVisibleRanges,
3380
3602
  result: allTextInfos
3381
- } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
3603
+ } = await getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth, horizontalVirtualizationThreshold);
3382
3604
  const relativeIndices = visibleLineIndices.map(rowIndex => rowIndex - minLineY);
3383
3605
  return {
3384
3606
  differences: relativeIndices.map(index => allDifferences[index]),
3607
+ horizontalVisibleRanges: allHorizontalVisibleRanges.filter(({
3608
+ rowIndex
3609
+ }) => visibleLineIndices.includes(rowIndex)),
3385
3610
  textInfos: relativeIndices.map(index => allTextInfos[index])
3386
3611
  };
3387
3612
  };
@@ -3430,11 +3655,13 @@ const setDeltaY$1 = async (state, value) => {
3430
3655
  const syncIncremental = getEnabled();
3431
3656
  const {
3432
3657
  differences,
3658
+ horizontalVisibleRanges,
3433
3659
  textInfos
3434
3660
  } = await getVisible$1(newEditor1, syncIncremental);
3435
3661
  const newEditor2 = {
3436
3662
  ...newEditor1,
3437
3663
  differences,
3664
+ horizontalVisibleRanges,
3438
3665
  textInfos
3439
3666
  };
3440
3667
  return newEditor2;
@@ -3461,6 +3688,11 @@ const getIncrementalEdits = async (oldState, newState) => {
3461
3688
  } = newState;
3462
3689
  const oldLine = oldState.lines[rowIndex];
3463
3690
  const newLine = lines[rowIndex];
3691
+ // Renderer edit offsets address full lines, so clipped rows need a fresh visible window.
3692
+ const threshold = newState.horizontalVirtualizationThreshold ?? 500;
3693
+ if (newState.deltaX !== 0 || oldLine.length > threshold || newLine.length > threshold) {
3694
+ return emptyIncrementalEdits;
3695
+ }
3464
3696
  // @ts-ignore
3465
3697
  const incrementalEdits = await invoke$b(
3466
3698
  // @ts-ignore
@@ -3878,7 +4110,7 @@ const invoke$a = async (method, ...args) => {
3878
4110
  const invoke$9 = async (applicationId, method, ...args) => {
3879
4111
  return applicationId === undefined ? invoke$d(method, ...args) : invoke$d('Application.execute', applicationId, method, ...args);
3880
4112
  };
3881
- const readFile = async (applicationId, uri) => {
4113
+ const readFile$1 = async (applicationId, uri) => {
3882
4114
  return applicationId === undefined ? invoke$a('FileSystem.readFile', uri) : invoke$a('ApplicationFileSystem.execute', applicationId, 'readFile', uri);
3883
4115
  };
3884
4116
 
@@ -3911,31 +4143,6 @@ const getTabCount = string => {
3911
4143
  return count;
3912
4144
  };
3913
4145
 
3914
- const RE_ASCII = /^\p{ASCII}*$/u;
3915
- const isAscii = line => {
3916
- return RE_ASCII.test(line);
3917
- };
3918
-
3919
- const measureTextWidthFast = async (text, charWidth) => {
3920
- return text.length * charWidth;
3921
- };
3922
-
3923
- const measureTextWidthSlow = async (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
3924
- const width = await invoke$e('TextMeasurement.measureTextWidth', text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
3925
- return width;
3926
- };
3927
-
3928
- const measureTextWidth = async (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
3929
- // TODO maybe have a property for the whole text document
3930
- // whether the document is ASCII or not
3931
- // so that it doesn't need to be checked on every cursor change
3932
- // or scroll position change
3933
- if (isMonoSpaceFont && isAscii(text)) {
3934
- return await measureTextWidthFast(text, charWidth);
3935
- }
3936
- return await measureTextWidthSlow(text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
3937
- };
3938
-
3939
4146
  const getX = async (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, letterSpacing, tabSize, halfCursorWidth, width, averageCharWidth, difference = 0) => {
3940
4147
  if (!line) {
3941
4148
  return 0;
@@ -4409,11 +4616,13 @@ const scheduleDocumentAndCursorsSelections = async (editor, changes, selectionCh
4409
4616
  const syncIncremental = getEnabled();
4410
4617
  const {
4411
4618
  differences,
4619
+ horizontalVisibleRanges,
4412
4620
  textInfos
4413
4621
  } = await getVisible$1(newEditor2, syncIncremental);
4414
4622
  return {
4415
4623
  ...newEditor2,
4416
4624
  differences,
4625
+ horizontalVisibleRanges,
4417
4626
  textInfos
4418
4627
  };
4419
4628
  };
@@ -4448,11 +4657,13 @@ const scheduleDocumentAndCursorsSelectionIsUndo = async (editor, changes, select
4448
4657
  const syncIncremental = getEnabled();
4449
4658
  const {
4450
4659
  differences,
4660
+ horizontalVisibleRanges,
4451
4661
  textInfos
4452
4662
  } = await getVisible$1(finalEditor, syncIncremental);
4453
4663
  return {
4454
4664
  ...finalEditor,
4455
4665
  differences,
4666
+ horizontalVisibleRanges,
4456
4667
  textInfos
4457
4668
  };
4458
4669
  };
@@ -4492,11 +4703,13 @@ const scheduleDocument = async (editor, changes) => {
4492
4703
  const syncIncremental = getEnabled();
4493
4704
  const {
4494
4705
  differences,
4706
+ horizontalVisibleRanges,
4495
4707
  textInfos
4496
4708
  } = await getVisible$1(finalEditor, syncIncremental);
4497
4709
  return {
4498
4710
  ...finalEditor,
4499
4711
  differences,
4712
+ horizontalVisibleRanges,
4500
4713
  textInfos
4501
4714
  };
4502
4715
  // RendererProcess.send([
@@ -5140,6 +5353,69 @@ const getVisibleDiagnostics = async (editor, diagnostics) => {
5140
5353
  return visibleDiagnostics;
5141
5354
  };
5142
5355
 
5356
+ // Owned by the editor lifecycle, with only the current document's distinct lines retained.
5357
+ const caches = new WeakMap();
5358
+ const updateHorizontalLayout = async state => {
5359
+ const {
5360
+ breakPoints = [],
5361
+ deltaX,
5362
+ gutterDecorations = [],
5363
+ largeFile,
5364
+ lifecycle,
5365
+ lightBulbRowIndex = -1,
5366
+ lineNumbers
5367
+ } = state;
5368
+ if (largeFile || lifecycle?.disposed) {
5369
+ return state;
5370
+ }
5371
+ const {
5372
+ charWidth,
5373
+ fontFamily,
5374
+ fontSize,
5375
+ fontWeight,
5376
+ isMonospaceFont,
5377
+ letterSpacing,
5378
+ lines,
5379
+ tabSize,
5380
+ width
5381
+ } = state;
5382
+ const font = JSON.stringify([charWidth, fontFamily, fontSize, fontWeight, isMonospaceFont, letterSpacing, tabSize]);
5383
+ const owner = lifecycle || lines;
5384
+ const previous = caches.get(owner);
5385
+ const previousWidths = previous?.font === font ? previous.widths : undefined;
5386
+ const widths = new Map();
5387
+ let longestLineWidth = 0;
5388
+ for (const line of lines) {
5389
+ let measured = widths.get(line) ?? previousWidths?.get(line);
5390
+ if (measured === undefined) {
5391
+ measured = await measureTextWidth(line.replaceAll('\t', () => ' '.repeat(tabSize)), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
5392
+ }
5393
+ widths.set(line, measured);
5394
+ longestLineWidth = Math.max(longestLineWidth, measured);
5395
+ }
5396
+ const showGutter = lineNumbers || breakPoints.length > 0 || lightBulbRowIndex >= 0 || gutterDecorations.length > 0;
5397
+ const gutterText = String(lines.length);
5398
+ let gutterWidth = 0;
5399
+ if (showGutter) {
5400
+ const measured = widths.get(gutterText) ?? previousWidths?.get(gutterText) ?? (await measureTextWidth(gutterText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth));
5401
+ widths.set(gutterText, measured);
5402
+ gutterWidth = Math.max(30, measured + 1);
5403
+ }
5404
+ if (lifecycle?.disposed) {
5405
+ return state;
5406
+ }
5407
+ caches.set(owner, {
5408
+ font,
5409
+ widths
5410
+ });
5411
+ return {
5412
+ ...state,
5413
+ deltaX: clamp(deltaX, 0, Math.max(0, longestLineWidth - Math.max(0, width - gutterWidth))),
5414
+ gutterWidth,
5415
+ longestLineWidth
5416
+ };
5417
+ };
5418
+
5143
5419
  const shouldUpdateDiagnosticData = (oldState, newState) => {
5144
5420
  return oldState.diagnostics !== newState.diagnostics || (newState.diagnostics?.length ?? 0) > 0 && (oldState.minLineY !== newState.minLineY || oldState.charWidth !== newState.charWidth || oldState.fontFamily !== newState.fontFamily || oldState.fontSize !== newState.fontSize || oldState.fontWeight !== newState.fontWeight || oldState.isMonospaceFont !== newState.isMonospaceFont || oldState.letterSpacing !== newState.letterSpacing || oldState.lines !== newState.lines || oldState.rowHeight !== newState.rowHeight || oldState.tabSize !== newState.tabSize || oldState.width !== newState.width);
5145
5421
  };
@@ -5154,10 +5430,10 @@ const shouldUpdateBracketMatchData = (oldState, newState) => {
5154
5430
  };
5155
5431
  const shouldUpdateLightBulb = (oldState, newState) => oldState.diagnostics !== newState.diagnostics || oldState.languageId !== newState.languageId || oldState.selections !== newState.selections || oldState.uri !== newState.uri;
5156
5432
  const shouldUpdateVisibleTextData = (oldState, newState) => {
5157
- if (oldState.textInfos !== newState.textInfos || oldState.differences !== newState.differences) {
5433
+ if (oldState.deltaX === newState.deltaX && (oldState.textInfos !== newState.textInfos || oldState.differences !== newState.differences)) {
5158
5434
  return false;
5159
5435
  }
5160
- return oldState.lines !== newState.lines || oldState.tokenizerId !== newState.tokenizerId || oldState.minLineY !== newState.minLineY || oldState.maxLineY !== newState.maxLineY || oldState.visibleLineIndices !== newState.visibleLineIndices || oldState.visibleViewLineIndices !== newState.visibleViewLineIndices || oldState.decorations !== newState.decorations || oldState.embeds !== newState.embeds || oldState.deltaX !== newState.deltaX || oldState.width !== newState.width || oldState.highlightedLine !== newState.highlightedLine || oldState.foldingRanges !== newState.foldingRanges || oldState.debugEnabled !== newState.debugEnabled;
5436
+ return oldState.lines !== newState.lines || oldState.tokenizerId !== newState.tokenizerId || oldState.minLineY !== newState.minLineY || oldState.maxLineY !== newState.maxLineY || oldState.visibleLineIndices !== newState.visibleLineIndices || oldState.visibleViewLineIndices !== newState.visibleViewLineIndices || oldState.decorations !== newState.decorations || oldState.embeds !== newState.embeds || oldState.deltaX !== newState.deltaX || oldState.width !== newState.width || oldState.charWidth !== newState.charWidth || oldState.tabSize !== newState.tabSize || oldState.horizontalVirtualizationThreshold !== newState.horizontalVirtualizationThreshold || oldState.fontFamily !== newState.fontFamily || oldState.fontSize !== newState.fontSize || oldState.fontWeight !== newState.fontWeight || oldState.letterSpacing !== newState.letterSpacing || oldState.isMonospaceFont !== newState.isMonospaceFont || oldState.highlightedLine !== newState.highlightedLine || oldState.foldingRanges !== newState.foldingRanges || oldState.debugEnabled !== newState.debugEnabled;
5161
5437
  };
5162
5438
  const shouldUpdateMinimapData = (oldState, newState) => {
5163
5439
  return newState.minimapEnabled && (!oldState.minimapEnabled || oldState.lines !== newState.lines || oldState.tokenizerId !== newState.tokenizerId);
@@ -5174,7 +5450,8 @@ const mergeConflictsEqual$1 = (oldState, newState) => {
5174
5450
  });
5175
5451
  };
5176
5452
  const updateDerivedState = async (oldState, newState) => {
5177
- const layoutState = oldState.lines !== newState.lines && 'foldingRanges' in newState ? updateLayout(newState, []) : newState;
5453
+ const horizontalState = oldState.initial || oldState.lines !== newState.lines || oldState.width !== newState.width || oldState.charWidth !== newState.charWidth || oldState.fontFamily !== newState.fontFamily || oldState.fontSize !== newState.fontSize || oldState.fontWeight !== newState.fontWeight || oldState.isMonospaceFont !== newState.isMonospaceFont || oldState.letterSpacing !== newState.letterSpacing || oldState.tabSize !== newState.tabSize || oldState.lineNumbers !== newState.lineNumbers || oldState.breakPoints !== newState.breakPoints || oldState.gutterDecorations !== newState.gutterDecorations || oldState.lightBulbRowIndex !== newState.lightBulbRowIndex ? await updateHorizontalLayout(newState) : newState;
5454
+ const layoutState = oldState.lines !== newState.lines && 'foldingRanges' in newState ? updateLayout(horizontalState, []) : horizontalState;
5178
5455
  const nextState = mergeConflictsEqual$1(oldState, layoutState) ? layoutState : {
5179
5456
  ...layoutState,
5180
5457
  incrementalEdits: emptyIncrementalEdits
@@ -5186,15 +5463,17 @@ const updateDerivedState = async (oldState, newState) => {
5186
5463
  documentSymbols: await getDocumentSymbols(nextState)
5187
5464
  };
5188
5465
  }
5189
- if (shouldUpdateVisibleTextData(oldState, nextState)) {
5466
+ if (newState.deltaX !== nextState.deltaX || newState.gutterWidth !== nextState.gutterWidth || shouldUpdateVisibleTextData(oldState, nextState)) {
5190
5467
  const syncIncremental = getEnabled();
5191
5468
  const {
5192
5469
  differences,
5470
+ horizontalVisibleRanges,
5193
5471
  textInfos
5194
5472
  } = await getVisible$1(nextState, syncIncremental);
5195
5473
  finalState = {
5196
5474
  ...finalState,
5197
5475
  differences,
5476
+ horizontalVisibleRanges,
5198
5477
  textInfos
5199
5478
  };
5200
5479
  }
@@ -5907,7 +6186,7 @@ const isEqual$5 = (oldState, newState) => {
5907
6186
  };
5908
6187
 
5909
6188
  const isEqual$4 = (oldState, newState) => {
5910
- return oldState.rowHeight === newState.rowHeight && oldState.deltaY === newState.deltaY && oldState.finalDeltaY === newState.finalDeltaY && oldState.height === newState.height && oldState.deltaX === newState.deltaX && oldState.longestLineWidth === newState.longestLineWidth && oldState.minimumSliderSize === newState.minimumSliderSize && oldState.width === newState.width && oldState.scrollBarHeight === newState.scrollBarHeight;
6189
+ return oldState.rowHeight === newState.rowHeight && oldState.deltaY === newState.deltaY && oldState.finalDeltaY === newState.finalDeltaY && oldState.height === newState.height && oldState.deltaX === newState.deltaX && oldState.gutterWidth === newState.gutterWidth && oldState.longestLineWidth === newState.longestLineWidth && oldState.minimumSliderSize === newState.minimumSliderSize && oldState.width === newState.width && oldState.scrollBarHeight === newState.scrollBarHeight;
5911
6190
  };
5912
6191
 
5913
6192
  const isEqual$3 = (oldState, newState) => {
@@ -5924,7 +6203,7 @@ const isEqual$3 = (oldState, newState) => {
5924
6203
  };
5925
6204
 
5926
6205
  const isEqual$2 = (oldState, newState) => {
5927
- return oldState.breadcrumbFileIcon === newState.breadcrumbFileIcon && oldState.breadcrumbsEnabled === newState.breadcrumbsEnabled && oldState.breakPoints === newState.breakPoints && oldState.bracketMatchInfos === newState.bracketMatchInfos && oldState.cursorInfos === newState.cursorInfos && oldState.diagnostics === newState.diagnostics && oldState.documentSymbols === newState.documentSymbols && oldState.endOfLineDecorations === newState.endOfLineDecorations && oldState.focused === newState.focused && oldState.gutterDecorations === newState.gutterDecorations && oldState.highlightActiveLineNumber === newState.highlightActiveLineNumber && oldState.highlightedLine === newState.highlightedLine && oldState.problemsHighlightedRow === newState.problemsHighlightedRow && oldState.lineNumbers === newState.lineNumbers && oldState.loadError === newState.loadError && oldState.textInfos === newState.textInfos && oldState.visibleViewLineIndices === newState.visibleViewLineIndices && oldState.differences === newState.differences && oldState.initial === newState.initial && oldState.scrollBarHeight === newState.scrollBarHeight && oldState.width === newState.width && oldState.longestLineWidth === newState.longestLineWidth && oldState.minimumSliderSize === newState.minimumSliderSize && oldState.selectionInfos === newState.selectionInfos && oldState.selections === newState.selections && oldState.workspaceUri === newState.workspaceUri;
6206
+ return oldState.breadcrumbFileIcon === newState.breadcrumbFileIcon && oldState.breadcrumbsEnabled === newState.breadcrumbsEnabled && oldState.breakPoints === newState.breakPoints && oldState.bracketMatchInfos === newState.bracketMatchInfos && oldState.cursorInfos === newState.cursorInfos && oldState.diagnostics === newState.diagnostics && oldState.documentSymbols === newState.documentSymbols && oldState.endOfLineDecorations === newState.endOfLineDecorations && oldState.focused === newState.focused && oldState.gutterDecorations === newState.gutterDecorations && oldState.highlightActiveLineNumber === newState.highlightActiveLineNumber && oldState.highlightedLine === newState.highlightedLine && oldState.problemsHighlightedRow === newState.problemsHighlightedRow && oldState.lineNumbers === newState.lineNumbers && oldState.loadError === newState.loadError && oldState.textInfos === newState.textInfos && oldState.visibleViewLineIndices === newState.visibleViewLineIndices && oldState.differences === newState.differences && oldState.initial === newState.initial && oldState.scrollBarHeight === newState.scrollBarHeight && oldState.width === newState.width && oldState.gutterWidth === newState.gutterWidth && oldState.longestLineWidth === newState.longestLineWidth && oldState.minimumSliderSize === newState.minimumSliderSize && oldState.selectionInfos === newState.selectionInfos && oldState.selections === newState.selections && oldState.workspaceUri === newState.workspaceUri;
5928
6207
  };
5929
6208
 
5930
6209
  const isEqual$1 = (oldState, newState) => {
@@ -7356,7 +7635,7 @@ const at = async (editor, eventX, eventY) => {
7356
7635
  };
7357
7636
  }
7358
7637
  const rowIndex = viewLineIndices ? getDocumentRowForVisualRow$1(visualRowIndex, viewLineIndices) : getDocumentRowForVisualRow(visualRowIndex, foldingRanges);
7359
- const relativeX = eventX - x - gutterWidth + deltaX;
7638
+ const relativeX = Math.max(0, eventX - x - gutterWidth + deltaX);
7360
7639
  const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
7361
7640
  const line = lines[clampedRowIndex];
7362
7641
  const columnIndex = await getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, relativeX);
@@ -7709,7 +7988,10 @@ const wrapCommand$1 = (fn, preservesTypingCoalescing = false, returnState = true
7709
7988
  return returnState ? newEditor : undefined;
7710
7989
  }
7711
7990
  const newEditorWithDerivedState = await updateDerivedState(state, newEditor);
7712
- set$a(uid, state, newEditorWithDerivedState);
7991
+ // Another command can commit before the renderer consumes this edit.
7992
+ // Keep its last rendered baseline, including renders completed while we awaited derived state.
7993
+ const renderedState = get$8(uid)?.oldState ?? oldInstance.oldState;
7994
+ set$a(uid, renderedState, newEditorWithDerivedState);
7713
7995
  await dispose$3();
7714
7996
  if (editorDiagnosticEffect.isActive(state, newEditorWithDerivedState)) {
7715
7997
  void editorDiagnosticEffect.apply(newEditorWithDerivedState);
@@ -7853,7 +8135,7 @@ const applyWorkspaceEdit = async (editor, changes) => {
7853
8135
  }
7854
8136
  continue;
7855
8137
  }
7856
- const text = await readFile(editor.applicationId, uri);
8138
+ const text = await readFile$1(editor.applicationId, uri);
7857
8139
  const newText = applyEditsToText(text, edits);
7858
8140
  await invoke$9(editor.applicationId, 'FileSystem.writeFile', uri, newText);
7859
8141
  }
@@ -11259,56 +11541,25 @@ const handlePointerUp = async editor => {
11259
11541
  }
11260
11542
  };
11261
11543
 
11262
- // @ts-ignore
11263
-
11264
- // @ts-ignore
11265
- const getNewPercent$1 = (size, scrollBarSize, relativeX) => {
11266
- if (relativeX <= 0) {
11267
- return 0;
11268
- }
11269
- // if (relativeY <= editor.scrollBarHeight / 2) {
11270
- // // clicked at top
11271
- // return 0
11272
- // }
11273
- if (relativeX <= size - scrollBarSize / 2) {
11274
- // clicked in middle
11275
- return relativeX / (size - scrollBarSize);
11276
- }
11277
- // clicked at bottom
11278
- return 1;
11279
- };
11280
-
11281
- // @ts-ignore
11282
11544
  const handleScrollBarHorizontalMove = (state, eventX) => {
11283
11545
  const {
11284
11546
  handleOffsetX,
11285
11547
  longestLineWidth,
11548
+ minimumSliderSize
11549
+ } = state;
11550
+ const {
11286
11551
  width,
11287
11552
  x
11288
- } = state;
11289
- if (width > longestLineWidth) {
11290
- return {
11291
- ...state,
11292
- deltaX: 0,
11293
- scrollBarWidth: 0
11294
- };
11295
- }
11296
- const spaceRight = 20; // TODO make this configurable
11297
- const normalizedEventX = clamp(eventX, x, x + width);
11298
- const relativeX = normalizedEventX - x - handleOffsetX;
11299
- const scrollBarWidth = getScrollBarWidth(width, longestLineWidth);
11300
- const finalDeltaX = longestLineWidth - width + spaceRight;
11301
- const newPercent = getNewPercent$1(width, scrollBarWidth, relativeX);
11302
- const clampedPercent = clamp(newPercent, 0, 1);
11303
- const newDeltaX = clampedPercent * finalDeltaX;
11553
+ } = getHorizontalScrollDimensions(state);
11554
+ const scrollBarWidth = getScrollBarSize(width, longestLineWidth, minimumSliderSize);
11555
+ const travel = width - scrollBarWidth;
11556
+ const percent = travel > 0 ? clamp((eventX - x - handleOffsetX) / travel, 0, 1) : 0;
11304
11557
  return {
11305
11558
  ...state,
11306
- deltaX: newDeltaX
11559
+ deltaX: percent * Math.max(0, longestLineWidth - width)
11307
11560
  };
11308
11561
  };
11309
11562
 
11310
- // @ts-ignore
11311
-
11312
11563
  // TODO duplicate code with vertical pointer down event listener
11313
11564
 
11314
11565
  // @ts-ignore
@@ -11317,12 +11568,15 @@ const handleScrollBarHorizontalPointerDown = (state, eventX) => {
11317
11568
  const {
11318
11569
  deltaX,
11319
11570
  longestLineWidth,
11571
+ minimumSliderSize
11572
+ } = state;
11573
+ const {
11320
11574
  width,
11321
11575
  x
11322
- } = state;
11576
+ } = getHorizontalScrollDimensions(state);
11323
11577
  const relativeX = eventX - x;
11324
- const scrollBarWidth = getScrollBarWidth(width, longestLineWidth);
11325
- const finalDeltaX = width - scrollBarWidth;
11578
+ const scrollBarWidth = getScrollBarSize(width, longestLineWidth, minimumSliderSize);
11579
+ const finalDeltaX = Math.max(0, longestLineWidth - width);
11326
11580
  const currentScrollBarX = getScrollBarOffset(deltaX, finalDeltaX, width, scrollBarWidth);
11327
11581
  const diff = relativeX - currentScrollBarX;
11328
11582
  if (diff >= 0 && diff < scrollBarWidth) {
@@ -11455,7 +11709,7 @@ const scrollByLines = (editor, lineCount) => {
11455
11709
  };
11456
11710
 
11457
11711
  // @ts-ignore
11458
- const setDelta = (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11712
+ const setDelta = async (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11459
11713
  number(deltaMode);
11460
11714
  number(eventDeltaX);
11461
11715
  number(eventDeltaY);
@@ -11463,12 +11717,15 @@ const setDelta = (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11463
11717
  const {
11464
11718
  deltaX
11465
11719
  } = editor;
11720
+ const {
11721
+ width
11722
+ } = getHorizontalScrollDimensions(editor);
11466
11723
  if (eventDeltaX === 0) {
11467
11724
  return setDeltaY(editor, eventDeltaY);
11468
11725
  }
11469
- const newDeltaX = clamp(deltaX + eventDeltaX, 0, Infinity);
11726
+ const newDeltaX = clamp(deltaX + eventDeltaX, 0, Math.max(0, editor.longestLineWidth - width));
11470
11727
  return {
11471
- ...setDeltaY(editor, eventDeltaY),
11728
+ ...(await setDeltaY(editor, eventDeltaY)),
11472
11729
  deltaX: newDeltaX
11473
11730
  };
11474
11731
  };
@@ -16028,6 +16285,7 @@ const DEFAULT_HOVER_DELAY = 200;
16028
16285
  const DEFAULT_MESSAGE_DELAY = 3000;
16029
16286
  const MAX_MESSAGE_DELAY = 10_000;
16030
16287
  const DEFAULT_SELECTED_TEXT_OCCURRENCE_MATCHING = 'caseSensitive';
16288
+ const DEFAULT_HORIZONTAL_VIRTUALIZATION_THRESHOLD = 500;
16031
16289
  const getSelectedTextOccurrenceMatching = value => {
16032
16290
  return value === 'caseInsensitive' ? value : DEFAULT_SELECTED_TEXT_OCCURRENCE_MATCHING;
16033
16291
  };
@@ -16045,8 +16303,15 @@ const getMessageDelay = value => {
16045
16303
  }
16046
16304
  return delay;
16047
16305
  };
16306
+ const getHorizontalVirtualizationThreshold = value => {
16307
+ const threshold = typeof value === 'number' ? value : Number(value);
16308
+ if (!Number.isFinite(threshold) || threshold < 0 || threshold > 100_000) {
16309
+ return DEFAULT_HORIZONTAL_VIRTUALIZATION_THRESHOLD;
16310
+ }
16311
+ return Math.floor(threshold);
16312
+ };
16048
16313
  const getEditorPreferences = async () => {
16049
- const [diagnosticsEnabled, fontFamily, fontSize, fontWeight, formatOnSave, hoverEnabled, hoverDelay, messageDelay, isAutoClosingBracketsEnabled$1, isAutoClosingQuotesEnabled$1, isAutoClosingTagsEnabled$1, isQuickSuggestionsEnabled$1, lineNumbers, highlightActiveLineNumber, rowHeight, tabSize, letterSpacing, completionTriggerCharacters, minimapEnabled, mergeConflictActionsEnabled, breadcrumbsEnabled, insertSpaces, dragAndDropEnabled, roundedSelection, combineWhitespaceTokens, selectedTextOccurrenceMatching] = await Promise.all([get$3('editor.diagnostics'), getFontFamily(), getFontSize(), getFontWeight(), get$3('editor.formatOnSave'), get$3('editor.hover'), get$3('editor.hoverDelay'), get$3('editor.messageDelay'), isAutoClosingBracketsEnabled(), isAutoClosingQuotesEnabled(), isAutoClosingTagsEnabled(), isQuickSuggestionsEnabled(), getLineNumbers(), getHighlightActiveLineNumber(), getRowHeight(), getTabSize(), getLetterSpacing(), getCompletionTriggerCharacters(), getMinimapEnabled(), getMergeConflictActionsEnabled(), get$3('breadcrumbs.enabled'), get$3('editor.insertSpaces'), get$3('editor.dragAndDrop'), get$3('editor.roundedSelection'), get$3('editor.combineWhitespaceTokens'), get$3('editor.selectedTextOccurrenceMatching')]);
16314
+ const [diagnosticsEnabled, fontFamily, fontSize, fontWeight, formatOnSave, hoverEnabled, hoverDelay, horizontalVirtualizationThreshold, messageDelay, isAutoClosingBracketsEnabled$1, isAutoClosingQuotesEnabled$1, isAutoClosingTagsEnabled$1, isQuickSuggestionsEnabled$1, lineNumbers, highlightActiveLineNumber, rowHeight, tabSize, letterSpacing, completionTriggerCharacters, minimapEnabled, mergeConflictActionsEnabled, breadcrumbsEnabled, insertSpaces, dragAndDropEnabled, roundedSelection, combineWhitespaceTokens, selectedTextOccurrenceMatching] = await Promise.all([get$3('editor.diagnostics'), getFontFamily(), getFontSize(), getFontWeight(), get$3('editor.formatOnSave'), get$3('editor.hover'), get$3('editor.hoverDelay'), get$3('editor.horizontalVirtualizationThreshold'), get$3('editor.messageDelay'), isAutoClosingBracketsEnabled(), isAutoClosingQuotesEnabled(), isAutoClosingTagsEnabled(), isQuickSuggestionsEnabled(), getLineNumbers(), getHighlightActiveLineNumber(), getRowHeight(), getTabSize(), getLetterSpacing(), getCompletionTriggerCharacters(), getMinimapEnabled(), getMergeConflictActionsEnabled(), get$3('breadcrumbs.enabled'), get$3('editor.insertSpaces'), get$3('editor.dragAndDrop'), get$3('editor.roundedSelection'), get$3('editor.combineWhitespaceTokens'), get$3('editor.selectedTextOccurrenceMatching')]);
16050
16315
  return {
16051
16316
  breadcrumbsEnabled: breadcrumbsEnabled ?? false,
16052
16317
  combineWhitespaceTokens: combineWhitespaceTokens ?? true,
@@ -16058,6 +16323,7 @@ const getEditorPreferences = async () => {
16058
16323
  fontWeight,
16059
16324
  formatOnSave: formatOnSave ?? false,
16060
16325
  highlightActiveLineNumber,
16326
+ horizontalVirtualizationThreshold: getHorizontalVirtualizationThreshold(horizontalVirtualizationThreshold),
16061
16327
  hoverDelay: getHoverDelay(hoverDelay),
16062
16328
  hoverEnabled: hoverEnabled ?? false,
16063
16329
  insertSpaces: insertSpaces ?? true,
@@ -16388,6 +16654,40 @@ const intialize = async (syntaxHighlightingEnabled, syncIncremental) => {
16388
16654
  };
16389
16655
 
16390
16656
  const largeFileContentLength = 10 * 1024 * 1024;
16657
+ const pendingFileReads = new Map();
16658
+ const readFile = (applicationId, uri, forceReload) => {
16659
+ if (forceReload) {
16660
+ return readFile$1(applicationId, uri);
16661
+ }
16662
+ let applicationReads = pendingFileReads.get(applicationId);
16663
+ if (!applicationReads) {
16664
+ applicationReads = new Map();
16665
+ pendingFileReads.set(applicationId, applicationReads);
16666
+ }
16667
+ const pendingRead = applicationReads.get(uri);
16668
+ if (pendingRead) {
16669
+ return pendingRead;
16670
+ }
16671
+ let fileRead;
16672
+ const removePendingRead = () => {
16673
+ if (applicationReads?.get(uri) !== fileRead) {
16674
+ return;
16675
+ }
16676
+ applicationReads.delete(uri);
16677
+ if (applicationReads.size === 0) {
16678
+ pendingFileReads.delete(applicationId);
16679
+ }
16680
+ };
16681
+ fileRead = (async () => {
16682
+ try {
16683
+ return await readFile$1(applicationId, uri);
16684
+ } finally {
16685
+ removePendingRead();
16686
+ }
16687
+ })();
16688
+ applicationReads.set(uri, fileRead);
16689
+ return fileRead;
16690
+ };
16391
16691
  const getWorkspaceUri = async applicationId => {
16392
16692
  try {
16393
16693
  return await invoke$9(applicationId, 'Workspace.getPath');
@@ -16466,6 +16766,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16466
16766
  fontWeight,
16467
16767
  formatOnSave,
16468
16768
  highlightActiveLineNumber,
16769
+ horizontalVirtualizationThreshold,
16469
16770
  hoverDelay,
16470
16771
  hoverEnabled,
16471
16772
  insertSpaces,
@@ -16504,6 +16805,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16504
16805
  fontWeight,
16505
16806
  formatOnSave,
16506
16807
  highlightActiveLineNumber,
16808
+ horizontalVirtualizationThreshold,
16507
16809
  hoverDelay,
16508
16810
  hoverEnabled,
16509
16811
  insertSpaces,
@@ -16537,7 +16839,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16537
16839
  let endOfLine = existingEditor?.endOfLine || 'lf';
16538
16840
  try {
16539
16841
  if (!existingEditor) {
16540
- content = await readFile(state.applicationId, uri);
16842
+ content = await readFile(state.applicationId, uri, forceReload);
16541
16843
  endOfLine = getEndOfLine(content);
16542
16844
  content = normalizeLineEndings(content);
16543
16845
  }
@@ -16579,7 +16881,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16579
16881
  endOfLine
16580
16882
  }, x, y, width, height, 9);
16581
16883
  const newEditor2 = setText$1(newEditor1, content);
16582
- let newEditor3 = newEditor2;
16884
+ const newEditor3 = await updateHorizontalLayout(newEditor2);
16583
16885
 
16584
16886
  // Detect links and initialize decorations
16585
16887
  const linkDecorations = detectAllLinksAsDecorations(newEditor3);
@@ -16603,6 +16905,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16603
16905
  const syncIncremental = getEnabled();
16604
16906
  const {
16605
16907
  differences,
16908
+ horizontalVisibleRanges,
16606
16909
  textInfos
16607
16910
  } = await getVisible$1(newEditor3WithBreadcrumbs, syncIncremental);
16608
16911
  const newEditor4 = {
@@ -16610,6 +16913,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16610
16913
  differences,
16611
16914
  focus: FocusEditorText$1,
16612
16915
  focused: focus,
16916
+ horizontalVisibleRanges,
16613
16917
  textInfos
16614
16918
  };
16615
16919
  const completionsOnTypeRaw = await get$3('editor.completionsOnType');
@@ -16849,31 +17153,27 @@ ${editorSelector} .ScrollBarThumbHorizontal {
16849
17153
  `;
16850
17154
  };
16851
17155
 
16852
- const getScrollBarLeft = (deltaX, longestLineWidth, width) => {
16853
- const scrollBarLeft = deltaX / longestLineWidth * width;
16854
- if (!Number.isFinite(scrollBarLeft)) {
16855
- return 0;
16856
- }
16857
- return scrollBarLeft;
16858
- };
16859
17156
  const renderCss$1 = (oldState, newState) => {
16860
17157
  const {
16861
17158
  deltaX,
16862
17159
  deltaY,
16863
17160
  finalDeltaY,
17161
+ gutterWidth,
16864
17162
  height,
16865
17163
  longestLineWidth,
16866
17164
  minimumSliderSize,
16867
17165
  rowHeight,
16868
17166
  scrollBarHeight,
16869
- uid,
16870
- width
17167
+ uid
16871
17168
  } = newState;
17169
+ const {
17170
+ width
17171
+ } = getHorizontalScrollDimensions(newState);
16872
17172
  const scrollBarTop = getScrollBarY(deltaY, finalDeltaY, height, scrollBarHeight);
16873
17173
  const scrollBarWidth = getScrollBarSize(width, longestLineWidth, minimumSliderSize);
16874
- const scrollBarLeft = getScrollBarLeft(deltaX, longestLineWidth, width);
17174
+ const scrollBarLeft = getScrollBarOffset(deltaX, Math.max(0, longestLineWidth - width), width, scrollBarWidth);
16875
17175
  const css = getCss(uid, rowHeight, scrollBarHeight, scrollBarTop, scrollBarWidth, scrollBarLeft, deltaY);
16876
- return [SetCss$1, uid, css];
17176
+ return [SetCss$1, uid, gutterWidth ? `${css}\n.Editor[data-uid="${uid}"] .Gutter { width: ${gutterWidth}px; }\n` : css];
16877
17177
  };
16878
17178
 
16879
17179
  const renderFocus$2 = (oldState, newState) => {
@@ -17160,13 +17460,18 @@ const mergeConflictActions = [{
17160
17460
  action: 'both',
17161
17461
  label: 'Accept Both Changes'
17162
17462
  }];
17163
- const getUnnecessaryRanges = (diagnostics, rowIndex, textLength) => {
17463
+ const getUnnecessaryRanges = (diagnostics, rowIndex, textLength, visibleStartColumn, line, hasLine, tabSize) => {
17464
+ const visibleStart = hasLine ? getHorizontalDisplayColumn(line, visibleStartColumn, tabSize) : visibleStartColumn;
17164
17465
  return diagnostics.filter(diagnostic => diagnostic.tags?.includes(DiagnosticTag.Unnecessary)).flatMap(diagnostic => {
17165
17466
  if (rowIndex < diagnostic.rowIndex || rowIndex > diagnostic.endRowIndex) {
17166
17467
  return [];
17167
17468
  }
17168
- const start = rowIndex === diagnostic.rowIndex ? diagnostic.columnIndex : 0;
17169
- const end = rowIndex === diagnostic.endRowIndex ? diagnostic.endColumnIndex : textLength;
17469
+ const diagnosticStart = rowIndex === diagnostic.rowIndex ? diagnostic.columnIndex : 0;
17470
+ const diagnosticEnd = rowIndex === diagnostic.endRowIndex ? diagnostic.endColumnIndex : line.length;
17471
+ const startColumn = hasLine ? getHorizontalDisplayColumn(line, diagnosticStart, tabSize) : diagnosticStart;
17472
+ const endColumn = hasLine ? getHorizontalDisplayColumn(line, diagnosticEnd, tabSize) : diagnosticEnd;
17473
+ const start = Math.max(0, startColumn - visibleStart);
17474
+ const end = Math.min(textLength, endColumn - visibleStart);
17170
17475
  return start < end ? [{
17171
17476
  end,
17172
17477
  start
@@ -17227,7 +17532,7 @@ const addMergeConflictActions = (dom, rowIndex) => {
17227
17532
  }, text(label));
17228
17533
  }
17229
17534
  };
17230
- const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, highlightedLine = -1, visibleLineIndices = [], endOfLineDecorations = [], visibleViewLineIndices = [], problemsHighlightedRow = -1, diagnostics = []) => {
17535
+ const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, highlightedLine = -1, visibleLineIndices = [], endOfLineDecorations = [], visibleViewLineIndices = [], problemsHighlightedRow = -1, diagnostics = [], deltaX = 0, width = Infinity, charWidth = 1, tabSize = 2, lines = [], horizontalVirtualizationThreshold = 500, horizontalVisibleRanges = []) => {
17231
17536
  const dom = [];
17232
17537
  const actualViewRows = visibleViewLineIndices.length === 0 ? Array.from({
17233
17538
  length: textInfos.length
@@ -17241,12 +17546,14 @@ const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, h
17241
17546
  const textInfo = textInfos[textInfoIndex];
17242
17547
  const difference = differences[textInfoIndex];
17243
17548
  const rowIndex = viewRow;
17244
- const rowDecorations = endOfLineDecorations.filter(decoration => decoration.rowIndex === rowIndex);
17549
+ const line = lines[rowIndex] || '';
17550
+ const visibleRange = horizontalVisibleRanges.find(range => range.rowIndex === rowIndex) || getHorizontalVisibleRange(line, deltaX, width, charWidth, tabSize, horizontalVirtualizationThreshold);
17551
+ const rowDecorations = endOfLineDecorations.filter(decoration => decoration.rowIndex === rowIndex && (!lines[rowIndex] || visibleRange.end >= line.length));
17245
17552
  let rowTextLength = 0;
17246
17553
  for (let index = 0; index < textInfo.length; index += 2) {
17247
17554
  rowTextLength += textInfo[index].length;
17248
17555
  }
17249
- const tokenParts = getTokenParts(textInfo, getUnnecessaryRanges(diagnostics, rowIndex, rowTextLength));
17556
+ const tokenParts = getTokenParts(textInfo, getUnnecessaryRanges(diagnostics, rowIndex, rowTextLength, visibleRange.start, line, lines[rowIndex] !== undefined, tabSize));
17250
17557
  let className = EditorRow;
17251
17558
  if (rowIndex === highlightedLine) {
17252
17559
  className = mergeClassNames(className, EditorRowHighlighted);
@@ -17278,8 +17585,8 @@ const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, h
17278
17585
  return dom;
17279
17586
  };
17280
17587
 
17281
- const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true, highlightedLine = -1, visibleLineIndices = [], endOfLineDecorations = [], visibleViewLineIndices = [], problemsHighlightedRow = -1, unnecessaryDiagnostics = []) => {
17282
- const rowsDom = getEditorRowsVirtualDom$1(textInfos, differences, lineNumbers, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, problemsHighlightedRow, unnecessaryDiagnostics);
17588
+ const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true, highlightedLine = -1, visibleLineIndices = [], endOfLineDecorations = [], visibleViewLineIndices = [], problemsHighlightedRow = -1, unnecessaryDiagnostics = [], horizontalVisibleRanges = [], lines = [], tabSize = 2) => {
17589
+ const rowsDom = getEditorRowsVirtualDom$1(textInfos, differences, lineNumbers, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, problemsHighlightedRow, unnecessaryDiagnostics, 0, Infinity, 1, tabSize, lines, 500, horizontalVisibleRanges);
17283
17590
  return [{
17284
17591
  childCount: visibleViewLineIndices.length || textInfos.length,
17285
17592
  className: 'EditorRows',
@@ -17376,8 +17683,8 @@ const editorLayersNode = {
17376
17683
  className: 'EditorLayers',
17377
17684
  type: Div
17378
17685
  };
17379
- const getEditorLayersVirtualDom = (selectionInfos, textInfos, differences, lineNumbers = true, highlightedLine = -1, cursorInfos = [], diagnostics = [], visibleLineIndices = [], endOfLineDecorations = [], bracketMatchInfos = [], focused = true, visibleViewLineIndices = [], problemsHighlightedRow = -1, roundedSelection = false, unnecessaryDiagnostics = []) => {
17380
- return [editorLayersNode, ...getEditorSelectionsVirtualDom(selectionInfos, focused, roundedSelection), ...getEditorRowsVirtualDom(textInfos, differences, lineNumbers, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, problemsHighlightedRow, unnecessaryDiagnostics), ...getEditorCursorsVirtualDom(cursorInfos, focused), ...getEditorDiagnosticsVirtualDom(diagnostics, bracketMatchInfos)];
17686
+ const getEditorLayersVirtualDom = (selectionInfos, textInfos, differences, lineNumbers = true, highlightedLine = -1, cursorInfos = [], diagnostics = [], visibleLineIndices = [], endOfLineDecorations = [], bracketMatchInfos = [], focused = true, visibleViewLineIndices = [], problemsHighlightedRow = -1, roundedSelection = false, unnecessaryDiagnostics = [], horizontalVisibleRanges = [], lines = [], tabSize = 2) => {
17687
+ return [editorLayersNode, ...getEditorSelectionsVirtualDom(selectionInfos, focused, roundedSelection), ...getEditorRowsVirtualDom(textInfos, differences, lineNumbers, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, problemsHighlightedRow, unnecessaryDiagnostics, horizontalVisibleRanges, lines, tabSize), ...getEditorCursorsVirtualDom(cursorInfos, focused), ...getEditorDiagnosticsVirtualDom(diagnostics, bracketMatchInfos)];
17381
17688
  };
17382
17689
 
17383
17690
  const getEditorScrollBarDiagnosticsVirtualDom = scrollBarDiagnostics => {
@@ -17450,13 +17757,16 @@ const getEditorContentVirtualDom = ({
17450
17757
  endOfLineDecorations = [],
17451
17758
  focused = true,
17452
17759
  highlightedLine = -1,
17760
+ horizontalVisibleRanges = [],
17453
17761
  lineNumbers = true,
17762
+ lines = [],
17454
17763
  problemsHighlightedRow = -1,
17455
17764
  roundedSelection = false,
17456
17765
  scrollBarDiagnostics = [],
17457
17766
  scrollBarHeight = 0,
17458
17767
  scrollBarWidth = 0,
17459
17768
  selectionInfos = [],
17769
+ tabSize = 2,
17460
17770
  textInfos,
17461
17771
  unnecessaryDiagnostics = [],
17462
17772
  visibleLineIndices = [],
@@ -17465,7 +17775,7 @@ const getEditorContentVirtualDom = ({
17465
17775
  return [{
17466
17776
  ...editorContentNode,
17467
17777
  className: breadcrumbsEnabled ? 'EditorContent EditorBreadcrumbsOffset' : 'EditorContent'
17468
- }, ...getEditorInputVirtualDom(), ...getEditorLayersVirtualDom(selectionInfos, textInfos, differences, lineNumbers, highlightedLine, cursorInfos, diagnostics, visibleLineIndices, endOfLineDecorations, bracketMatchInfos, focused, visibleViewLineIndices, problemsHighlightedRow, roundedSelection, unnecessaryDiagnostics), ...getEditorScrollBarDiagnosticsVirtualDom(scrollBarDiagnostics), ...getScrollBarVirtualDom(scrollBarHeight, scrollBarWidth)];
17778
+ }, ...getEditorInputVirtualDom(), ...getEditorLayersVirtualDom(selectionInfos, textInfos, differences, lineNumbers, highlightedLine, cursorInfos, diagnostics, visibleLineIndices, endOfLineDecorations, bracketMatchInfos, focused, visibleViewLineIndices, problemsHighlightedRow, roundedSelection, unnecessaryDiagnostics, horizontalVisibleRanges, lines, tabSize), ...getEditorScrollBarDiagnosticsVirtualDom(scrollBarDiagnostics), ...getScrollBarVirtualDom(scrollBarHeight, scrollBarWidth)];
17469
17779
  };
17470
17780
 
17471
17781
  const getGutterInfoVirtualDom = (gutterInfo, activeLineNumber) => {
@@ -17619,6 +17929,7 @@ const getEditorVirtualDom = ({
17619
17929
  gutterInfos = [],
17620
17930
  highlightActiveLineNumber = true,
17621
17931
  highlightedLine = -1,
17932
+ horizontalVisibleRanges = [],
17622
17933
  lightBulbRowIndex = -1,
17623
17934
  lineNumbers = true,
17624
17935
  lines = [],
@@ -17635,6 +17946,7 @@ const getEditorVirtualDom = ({
17635
17946
  scrollBarWidth = 0,
17636
17947
  selectionInfos = [],
17637
17948
  selections = new Uint32Array(),
17949
+ tabSize = 2,
17638
17950
  textInfos,
17639
17951
  uid,
17640
17952
  unnecessaryDiagnostics = [],
@@ -17687,13 +17999,16 @@ const getEditorVirtualDom = ({
17687
17999
  endOfLineDecorations,
17688
18000
  focused,
17689
18001
  highlightedLine,
18002
+ horizontalVisibleRanges,
17690
18003
  lineNumbers,
18004
+ lines,
17691
18005
  problemsHighlightedRow,
17692
18006
  roundedSelection,
17693
18007
  scrollBarDiagnostics,
17694
18008
  scrollBarHeight,
17695
18009
  scrollBarWidth,
17696
18010
  selectionInfos,
18011
+ tabSize,
17697
18012
  textInfos: combineWhitespaceTokens$1 ? textInfos.map(combineWhitespaceTokens) : textInfos,
17698
18013
  unnecessaryDiagnostics,
17699
18014
  visibleLineIndices: visibleLineIndices || [],
@@ -17861,12 +18176,14 @@ const getDom = state => {
17861
18176
  longestLineWidth,
17862
18177
  minimumSliderSize,
17863
18178
  textInfos,
17864
- visualDecorations = [],
17865
- width
18179
+ visualDecorations = []
17866
18180
  } = state;
17867
18181
  if (initial && textInfos.length === 0) {
17868
18182
  return [];
17869
18183
  }
18184
+ const {
18185
+ width
18186
+ } = getHorizontalScrollDimensions(state);
17870
18187
  return getEditorVirtualDom({
17871
18188
  ...state,
17872
18189
  diagnostics: visualDecorations,
@@ -18048,10 +18365,10 @@ const renderLines = {
18048
18365
  visibleLineIndices,
18049
18366
  visibleViewLineIndices
18050
18367
  } = newState;
18051
- const dom = getEditorRowsVirtualDom$1(textInfos, differences, true, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, newState.problemsHighlightedRow, diagnostics || []);
18368
+ const dom = getEditorRowsVirtualDom$1(textInfos, differences, true, highlightedLine, visibleLineIndices, endOfLineDecorations, visibleViewLineIndices, newState.problemsHighlightedRow, diagnostics || [], newState.deltaX, newState.width, newState.charWidth, newState.tabSize, newState.lines, newState.horizontalVirtualizationThreshold, newState.horizontalVisibleRanges);
18052
18369
  return [/* method */'setText', dom];
18053
18370
  },
18054
- isEqual: (oldState, newState) => oldState.lines === newState.lines && oldState.foldingRanges === newState.foldingRanges && oldState.visibleViewLineIndices === newState.visibleViewLineIndices && oldState.tokenizerId === newState.tokenizerId && oldState.minLineY === newState.minLineY && oldState.decorations === newState.decorations && oldState.embeds === newState.embeds && oldState.endOfLineDecorations === newState.endOfLineDecorations && oldState.diagnostics === newState.diagnostics && oldState.deltaX === newState.deltaX && oldState.width === newState.width && oldState.highlightedLine === newState.highlightedLine && oldState.problemsHighlightedRow === newState.problemsHighlightedRow && oldState.debugEnabled === newState.debugEnabled
18371
+ isEqual: (oldState, newState) => oldState.lines === newState.lines && oldState.foldingRanges === newState.foldingRanges && oldState.visibleViewLineIndices === newState.visibleViewLineIndices && oldState.tokenizerId === newState.tokenizerId && oldState.minLineY === newState.minLineY && oldState.decorations === newState.decorations && oldState.embeds === newState.embeds && oldState.endOfLineDecorations === newState.endOfLineDecorations && oldState.diagnostics === newState.diagnostics && oldState.deltaX === newState.deltaX && oldState.charWidth === newState.charWidth && oldState.tabSize === newState.tabSize && oldState.gutterWidth === newState.gutterWidth && oldState.horizontalVirtualizationThreshold === newState.horizontalVirtualizationThreshold && oldState.fontFamily === newState.fontFamily && oldState.fontSize === newState.fontSize && oldState.fontWeight === newState.fontWeight && oldState.letterSpacing === newState.letterSpacing && oldState.isMonospaceFont === newState.isMonospaceFont && oldState.width === newState.width && oldState.highlightedLine === newState.highlightedLine && oldState.problemsHighlightedRow === newState.problemsHighlightedRow && oldState.debugEnabled === newState.debugEnabled
18055
18372
  };
18056
18373
  const renderSelections = {
18057
18374
  apply: (oldState, newState) => {
@@ -791,6 +791,16 @@
791
791
  "minimum": 0,
792
792
  "maximum": 10000
793
793
  },
794
+ {
795
+ "category": "text-editor",
796
+ "description": "Controls the minimum line length in characters before horizontal virtualization is enabled",
797
+ "heading": "Horizontal Virtualization Threshold",
798
+ "id": "editor.horizontalVirtualizationThreshold",
799
+ "type": "number",
800
+ "value": 500,
801
+ "minimum": 0,
802
+ "maximum": 100000
803
+ },
794
804
  {
795
805
  "category": "text-editor",
796
806
  "description": "Controls how long editor messages remain visible, in milliseconds",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/editor-worker",
3
- "version": "19.71.5",
3
+ "version": "19.72.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git@github.com:lvce-editor/editor-worker.git"