@lvce-editor/editor-worker 19.71.6 → 19.72.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.
@@ -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,20 +3201,27 @@ 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];
3005
3208
  end += tokenLength;
3006
3209
  start = end;
3007
- if (start >= minOffset) {
3210
+ if (start > 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);
@@ -7856,7 +8135,7 @@ const applyWorkspaceEdit = async (editor, changes) => {
7856
8135
  }
7857
8136
  continue;
7858
8137
  }
7859
- const text = await readFile(editor.applicationId, uri);
8138
+ const text = await readFile$1(editor.applicationId, uri);
7860
8139
  const newText = applyEditsToText(text, edits);
7861
8140
  await invoke$9(editor.applicationId, 'FileSystem.writeFile', uri, newText);
7862
8141
  }
@@ -11262,56 +11541,25 @@ const handlePointerUp = async editor => {
11262
11541
  }
11263
11542
  };
11264
11543
 
11265
- // @ts-ignore
11266
-
11267
- // @ts-ignore
11268
- const getNewPercent$1 = (size, scrollBarSize, relativeX) => {
11269
- if (relativeX <= 0) {
11270
- return 0;
11271
- }
11272
- // if (relativeY <= editor.scrollBarHeight / 2) {
11273
- // // clicked at top
11274
- // return 0
11275
- // }
11276
- if (relativeX <= size - scrollBarSize / 2) {
11277
- // clicked in middle
11278
- return relativeX / (size - scrollBarSize);
11279
- }
11280
- // clicked at bottom
11281
- return 1;
11282
- };
11283
-
11284
- // @ts-ignore
11285
11544
  const handleScrollBarHorizontalMove = (state, eventX) => {
11286
11545
  const {
11287
11546
  handleOffsetX,
11288
11547
  longestLineWidth,
11548
+ minimumSliderSize
11549
+ } = state;
11550
+ const {
11289
11551
  width,
11290
11552
  x
11291
- } = state;
11292
- if (width > longestLineWidth) {
11293
- return {
11294
- ...state,
11295
- deltaX: 0,
11296
- scrollBarWidth: 0
11297
- };
11298
- }
11299
- const spaceRight = 20; // TODO make this configurable
11300
- const normalizedEventX = clamp(eventX, x, x + width);
11301
- const relativeX = normalizedEventX - x - handleOffsetX;
11302
- const scrollBarWidth = getScrollBarWidth(width, longestLineWidth);
11303
- const finalDeltaX = longestLineWidth - width + spaceRight;
11304
- const newPercent = getNewPercent$1(width, scrollBarWidth, relativeX);
11305
- const clampedPercent = clamp(newPercent, 0, 1);
11306
- 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;
11307
11557
  return {
11308
11558
  ...state,
11309
- deltaX: newDeltaX
11559
+ deltaX: percent * Math.max(0, longestLineWidth - width)
11310
11560
  };
11311
11561
  };
11312
11562
 
11313
- // @ts-ignore
11314
-
11315
11563
  // TODO duplicate code with vertical pointer down event listener
11316
11564
 
11317
11565
  // @ts-ignore
@@ -11320,12 +11568,15 @@ const handleScrollBarHorizontalPointerDown = (state, eventX) => {
11320
11568
  const {
11321
11569
  deltaX,
11322
11570
  longestLineWidth,
11571
+ minimumSliderSize
11572
+ } = state;
11573
+ const {
11323
11574
  width,
11324
11575
  x
11325
- } = state;
11576
+ } = getHorizontalScrollDimensions(state);
11326
11577
  const relativeX = eventX - x;
11327
- const scrollBarWidth = getScrollBarWidth(width, longestLineWidth);
11328
- const finalDeltaX = width - scrollBarWidth;
11578
+ const scrollBarWidth = getScrollBarSize(width, longestLineWidth, minimumSliderSize);
11579
+ const finalDeltaX = Math.max(0, longestLineWidth - width);
11329
11580
  const currentScrollBarX = getScrollBarOffset(deltaX, finalDeltaX, width, scrollBarWidth);
11330
11581
  const diff = relativeX - currentScrollBarX;
11331
11582
  if (diff >= 0 && diff < scrollBarWidth) {
@@ -11458,7 +11709,7 @@ const scrollByLines = (editor, lineCount) => {
11458
11709
  };
11459
11710
 
11460
11711
  // @ts-ignore
11461
- const setDelta = (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11712
+ const setDelta = async (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11462
11713
  number(deltaMode);
11463
11714
  number(eventDeltaX);
11464
11715
  number(eventDeltaY);
@@ -11466,12 +11717,15 @@ const setDelta = (editor, deltaMode, eventDeltaX, eventDeltaY) => {
11466
11717
  const {
11467
11718
  deltaX
11468
11719
  } = editor;
11720
+ const {
11721
+ width
11722
+ } = getHorizontalScrollDimensions(editor);
11469
11723
  if (eventDeltaX === 0) {
11470
11724
  return setDeltaY(editor, eventDeltaY);
11471
11725
  }
11472
- const newDeltaX = clamp(deltaX + eventDeltaX, 0, Infinity);
11726
+ const newDeltaX = clamp(deltaX + eventDeltaX, 0, Math.max(0, editor.longestLineWidth - width));
11473
11727
  return {
11474
- ...setDeltaY(editor, eventDeltaY),
11728
+ ...(await setDeltaY(editor, eventDeltaY)),
11475
11729
  deltaX: newDeltaX
11476
11730
  };
11477
11731
  };
@@ -16031,6 +16285,7 @@ const DEFAULT_HOVER_DELAY = 200;
16031
16285
  const DEFAULT_MESSAGE_DELAY = 3000;
16032
16286
  const MAX_MESSAGE_DELAY = 10_000;
16033
16287
  const DEFAULT_SELECTED_TEXT_OCCURRENCE_MATCHING = 'caseSensitive';
16288
+ const DEFAULT_HORIZONTAL_VIRTUALIZATION_THRESHOLD = 500;
16034
16289
  const getSelectedTextOccurrenceMatching = value => {
16035
16290
  return value === 'caseInsensitive' ? value : DEFAULT_SELECTED_TEXT_OCCURRENCE_MATCHING;
16036
16291
  };
@@ -16048,8 +16303,15 @@ const getMessageDelay = value => {
16048
16303
  }
16049
16304
  return delay;
16050
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
+ };
16051
16313
  const getEditorPreferences = async () => {
16052
- 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')]);
16053
16315
  return {
16054
16316
  breadcrumbsEnabled: breadcrumbsEnabled ?? false,
16055
16317
  combineWhitespaceTokens: combineWhitespaceTokens ?? true,
@@ -16061,6 +16323,7 @@ const getEditorPreferences = async () => {
16061
16323
  fontWeight,
16062
16324
  formatOnSave: formatOnSave ?? false,
16063
16325
  highlightActiveLineNumber,
16326
+ horizontalVirtualizationThreshold: getHorizontalVirtualizationThreshold(horizontalVirtualizationThreshold),
16064
16327
  hoverDelay: getHoverDelay(hoverDelay),
16065
16328
  hoverEnabled: hoverEnabled ?? false,
16066
16329
  insertSpaces: insertSpaces ?? true,
@@ -16391,6 +16654,40 @@ const intialize = async (syntaxHighlightingEnabled, syncIncremental) => {
16391
16654
  };
16392
16655
 
16393
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
+ };
16394
16691
  const getWorkspaceUri = async applicationId => {
16395
16692
  try {
16396
16693
  return await invoke$9(applicationId, 'Workspace.getPath');
@@ -16469,6 +16766,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16469
16766
  fontWeight,
16470
16767
  formatOnSave,
16471
16768
  highlightActiveLineNumber,
16769
+ horizontalVirtualizationThreshold,
16472
16770
  hoverDelay,
16473
16771
  hoverEnabled,
16474
16772
  insertSpaces,
@@ -16507,6 +16805,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16507
16805
  fontWeight,
16508
16806
  formatOnSave,
16509
16807
  highlightActiveLineNumber,
16808
+ horizontalVirtualizationThreshold,
16510
16809
  hoverDelay,
16511
16810
  hoverEnabled,
16512
16811
  insertSpaces,
@@ -16540,7 +16839,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16540
16839
  let endOfLine = existingEditor?.endOfLine || 'lf';
16541
16840
  try {
16542
16841
  if (!existingEditor) {
16543
- content = await readFile(state.applicationId, uri);
16842
+ content = await readFile(state.applicationId, uri, forceReload);
16544
16843
  endOfLine = getEndOfLine(content);
16545
16844
  content = normalizeLineEndings(content);
16546
16845
  }
@@ -16582,7 +16881,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16582
16881
  endOfLine
16583
16882
  }, x, y, width, height, 9);
16584
16883
  const newEditor2 = setText$1(newEditor1, content);
16585
- let newEditor3 = newEditor2;
16884
+ const newEditor3 = await updateHorizontalLayout(newEditor2);
16586
16885
 
16587
16886
  // Detect links and initialize decorations
16588
16887
  const linkDecorations = detectAllLinksAsDecorations(newEditor3);
@@ -16606,6 +16905,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16606
16905
  const syncIncremental = getEnabled();
16607
16906
  const {
16608
16907
  differences,
16908
+ horizontalVisibleRanges,
16609
16909
  textInfos
16610
16910
  } = await getVisible$1(newEditor3WithBreadcrumbs, syncIncremental);
16611
16911
  const newEditor4 = {
@@ -16613,6 +16913,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
16613
16913
  differences,
16614
16914
  focus: FocusEditorText$1,
16615
16915
  focused: focus,
16916
+ horizontalVisibleRanges,
16616
16917
  textInfos
16617
16918
  };
16618
16919
  const completionsOnTypeRaw = await get$3('editor.completionsOnType');
@@ -16852,31 +17153,27 @@ ${editorSelector} .ScrollBarThumbHorizontal {
16852
17153
  `;
16853
17154
  };
16854
17155
 
16855
- const getScrollBarLeft = (deltaX, longestLineWidth, width) => {
16856
- const scrollBarLeft = deltaX / longestLineWidth * width;
16857
- if (!Number.isFinite(scrollBarLeft)) {
16858
- return 0;
16859
- }
16860
- return scrollBarLeft;
16861
- };
16862
17156
  const renderCss$1 = (oldState, newState) => {
16863
17157
  const {
16864
17158
  deltaX,
16865
17159
  deltaY,
16866
17160
  finalDeltaY,
17161
+ gutterWidth,
16867
17162
  height,
16868
17163
  longestLineWidth,
16869
17164
  minimumSliderSize,
16870
17165
  rowHeight,
16871
17166
  scrollBarHeight,
16872
- uid,
16873
- width
17167
+ uid
16874
17168
  } = newState;
17169
+ const {
17170
+ width
17171
+ } = getHorizontalScrollDimensions(newState);
16875
17172
  const scrollBarTop = getScrollBarY(deltaY, finalDeltaY, height, scrollBarHeight);
16876
17173
  const scrollBarWidth = getScrollBarSize(width, longestLineWidth, minimumSliderSize);
16877
- const scrollBarLeft = getScrollBarLeft(deltaX, longestLineWidth, width);
17174
+ const scrollBarLeft = getScrollBarOffset(deltaX, Math.max(0, longestLineWidth - width), width, scrollBarWidth);
16878
17175
  const css = getCss(uid, rowHeight, scrollBarHeight, scrollBarTop, scrollBarWidth, scrollBarLeft, deltaY);
16879
- return [SetCss$1, uid, css];
17176
+ return [SetCss$1, uid, gutterWidth ? `${css}\n.Editor[data-uid="${uid}"] .Gutter { width: ${gutterWidth}px; }\n` : css];
16880
17177
  };
16881
17178
 
16882
17179
  const renderFocus$2 = (oldState, newState) => {
@@ -17163,13 +17460,18 @@ const mergeConflictActions = [{
17163
17460
  action: 'both',
17164
17461
  label: 'Accept Both Changes'
17165
17462
  }];
17166
- const getUnnecessaryRanges = (diagnostics, rowIndex, textLength) => {
17463
+ const getUnnecessaryRanges = (diagnostics, rowIndex, textLength, visibleStartColumn, line, hasLine, tabSize) => {
17464
+ const visibleStart = hasLine ? getHorizontalDisplayColumn(line, visibleStartColumn, tabSize) : visibleStartColumn;
17167
17465
  return diagnostics.filter(diagnostic => diagnostic.tags?.includes(DiagnosticTag.Unnecessary)).flatMap(diagnostic => {
17168
17466
  if (rowIndex < diagnostic.rowIndex || rowIndex > diagnostic.endRowIndex) {
17169
17467
  return [];
17170
17468
  }
17171
- const start = rowIndex === diagnostic.rowIndex ? diagnostic.columnIndex : 0;
17172
- 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);
17173
17475
  return start < end ? [{
17174
17476
  end,
17175
17477
  start
@@ -17230,7 +17532,7 @@ const addMergeConflictActions = (dom, rowIndex) => {
17230
17532
  }, text(label));
17231
17533
  }
17232
17534
  };
17233
- 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 = []) => {
17234
17536
  const dom = [];
17235
17537
  const actualViewRows = visibleViewLineIndices.length === 0 ? Array.from({
17236
17538
  length: textInfos.length
@@ -17244,12 +17546,14 @@ const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, h
17244
17546
  const textInfo = textInfos[textInfoIndex];
17245
17547
  const difference = differences[textInfoIndex];
17246
17548
  const rowIndex = viewRow;
17247
- 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));
17248
17552
  let rowTextLength = 0;
17249
17553
  for (let index = 0; index < textInfo.length; index += 2) {
17250
17554
  rowTextLength += textInfo[index].length;
17251
17555
  }
17252
- const tokenParts = getTokenParts(textInfo, getUnnecessaryRanges(diagnostics, rowIndex, rowTextLength));
17556
+ const tokenParts = getTokenParts(textInfo, getUnnecessaryRanges(diagnostics, rowIndex, rowTextLength, visibleRange.start, line, lines[rowIndex] !== undefined, tabSize));
17253
17557
  let className = EditorRow;
17254
17558
  if (rowIndex === highlightedLine) {
17255
17559
  className = mergeClassNames(className, EditorRowHighlighted);
@@ -17281,8 +17585,8 @@ const getEditorRowsVirtualDom$1 = (textInfos, differences, lineNumbers = true, h
17281
17585
  return dom;
17282
17586
  };
17283
17587
 
17284
- const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true, highlightedLine = -1, visibleLineIndices = [], endOfLineDecorations = [], visibleViewLineIndices = [], problemsHighlightedRow = -1, unnecessaryDiagnostics = []) => {
17285
- 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);
17286
17590
  return [{
17287
17591
  childCount: visibleViewLineIndices.length || textInfos.length,
17288
17592
  className: 'EditorRows',
@@ -17379,8 +17683,8 @@ const editorLayersNode = {
17379
17683
  className: 'EditorLayers',
17380
17684
  type: Div
17381
17685
  };
17382
- const getEditorLayersVirtualDom = (selectionInfos, textInfos, differences, lineNumbers = true, highlightedLine = -1, cursorInfos = [], diagnostics = [], visibleLineIndices = [], endOfLineDecorations = [], bracketMatchInfos = [], focused = true, visibleViewLineIndices = [], problemsHighlightedRow = -1, roundedSelection = false, unnecessaryDiagnostics = []) => {
17383
- 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)];
17384
17688
  };
17385
17689
 
17386
17690
  const getEditorScrollBarDiagnosticsVirtualDom = scrollBarDiagnostics => {
@@ -17453,13 +17757,16 @@ const getEditorContentVirtualDom = ({
17453
17757
  endOfLineDecorations = [],
17454
17758
  focused = true,
17455
17759
  highlightedLine = -1,
17760
+ horizontalVisibleRanges = [],
17456
17761
  lineNumbers = true,
17762
+ lines = [],
17457
17763
  problemsHighlightedRow = -1,
17458
17764
  roundedSelection = false,
17459
17765
  scrollBarDiagnostics = [],
17460
17766
  scrollBarHeight = 0,
17461
17767
  scrollBarWidth = 0,
17462
17768
  selectionInfos = [],
17769
+ tabSize = 2,
17463
17770
  textInfos,
17464
17771
  unnecessaryDiagnostics = [],
17465
17772
  visibleLineIndices = [],
@@ -17468,7 +17775,7 @@ const getEditorContentVirtualDom = ({
17468
17775
  return [{
17469
17776
  ...editorContentNode,
17470
17777
  className: breadcrumbsEnabled ? 'EditorContent EditorBreadcrumbsOffset' : 'EditorContent'
17471
- }, ...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)];
17472
17779
  };
17473
17780
 
17474
17781
  const getGutterInfoVirtualDom = (gutterInfo, activeLineNumber) => {
@@ -17622,6 +17929,7 @@ const getEditorVirtualDom = ({
17622
17929
  gutterInfos = [],
17623
17930
  highlightActiveLineNumber = true,
17624
17931
  highlightedLine = -1,
17932
+ horizontalVisibleRanges = [],
17625
17933
  lightBulbRowIndex = -1,
17626
17934
  lineNumbers = true,
17627
17935
  lines = [],
@@ -17638,6 +17946,7 @@ const getEditorVirtualDom = ({
17638
17946
  scrollBarWidth = 0,
17639
17947
  selectionInfos = [],
17640
17948
  selections = new Uint32Array(),
17949
+ tabSize = 2,
17641
17950
  textInfos,
17642
17951
  uid,
17643
17952
  unnecessaryDiagnostics = [],
@@ -17690,13 +17999,16 @@ const getEditorVirtualDom = ({
17690
17999
  endOfLineDecorations,
17691
18000
  focused,
17692
18001
  highlightedLine,
18002
+ horizontalVisibleRanges,
17693
18003
  lineNumbers,
18004
+ lines,
17694
18005
  problemsHighlightedRow,
17695
18006
  roundedSelection,
17696
18007
  scrollBarDiagnostics,
17697
18008
  scrollBarHeight,
17698
18009
  scrollBarWidth,
17699
18010
  selectionInfos,
18011
+ tabSize,
17700
18012
  textInfos: combineWhitespaceTokens$1 ? textInfos.map(combineWhitespaceTokens) : textInfos,
17701
18013
  unnecessaryDiagnostics,
17702
18014
  visibleLineIndices: visibleLineIndices || [],
@@ -17864,12 +18176,14 @@ const getDom = state => {
17864
18176
  longestLineWidth,
17865
18177
  minimumSliderSize,
17866
18178
  textInfos,
17867
- visualDecorations = [],
17868
- width
18179
+ visualDecorations = []
17869
18180
  } = state;
17870
18181
  if (initial && textInfos.length === 0) {
17871
18182
  return [];
17872
18183
  }
18184
+ const {
18185
+ width
18186
+ } = getHorizontalScrollDimensions(state);
17873
18187
  return getEditorVirtualDom({
17874
18188
  ...state,
17875
18189
  diagnostics: visualDecorations,
@@ -18051,10 +18365,10 @@ const renderLines = {
18051
18365
  visibleLineIndices,
18052
18366
  visibleViewLineIndices
18053
18367
  } = newState;
18054
- 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);
18055
18369
  return [/* method */'setText', dom];
18056
18370
  },
18057
- 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
18058
18372
  };
18059
18373
  const renderSelections = {
18060
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.6",
3
+ "version": "19.72.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git@github.com:lvce-editor/editor-worker.git"