@lvce-editor/editor-worker 2.3.0 → 2.4.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.
@@ -238,1097 +238,131 @@ const IndentMore = 'indentMore';
238
238
  const InsertLineBreak = 'insertLineBreak';
239
239
  const ToggleBlockComment = 'toggleBlockComment';
240
240
 
241
- const Dash = '-';
242
- const Dot = '.';
243
- const EmptyString = '';
244
- const Space = ' ';
245
- const Tab = '\t';
246
- const Underline = '_';
247
- const T = 't';
248
-
249
- const getFontString = (fontWeight, fontSize, fontFamily) => {
250
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
241
+ const modules = Object.create(null);
242
+ const register = (id, value) => {
243
+ modules[id] = value;
244
+ };
245
+ const get$7 = id => {
246
+ return modules[id];
251
247
  };
252
248
 
253
- const getLetterSpacingString = letterSpacing => {
254
- return `${letterSpacing}px`;
249
+ const getModule$2 = id => {
250
+ return get$7(id);
255
251
  };
256
252
 
257
- const createMeasureContext = () => {
258
- const canvas = new OffscreenCanvas(0, 0);
259
- const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
260
- if (!ctx) {
261
- throw new Error('Failed to get canvas context 2d');
253
+ const applyWidgetChange = (editor, widget, changes) => {
254
+ const module = getModule$2(widget.id);
255
+ if (changes.length === 1 && changes[0].origin === EditorType && module.handleEditorType) {
256
+ const newState = module.handleEditorType(editor, widget.newState);
257
+ return {
258
+ ...widget,
259
+ newState
260
+ };
262
261
  }
263
- return ctx;
262
+ if (changes.length === 1 && changes[0].origin === DeleteLeft && module.handleEditorDeleteLeft) {
263
+ const newState = module.handleEditorDeleteLeft(editor, widget.newState);
264
+ return {
265
+ ...widget,
266
+ newState
267
+ };
268
+ }
269
+ return widget;
264
270
  };
265
271
 
266
- const state$b = {
267
- ctx: undefined
268
- };
269
- const getOrCreate = createCtx => {
270
- if (state$b.ctx) {
271
- return state$b.ctx;
272
+ const applyWidgetChanges = (editor, changes) => {
273
+ const widgets = editor.widgets || [];
274
+ if (widgets.length === 0) {
275
+ return widgets;
272
276
  }
273
- state$b.ctx = createCtx();
274
- return state$b.ctx;
277
+ const newWidgets = [];
278
+ for (const widget of widgets) {
279
+ const newWidget = applyWidgetChange(editor, widget, changes);
280
+ if (newWidget.newState) {
281
+ newWidgets.push(newWidget);
282
+ }
283
+ }
284
+ return newWidgets;
275
285
  };
276
286
 
277
- const getContext = () => {
278
- const ctx = getOrCreate(createMeasureContext);
279
- return ctx;
287
+ const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
288
+ const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
289
+ return scrollBarOffset;
280
290
  };
291
+ const getScrollBarY = getScrollBarOffset;
281
292
 
282
- const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
283
- string(text);
284
- number$1(fontWeight);
285
- number$1(fontSize);
286
- string(fontFamily);
287
- boolean(isMonoSpaceFont);
288
- number$1(charWidth);
289
- if (isMonoSpaceFont) {
290
- return text.length * charWidth;
291
- }
292
- if (typeof letterSpacing !== 'number') {
293
- throw new TypeError('letterSpacing must be of type number');
293
+ const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
294
+ if (size >= contentSize) {
295
+ return 0;
294
296
  }
295
- const letterSpacingString = getLetterSpacingString(letterSpacing);
296
- const fontString = getFontString(fontWeight, fontSize, fontFamily);
297
- const ctx = getContext();
298
- // @ts-ignore
299
- ctx.letterSpacing = letterSpacingString;
300
- // @ts-ignore
301
- ctx.font = fontString;
302
- // @ts-ignore
303
- const metrics = ctx.measureText(text);
304
- const width = metrics.width;
305
- return width;
297
+ return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
306
298
  };
307
299
 
308
- const getAccurateColumnIndexAscii = (
309
- // @ts-ignore
310
- line,
311
- // @ts-ignore
312
- guess,
313
- // @ts-ignore
314
- averageCharWidth,
315
- // @ts-ignore
316
- eventX,
317
- // @ts-ignore
318
- fontWeight,
319
- // @ts-ignore
320
- fontSize,
321
- // @ts-ignore
322
- fontFamily,
323
- // @ts-ignore
324
- letterSpacing,
325
- // @ts-ignore
326
- isMonospaceFont,
327
- // @ts-ignore
328
- charWidth) => {
329
- for (let i = guess; i < line.length; i++) {
330
- const width = measureTextWidth(line.slice(0, i), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
331
- if (eventX - width < averageCharWidth / 2) {
332
- return i;
333
- }
300
+ const getScrollBarWidth = (width, longestLineWidth) => {
301
+ if (width > longestLineWidth) {
302
+ return 0;
334
303
  }
335
- return line.length;
304
+ return width ** 2 / longestLineWidth;
336
305
  };
337
306
 
338
- const supported = () => {
339
- return 'Segmenter' in Intl;
340
- };
341
- const create$5 = () => {
342
- // @ts-ignore
343
- const segmenter = new Intl.Segmenter();
307
+ const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
308
+ const halfScrollBarHeight = scrollBarHeight / 2;
309
+ if (relativeY <= halfScrollBarHeight) {
310
+ // clicked at top
311
+ return {
312
+ percent: 0,
313
+ handleOffset: relativeY
314
+ };
315
+ }
316
+ if (relativeY <= height - halfScrollBarHeight) {
317
+ // clicked in middle
318
+ return {
319
+ percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight),
320
+ handleOffset: halfScrollBarHeight
321
+ };
322
+ }
323
+ // clicked at bottom
344
324
  return {
345
- at(line, index) {
346
- const segments = segmenter.segment(line);
347
- return segments.containing(index);
348
- },
349
- visualIndex(line, index) {
350
- const segments = segmenter.segment(line);
351
- let currentVisualIndex = 0;
352
- for (const segment of segments) {
353
- if (segment.index >= index) {
354
- return currentVisualIndex;
355
- }
356
- currentVisualIndex++;
357
- }
358
- return currentVisualIndex;
359
- },
360
- modelIndex(line, visualIndex) {
361
- const segments = segmenter.segment(line);
362
- let currentVisualIndex = 0;
363
- for (const segment of segments) {
364
- if (currentVisualIndex >= visualIndex) {
365
- return segment.index;
366
- }
367
- currentVisualIndex++;
368
- }
369
- return line.length;
370
- },
371
- getSegments(line) {
372
- return segmenter.segment(line);
373
- }
325
+ percent: 1,
326
+ handleOffset: scrollBarHeight - height + relativeY
374
327
  };
375
328
  };
376
329
 
377
- // @ts-ignore
378
- const getAccurateColumnIndexUnicode = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing) => {
379
- const segmenter = create$5();
380
- const segments = segmenter.getSegments(line);
381
- const isMonospaceFont = false;
382
- const charWidth = 0;
383
- for (const segment of segments) {
384
- const width = measureTextWidth(line.slice(0, segment.index), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
385
- if (eventX - width < averageCharWidth) {
386
- return segment.index;
387
- }
330
+ const splitLines$2 = lines => {
331
+ if (!lines) {
332
+ return [];
388
333
  }
389
- return line.length;
334
+ return lines.split('\n');
390
335
  };
391
336
 
392
- const RE_ASCII = /^[\p{ASCII}]*$/u;
393
- const isAscii = line => {
394
- return RE_ASCII.test(line);
395
- };
337
+ // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
396
338
 
397
- const normalizeText = (text, normalize, tabSize) => {
398
- if (normalize) {
399
- return text.replaceAll(Tab, Space.repeat(tabSize));
339
+ // @ts-ignore
340
+ const insertInto = (array, start, newItems) => {
341
+ const originalLength = array.length;
342
+ const newItemsLength = newItems.length;
343
+ array.length = originalLength + newItemsLength;
344
+ // Move the items after the start index, start from the end so that we don't overwrite any value.
345
+ for (let i = originalLength - 1; i >= start; i--) {
346
+ array[i + newItemsLength] = array[i];
347
+ }
348
+ for (let i = 0; i < newItemsLength; i++) {
349
+ array[i + start] = newItems[i];
400
350
  }
401
- return text;
402
- };
403
- const shouldNormalizeText = text => {
404
- return text.includes(Tab);
405
351
  };
406
352
 
353
+ /**
354
+ * Alternative to the native Array.splice method, it
355
+ * can only support limited number of items due to the maximum call stack size limit.
356
+ */
407
357
  // @ts-ignore
408
- const guessOffset = (eventX, averageCharWidth) => {
409
- const guess = Math.round(eventX / averageCharWidth);
410
- return guess;
358
+ const spliceLargeArray = (array, start, deleteCount, newItems) => {
359
+ const result = array.splice(start, deleteCount);
360
+ insertInto(array, start, newItems);
361
+ return result;
411
362
  };
412
363
 
413
- // @ts-ignore
414
- const normalizeGuess = (line, guess, tabSize) => {
415
- let normalizedGuess = guess;
416
- for (let i = 0; i < guess; i++) {
417
- if (line[i] === Tab) {
418
- normalizedGuess -= tabSize - 1;
419
- }
420
- }
421
- return normalizedGuess;
422
- };
423
-
424
- // @ts-ignore
425
- const getAccurateColumnIndex = (line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX) => {
426
- string(line);
427
- number$1(fontWeight);
428
- number$1(fontSize);
429
- string(fontFamily);
430
- number$1(letterSpacing);
431
- boolean(isMonospaceFont);
432
- number$1(charWidth);
433
- number$1(tabSize);
434
- number$1(eventX);
435
- // Assert.greaterZero(charWidth)
436
- const guess = guessOffset(eventX, charWidth);
437
- const normalize = shouldNormalizeText(line);
438
- const normalizedGuess = normalizeGuess(line, guess, tabSize);
439
- const text = line.slice(0, normalizedGuess);
440
- const normalizedText = normalizeText(text, normalize, tabSize);
441
- const actual = measureTextWidth(normalizedText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
442
- const isAscii$1 = isAscii(line);
443
- if (isAscii$1) {
444
- if (Math.abs(eventX - actual) < charWidth / 2) {
445
- return normalizedGuess;
446
- }
447
- return getAccurateColumnIndexAscii(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
448
- }
449
- return getAccurateColumnIndexUnicode(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing);
450
- };
451
-
452
- const at = (editor, eventX, eventY) => {
453
- object(editor);
454
- number$1(eventX);
455
- number$1(eventY);
456
- const {
457
- y,
458
- deltaY,
459
- rowHeight,
460
- fontSize,
461
- fontWeight,
462
- fontFamily,
463
- letterSpacing,
464
- lines,
465
- tabSize,
466
- differences,
467
- isMonospaceFont,
468
- charWidth
469
- } = editor;
470
- const rowIndex = Math.floor((eventY - y + deltaY) / rowHeight);
471
- if (rowIndex < 0) {
472
- return {
473
- rowIndex: 0,
474
- columnIndex: 0
475
- };
476
- }
477
- const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
478
- const line = lines[clampedRowIndex];
479
- const columnIndex = getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX);
480
- return {
481
- rowIndex: clampedRowIndex,
482
- columnIndex
483
- };
484
- };
485
-
486
- /**
487
- * @deprecated this doesn't work for variable width characters (unicode/emoji).
488
- * Use position computation in renderer process instead
489
- *
490
- * @param {object} editor
491
- * @param {number} rowIndex
492
- * @param {number} columnIndex
493
- * @returns
494
- */
495
- const x = (editor, rowIndex, columnIndex) => {
496
- const {
497
- columnWidth,
498
- x
499
- } = editor;
500
- const offsetX = columnIndex * columnWidth + x;
501
- return offsetX;
502
- };
503
- const y = (editor, rowIndex) => {
504
- const {
505
- rowHeight,
506
- y
507
- } = editor;
508
- const offsetY = (rowIndex + 1) * rowHeight + y;
509
- return offsetY;
510
- };
511
-
512
- const emptyObject = {};
513
- const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
514
- const i18nString = (key, placeholders = emptyObject) => {
515
- if (placeholders === emptyObject) {
516
- return key;
517
- }
518
- const replacer = (match, rest) => {
519
- return placeholders[rest];
520
- };
521
- return key.replaceAll(RE_PLACEHOLDER, replacer);
522
- };
523
-
524
- /**
525
- * @enum {string}
526
- */
527
- const UiStrings = {
528
- OrganizeImports: 'Organize Imports',
529
- Copy: 'Copy',
530
- CopyLineDown: 'Copy Line Down',
531
- CopyLineUp: 'Copy Line Up',
532
- Cut: 'Cut',
533
- DuplicateSelection: 'Duplicate Selection',
534
- FindAllImplementations: 'Find All Implementations',
535
- FindAllReferences: 'Find All References',
536
- GoToDefinition: 'Go to Definition',
537
- GoToTypeDefinition: 'Go to Type Definition',
538
- MoveLineDown: 'Move Line Down',
539
- MoveLineUp: 'Move Line Up',
540
- NoDefinitionFound: 'No definition found',
541
- NoDefinitionFoundFor: "No definition found for '{PH1}'",
542
- NoTypeDefinitionFound: 'No type definition found',
543
- NoTypeDefinitionFoundFor: "No type definition found for '{PH1}'",
544
- Paste: 'Paste',
545
- Redo: 'Redo',
546
- SelectAll: 'Select All',
547
- Separator: 'Separator',
548
- ToggleBlockComment: 'Toggle Block Comment',
549
- ToggleLineComment: 'Toggle Line Comment',
550
- Undo: 'Undo',
551
- FormatDocument: 'Format Document',
552
- SourceActions: 'Source Actions',
553
- EditorShowHover: 'Show Hover',
554
- EditorFormatDocumentForced: 'Editor: Format Document (forced)',
555
- EditorSelectNextOccurrence: 'Editor: Select Next Occurrence',
556
- EditorSelectAllOccurrences: 'Editor: Select All Occurrences',
557
- EditorGoToDefinition: 'Editor: Go To Definition',
558
- EditorGoToTypeDefinition: 'Editor: Go To Type Definition',
559
- EditorSelectInsideString: 'Editor: Select Inside String',
560
- EditorIndent: 'Editor: Indent',
561
- EditorUnindent: 'Editor: Unindent',
562
- EditorSortLinesAscending: 'Editor: Sort Lines Ascending',
563
- EditorToggleComment: 'Editor: Toggle Comment',
564
- EditorSelectUp: 'Editor: Select Up',
565
- EditorSelectDown: 'Editor: Select Down',
566
- EditorToggleBlockComment: 'Editor: Toggle Block Comment',
567
- EditorOpenColorPicker: 'Editor: Open Color Picker',
568
- EditorCloseColorPicker: 'Editor: Close Color Picker',
569
- EditorCopyLineDown: 'Editor: Copy Line Down',
570
- EditorCopyLineUp: 'Editor: Copy Line Up',
571
- Replace: 'replace',
572
- NoResults: 'No Results'
573
- };
574
- const noDefinitionFound = () => {
575
- return i18nString(UiStrings.NoDefinitionFound);
576
- };
577
-
578
- // @ts-ignore
579
- const noDefinitionFoundFor = word => {
580
- return i18nString(UiStrings.NoDefinitionFoundFor, {
581
- PH1: word
582
- });
583
- };
584
-
585
- // @ts-ignore
586
- const noTypeDefinitionFoundFor = word => {
587
- return i18nString(UiStrings.NoTypeDefinitionFoundFor, {
588
- PH1: word
589
- });
590
- };
591
- const noTypeDefinitionFound = () => {
592
- return i18nString(UiStrings.NoTypeDefinitionFound);
593
- };
594
- const noResults = () => {
595
- return i18nString(UiStrings.NoResults);
596
- };
597
-
598
- const None$1 = 'none';
599
- const Option = 'option';
600
-
601
- const getFileIconVirtualDom = icon => {
602
- return {
603
- type: Img,
604
- className: FileIcon,
605
- src: icon,
606
- role: None$1,
607
- childCount: 0
608
- };
609
- };
610
-
611
- const getIconDom = (fileIcon, symbolName) => {
612
- if (fileIcon) {
613
- return getFileIconVirtualDom(fileIcon);
614
- }
615
- return {
616
- type: Div,
617
- className: `${ColoredMaskIcon} ${symbolName}`,
618
- childCount: 0
619
- };
620
- };
621
-
622
- const text = data => {
623
- return {
624
- type: Text,
625
- text: data,
626
- childCount: 0
627
- };
628
- };
629
-
630
- const label1 = {
631
- type: Div,
632
- className: Label,
633
- childCount: 1
634
- };
635
- const completionHighlight = {
636
- type: Span,
637
- className: EditorCompletionItemHighlight,
638
- childCount: 1
639
- };
640
- const getHighlightedLabelDom = (label, highlights) => {
641
- if (highlights.length === 0) {
642
- return [label1, text(label)];
643
- }
644
- const dom = [];
645
- const labelDom = {
646
- type: Div,
647
- className: Label,
648
- childCount: 0
649
- };
650
- dom.push(labelDom);
651
- let position = 0;
652
- for (let i = 0; i < highlights.length; i += 2) {
653
- const highlightStart = highlights[i];
654
- const highlightEnd = highlights[i + 1];
655
- if (position < highlightStart) {
656
- const beforeText = label.slice(position, highlightStart);
657
- labelDom.childCount++;
658
- dom.push(text(beforeText));
659
- }
660
- const highlightText = label.slice(highlightStart, highlightEnd);
661
- labelDom.childCount++;
662
- dom.push(completionHighlight, text(highlightText));
663
- position = highlightEnd;
664
- }
665
- if (position < label.length) {
666
- const afterText = label.slice(position);
667
- labelDom.childCount++;
668
- dom.push(text(afterText));
669
- }
670
- return dom;
671
- };
672
-
673
- const getCompletionItemVirtualDom = visibleItem => {
674
- const {
675
- top,
676
- label,
677
- symbolName,
678
- highlights,
679
- focused,
680
- deprecated,
681
- fileIcon
682
- } = visibleItem;
683
- let className = EditorCompletionItem;
684
- if (focused) {
685
- className += ' ' + EditorCompletionItemFocused;
686
- }
687
- if (deprecated) {
688
- className += ' ' + EditorCompletionItemDeprecated;
689
- }
690
- return [{
691
- type: Div,
692
- role: Option,
693
- className,
694
- top,
695
- childCount: 2
696
- }, getIconDom(fileIcon, symbolName), ...getHighlightedLabelDom(label, highlights)];
697
- };
698
-
699
- const getCompletionItemsVirtualDom = visibleItems => {
700
- if (visibleItems.length === 0) {
701
- return [{
702
- type: Div,
703
- childCount: 1
704
- }, text(noResults())];
705
- }
706
- const root = {
707
- type: Div,
708
- childCount: visibleItems.length
709
- };
710
- const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
711
- return dom;
712
- };
713
-
714
- const Deprecated = 1 << 0;
715
-
716
- const Property = 1;
717
- const Value = 2;
718
- const Function$1 = 3;
719
- const Variable = 4;
720
- const Keyword = 5;
721
- const Folder = 6;
722
- const File = 7;
723
- const Field = 8;
724
-
725
- const SymbolProperty = 'SymbolProperty';
726
- const SymbolValue = 'SymbolValue';
727
- const SymbolFunction = 'SymbolFunction';
728
- const SymbolVariable = 'SymbolVariable';
729
- const SymbolKeyword = 'SymbolKeyword';
730
- const SymbolDefault = 'SymbolDefault';
731
- const SymbolField = 'SymbolField';
732
- const SymbolNone = '';
733
-
734
- const getSymbolName = kind => {
735
- switch (kind) {
736
- case Property:
737
- return SymbolProperty;
738
- case Value:
739
- return SymbolValue;
740
- case Function$1:
741
- return SymbolFunction;
742
- case Variable:
743
- return SymbolVariable;
744
- case Keyword:
745
- return SymbolKeyword;
746
- case Field:
747
- return SymbolField;
748
- case File:
749
- return SymbolNone;
750
- default:
751
- return SymbolDefault;
752
- }
753
- };
754
-
755
- const getHighlights = item => {
756
- const {
757
- matches
758
- } = item;
759
- return matches.slice(1);
760
- };
761
-
762
- // import * as IconTheme from '../IconTheme/IconTheme.ts'
763
-
764
- const getLabel = item => {
765
- return item.label;
766
- };
767
- const getFileIcon = item => {
768
- switch (item.kind) {
769
- case File:
770
- // TODO IconTheme.getFileNameIcon(item.label)
771
- return '';
772
- case Folder:
773
- // TODO IconTheme.getFolderNameIcon(item.label)
774
- return '';
775
- default:
776
- return '';
777
- }
778
- };
779
- const getVisibleIem = (item, itemHeight, leadingWord, i, focusedIndex) => {
780
- return {
781
- label: getLabel(item),
782
- symbolName: getSymbolName(item.kind),
783
- top: i * itemHeight,
784
- highlights: getHighlights(item),
785
- focused: i === focusedIndex,
786
- deprecated: item.flags & Deprecated,
787
- fileIcon: getFileIcon(item)
788
- };
789
- };
790
-
791
- const getVisibleItems = (filteredItems, itemHeight, leadingWord, minLineY, maxLineY, focusedIndex) => {
792
- const visibleItems = [];
793
- for (let i = minLineY; i < maxLineY; i++) {
794
- const filteredItem = filteredItems[i];
795
- visibleItems.push(getVisibleIem(filteredItem, itemHeight, leadingWord, i, focusedIndex));
796
- }
797
- return visibleItems;
798
- };
799
-
800
- /**
801
- *
802
- * @param {number} size
803
- * @param {number} contentSize
804
- * @param {number} minimumSliderSize
805
- * @returns
806
- */
807
- const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
808
- if (size >= contentSize) {
809
- return 0;
810
- }
811
- return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
812
- };
813
- const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
814
- const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
815
- return scrollBarOffset;
816
- };
817
- const getScrollBarY = getScrollBarOffset;
818
- const getScrollBarWidth = (width, longestLineWidth) => {
819
- if (width > longestLineWidth) {
820
- return 0;
821
- }
822
- return width ** 2 / longestLineWidth;
823
- };
824
- const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
825
- const halfScrollBarHeight = scrollBarHeight / 2;
826
- if (relativeY <= halfScrollBarHeight) {
827
- // clicked at top
828
- return {
829
- percent: 0,
830
- handleOffset: relativeY
831
- };
832
- }
833
- if (relativeY <= height - halfScrollBarHeight) {
834
- // clicked in middle
835
- return {
836
- percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight),
837
- handleOffset: halfScrollBarHeight
838
- };
839
- }
840
- // clicked at bottom
841
- return {
842
- percent: 1,
843
- handleOffset: scrollBarHeight - height + relativeY
844
- };
845
- };
846
-
847
- const renderItems = {
848
- isEqual(oldState, newState) {
849
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
850
- },
851
- apply(oldState, newState) {
852
- const visibleItems = getVisibleItems(newState.items, newState.itemHeight, newState.leadingWord, newState.minLineY, newState.maxLineY, newState.focusedIndex);
853
- const dom = getCompletionItemsVirtualDom(visibleItems);
854
- return ['setDom', dom];
855
- }
856
- };
857
- const renderBounds$1 = {
858
- isEqual(oldState, newState) {
859
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.x === newState.x && oldState.y === newState.y;
860
- },
861
- apply(oldState, newState) {
862
- const {
863
- x,
864
- y,
865
- width,
866
- height
867
- } = newState;
868
- return [/* method */SetBounds, /* x */x, /* y */y, /* width */width, /* height */height];
869
- }
870
- };
871
- const renderHeight = {
872
- isEqual(oldState, newState) {
873
- return oldState.items.length === newState.items.length;
874
- },
875
- apply(oldState, newState) {
876
- const {
877
- itemHeight
878
- } = newState;
879
- const contentHeight = newState.items.length * itemHeight;
880
- return [/* method */SetContentHeight, /* contentHeight */contentHeight];
881
- }
882
- };
883
- const renderNegativeMargin = {
884
- isEqual(oldState, newState) {
885
- return oldState.deltaY === newState.deltaY;
886
- },
887
- apply(oldState, newState) {
888
- return [/* method */SetNegativeMargin, /* negativeMargin */-newState.deltaY];
889
- }
890
- };
891
- const renderScrollBar = {
892
- isEqual(oldState, newState) {
893
- return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length;
894
- },
895
- apply(oldState, newState) {
896
- const total = newState.items.length;
897
- const contentHeight = total * newState.itemHeight;
898
- const scrollBarHeight = getScrollBarSize(newState.height, contentHeight, newState.minimumSliderSize);
899
- const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
900
- return [/* method */SetScrollBar, /* scrollBarY */scrollBarY, /* scrollBarHeight */scrollBarHeight];
901
- }
902
- };
903
- const render$3 = [renderItems, renderBounds$1, renderHeight, renderNegativeMargin, renderScrollBar];
904
- const renderCompletion = (oldState, newState) => {
905
- const commands = [];
906
- for (const item of render$3) {
907
- if (!item.isEqual(oldState, newState)) {
908
- commands.push(item.apply(oldState, newState));
909
- }
910
- }
911
- return commands;
912
- };
913
-
914
- const EmptyMatches = [];
915
-
916
- const Diagonal = 1;
917
- const Left = 2;
918
-
919
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
920
-
921
- const createTable = size => {
922
- const table = [];
923
- for (let i = 0; i < size; i++) {
924
- const row = new Uint8Array(size);
925
- table.push(row);
926
- }
927
- return table;
928
- };
929
-
930
- const isLowerCase = char => {
931
- return char === char.toLowerCase();
932
- };
933
-
934
- const isUpperCase = char => {
935
- return char === char.toUpperCase();
936
- };
937
-
938
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
939
- const isGap = (columnCharBefore, columnChar) => {
940
- switch (columnCharBefore) {
941
- case Dash:
942
- case Underline:
943
- case EmptyString:
944
- case T:
945
- case Space:
946
- case Dot:
947
- return true;
948
- }
949
- if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
950
- return true;
951
- }
952
- return false;
953
- };
954
-
955
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
956
- const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch) => {
957
- if (rowCharLow !== columnCharLow) {
958
- return -1;
959
- }
960
- const isMatch = rowChar === columnChar;
961
- if (isMatch) {
962
- if (isDiagonalMatch) {
963
- return 8;
964
- }
965
- if (isGap(columnCharBefore, columnChar)) {
966
- return 8;
967
- }
968
- return 5;
969
- }
970
- if (isGap(columnCharBefore, columnChar)) {
971
- return 8;
972
- }
973
- return 5;
974
- };
975
-
976
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
977
-
978
- const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
979
- while (patternPos < patternLen && wordPos < wordLen) {
980
- if (patternLow[patternPos] === wordLow[wordPos]) {
981
- patternPos += 1;
982
- }
983
- wordPos += 1;
984
- }
985
- return patternPos === patternLen; // pattern must be exhausted
986
- };
987
-
988
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
989
- const traceHighlights = (table, arrows, patternLength, wordLength) => {
990
- let row = patternLength;
991
- let column = wordLength;
992
- const matches = [];
993
- while (row >= 1 && column >= 1) {
994
- const arrow = arrows[row][column];
995
- if (arrow === Left) {
996
- column--;
997
- } else if (arrow === Diagonal) {
998
- row--;
999
- column--;
1000
- const start = column + 1;
1001
- while (row >= 1 && column >= 1) {
1002
- const arrow = arrows[row][column];
1003
- if (arrow === Left) {
1004
- break;
1005
- }
1006
- if (arrow === Diagonal) {
1007
- row--;
1008
- column--;
1009
- }
1010
- }
1011
- const end = column;
1012
- matches.unshift(end, start);
1013
- }
1014
- }
1015
- matches.unshift(table[patternLength][wordLength - 1]);
1016
- return matches;
1017
- };
1018
-
1019
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
1020
- const gridSize = 128;
1021
- const table = createTable(gridSize);
1022
- const arrows = createTable(gridSize);
1023
- const filterCompletionItem = (pattern, word) => {
1024
- const patternLength = Math.min(pattern.length, gridSize - 1);
1025
- const wordLength = Math.min(word.length, gridSize - 1);
1026
- const patternLower = pattern.toLowerCase();
1027
- const wordLower = word.toLowerCase();
1028
- if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
1029
- return EmptyMatches;
1030
- }
1031
- let strongMatch = false;
1032
- for (let row = 1; row < patternLength + 1; row++) {
1033
- const rowChar = pattern[row - 1];
1034
- const rowCharLow = patternLower[row - 1];
1035
- for (let column = 1; column < wordLength + 1; column++) {
1036
- const columnChar = word[column - 1];
1037
- const columnCharLow = wordLower[column - 1];
1038
- const columnCharBefore = word[column - 2] || '';
1039
- const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
1040
- const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch);
1041
- if (row === 1 && score > 5) {
1042
- strongMatch = true;
1043
- }
1044
- let diagonalScore = score + table[row - 1][column - 1];
1045
- if (isDiagonalMatch && score !== -1) {
1046
- diagonalScore += 2;
1047
- }
1048
- const leftScore = table[row][column - 1];
1049
- if (leftScore > diagonalScore) {
1050
- table[row][column] = leftScore;
1051
- arrows[row][column] = Left;
1052
- } else {
1053
- table[row][column] = diagonalScore;
1054
- arrows[row][column] = Diagonal;
1055
- }
1056
- }
1057
- }
1058
- if (!strongMatch) {
1059
- return EmptyMatches;
1060
- }
1061
- const highlights = traceHighlights(table, arrows, patternLength, wordLength);
1062
- return highlights;
1063
- };
1064
-
1065
- const addEmptyMatch = item => {
1066
- return {
1067
- ...item,
1068
- matches: EmptyMatches
1069
- };
1070
- };
1071
- const filterCompletionItems = (completionItems, word) => {
1072
- if (word === EmptyString) {
1073
- return completionItems.map(addEmptyMatch);
1074
- }
1075
- const filteredCompletions = [];
1076
- const deprecated = [];
1077
- for (const completionItem of completionItems) {
1078
- const {
1079
- label,
1080
- flags
1081
- } = completionItem;
1082
- const result = filterCompletionItem(word, label);
1083
- if (result !== EmptyMatches) {
1084
- if (flags & Deprecated) {
1085
- // TODO avoid mutation
1086
- completionItem.matches = EmptyMatches;
1087
- deprecated.push(completionItem);
1088
- } else {
1089
- // TODO avoid mutation
1090
- completionItem.matches = result;
1091
- filteredCompletions.push(completionItem);
1092
- }
1093
- }
1094
- }
1095
- if (deprecated.length > 0) {
1096
- filteredCompletions.push(...deprecated);
1097
- }
1098
- return filteredCompletions;
1099
- };
1100
-
1101
- const getListHeight = (itemsLength, itemHeight, maxHeight) => {
1102
- number$1(itemsLength);
1103
- number$1(itemHeight);
1104
- number$1(maxHeight);
1105
- if (itemsLength === 0) {
1106
- return itemHeight;
1107
- }
1108
- const totalHeight = itemsLength * itemHeight;
1109
- return Math.min(totalHeight, maxHeight);
1110
- };
1111
-
1112
- const RE_WORD_START$1 = /^[\w\-]+/;
1113
- const RE_WORD_END$1 = /[\w\-]+$/;
1114
- const getWordAt$1 = (line, columnIndex) => {
1115
- const before = line.slice(0, columnIndex);
1116
- const matchBefore = before.match(RE_WORD_END$1);
1117
- const after = line.slice(columnIndex);
1118
- const matchAfter = after.match(RE_WORD_START$1);
1119
- let word = EmptyString;
1120
- if (matchBefore) {
1121
- word += matchBefore[0];
1122
- }
1123
- if (matchAfter) {
1124
- word += matchAfter[0];
1125
- }
1126
- return {
1127
- word
1128
- };
1129
- };
1130
- const getWordBefore$1 = (line, columnIndex) => {
1131
- const before = line.slice(0, columnIndex);
1132
- const matchBefore = before.match(RE_WORD_END$1);
1133
- if (matchBefore) {
1134
- return matchBefore[0];
1135
- }
1136
- return EmptyString;
1137
- };
1138
-
1139
- const getWordAt = (editor, rowIndex, columnIndex) => {
1140
- const {
1141
- lines
1142
- } = editor;
1143
- const line = lines[rowIndex];
1144
- return getWordAt$1(line, columnIndex);
1145
- };
1146
- const getWordBefore = (editor, rowIndex, columnIndex) => {
1147
- const {
1148
- lines
1149
- } = editor;
1150
- const line = lines[rowIndex];
1151
- return getWordBefore$1(line, columnIndex);
1152
- };
1153
-
1154
- const render$2 = (oldState, newState) => {
1155
- const commands = renderCompletion(oldState, newState);
1156
- const wrappedCommands = [];
1157
- const uid = newState.uid;
1158
- for (const command of commands) {
1159
- wrappedCommands.push(['Viewlet.send', uid, ...command]);
1160
- }
1161
- return wrappedCommands;
1162
- };
1163
- const add = widget => {
1164
- const commands = render$2(widget.oldState, widget.newState);
1165
- const id = 'EditorCompletion';
1166
- // TODO how to generate a unique integer id
1167
- // that doesn't collide with ids created in renderer worker?
1168
- const uid = widget.newState.uid;
1169
- const allCommands = [];
1170
- allCommands.push(['Viewlet.create', id, uid]);
1171
- allCommands.push(...commands);
1172
- return allCommands;
1173
- };
1174
- const remove$1 = widget => {
1175
- return [['Viewlet.send', widget.newState.uid, 'dispose']];
1176
- };
1177
- const handleEditorType$1 = (editor, state) => {
1178
- const {
1179
- unfilteredItems,
1180
- itemHeight,
1181
- maxHeight
1182
- } = state;
1183
- const {
1184
- selections
1185
- } = editor;
1186
- const rowIndex = selections[0];
1187
- const columnIndex = selections[1];
1188
- const x$1 = x(editor, rowIndex, columnIndex);
1189
- const y$1 = y(editor, rowIndex);
1190
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1191
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1192
- const newMinLineY = 0;
1193
- const newMaxLineY = Math.min(items.length, 8);
1194
- const height = getListHeight(items.length, itemHeight, maxHeight);
1195
- const finalDeltaY = items.length * itemHeight - height;
1196
- return {
1197
- ...state,
1198
- items,
1199
- x: x$1,
1200
- y: y$1,
1201
- minLineY: newMinLineY,
1202
- maxLineY: newMaxLineY,
1203
- leadingWord: wordAtOffset,
1204
- height,
1205
- finalDeltaY
1206
- };
1207
- };
1208
- const handleEditorDeleteLeft$1 = (editor, state) => {
1209
- const {
1210
- unfilteredItems,
1211
- itemHeight,
1212
- maxHeight
1213
- } = state;
1214
- const {
1215
- selections
1216
- } = editor;
1217
- const rowIndex = selections[0];
1218
- const columnIndex = selections[1];
1219
- const x$1 = x(editor, rowIndex, columnIndex);
1220
- const y$1 = y(editor, rowIndex);
1221
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1222
- if (!wordAtOffset) {
1223
- return undefined;
1224
- }
1225
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1226
- const newMaxLineY = Math.min(items.length, 8);
1227
- const height = getListHeight(items.length, itemHeight, maxHeight);
1228
- return {
1229
- ...state,
1230
- items,
1231
- x: x$1,
1232
- y: y$1,
1233
- maxLineY: newMaxLineY,
1234
- leadingWord: wordAtOffset,
1235
- height
1236
- };
1237
- };
1238
-
1239
- const EditorCompletionWidget = {
1240
- __proto__: null,
1241
- add,
1242
- handleEditorDeleteLeft: handleEditorDeleteLeft$1,
1243
- handleEditorType: handleEditorType$1,
1244
- remove: remove$1,
1245
- render: render$2
1246
- };
1247
-
1248
- const Completion = 'completion';
1249
-
1250
- const modules = Object.create(null);
1251
- const register = (id, value) => {
1252
- modules[id] = value;
1253
- };
1254
- const get$7 = id => {
1255
- return modules[id];
1256
- };
1257
-
1258
- const getModule$2 = id => {
1259
- return get$7(id);
1260
- };
1261
-
1262
- const applyWidgetChange = (editor, widget, changes) => {
1263
- const module = getModule$2(widget.id);
1264
- if (changes.length === 1 && changes[0].origin === EditorType && module.handleEditorType) {
1265
- const newState = module.handleEditorType(editor, widget.newState);
1266
- return {
1267
- ...widget,
1268
- newState
1269
- };
1270
- }
1271
- if (changes.length === 1 && changes[0].origin === DeleteLeft && module.handleEditorDeleteLeft) {
1272
- const newState = module.handleEditorDeleteLeft(editor, widget.newState);
1273
- return {
1274
- ...widget,
1275
- newState
1276
- };
1277
- }
1278
- return widget;
1279
- };
1280
-
1281
- const applyWidgetChanges = (editor, changes) => {
1282
- const widgets = editor.widgets || [];
1283
- if (widgets.length === 0) {
1284
- return widgets;
1285
- }
1286
- const newWidgets = [];
1287
- for (const widget of widgets) {
1288
- const newWidget = applyWidgetChange(editor, widget, changes);
1289
- if (newWidget.newState) {
1290
- newWidgets.push(newWidget);
1291
- }
1292
- }
1293
- return newWidgets;
1294
- };
1295
-
1296
- const splitLines$2 = lines => {
1297
- if (!lines) {
1298
- return [];
1299
- }
1300
- return lines.split('\n');
1301
- };
1302
-
1303
- // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
1304
-
1305
- // @ts-ignore
1306
- const insertInto = (array, start, newItems) => {
1307
- const originalLength = array.length;
1308
- const newItemsLength = newItems.length;
1309
- array.length = originalLength + newItemsLength;
1310
- // Move the items after the start index, start from the end so that we don't overwrite any value.
1311
- for (let i = originalLength - 1; i >= start; i--) {
1312
- array[i + newItemsLength] = array[i];
1313
- }
1314
- for (let i = 0; i < newItemsLength; i++) {
1315
- array[i + start] = newItems[i];
1316
- }
1317
- };
1318
-
1319
- /**
1320
- * Alternative to the native Array.splice method, it
1321
- * can only support limited number of items due to the maximum call stack size limit.
1322
- */
1323
- // @ts-ignore
1324
- const spliceLargeArray = (array, start, deleteCount, newItems) => {
1325
- const result = array.splice(start, deleteCount);
1326
- insertInto(array, start, newItems);
1327
- return result;
1328
- };
1329
-
1330
- const joinLines$2 = lines => {
1331
- return lines.join('\n');
364
+ const joinLines$2 = lines => {
365
+ return lines.join('\n');
1332
366
  };
1333
367
 
1334
368
  // TODO have function for single edit (most common, avoid one array)
@@ -1521,6 +555,14 @@ const getSelectionPairs = (selections, i) => {
1521
555
  return [first, second, third, fourth, 0];
1522
556
  };
1523
557
 
558
+ const Dash = '-';
559
+ const Dot = '.';
560
+ const EmptyString = '';
561
+ const Space = ' ';
562
+ const Tab = '\t';
563
+ const Underline = '_';
564
+ const T = 't';
565
+
1524
566
  const getTabCount = string => {
1525
567
  let count = 0;
1526
568
  for (const element of string) {
@@ -1531,6 +573,75 @@ const getTabCount = string => {
1531
573
  return count;
1532
574
  };
1533
575
 
576
+ const getFontString = (fontWeight, fontSize, fontFamily) => {
577
+ return `${fontWeight} ${fontSize}px ${fontFamily}`;
578
+ };
579
+
580
+ const getLetterSpacingString = letterSpacing => {
581
+ return `${letterSpacing}px`;
582
+ };
583
+
584
+ const createMeasureContext = () => {
585
+ const canvas = new OffscreenCanvas(0, 0);
586
+ const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
587
+ if (!ctx) {
588
+ throw new Error('Failed to get canvas context 2d');
589
+ }
590
+ return ctx;
591
+ };
592
+
593
+ const state$b = {
594
+ ctx: undefined
595
+ };
596
+ const getOrCreate = createCtx => {
597
+ if (state$b.ctx) {
598
+ return state$b.ctx;
599
+ }
600
+ state$b.ctx = createCtx();
601
+ return state$b.ctx;
602
+ };
603
+
604
+ const getContext = () => {
605
+ const ctx = getOrCreate(createMeasureContext);
606
+ return ctx;
607
+ };
608
+
609
+ const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
610
+ string(text);
611
+ number$1(fontWeight);
612
+ number$1(fontSize);
613
+ string(fontFamily);
614
+ boolean(isMonoSpaceFont);
615
+ number$1(charWidth);
616
+ if (isMonoSpaceFont) {
617
+ return text.length * charWidth;
618
+ }
619
+ if (typeof letterSpacing !== 'number') {
620
+ throw new TypeError('letterSpacing must be of type number');
621
+ }
622
+ const letterSpacingString = getLetterSpacingString(letterSpacing);
623
+ const fontString = getFontString(fontWeight, fontSize, fontFamily);
624
+ const ctx = getContext();
625
+ // @ts-ignore
626
+ ctx.letterSpacing = letterSpacingString;
627
+ // @ts-ignore
628
+ ctx.font = fontString;
629
+ // @ts-ignore
630
+ const metrics = ctx.measureText(text);
631
+ const width = metrics.width;
632
+ return width;
633
+ };
634
+
635
+ const normalizeText = (text, normalize, tabSize) => {
636
+ if (normalize) {
637
+ return text.replaceAll(Tab, Space.repeat(tabSize));
638
+ }
639
+ return text;
640
+ };
641
+ const shouldNormalizeText = text => {
642
+ return text.includes(Tab);
643
+ };
644
+
1534
645
  // TODO visible selections could also be uint16array
1535
646
  // [top1, left1, width1, height1, top2, left2, width2, height2...]
1536
647
 
@@ -1558,6 +669,7 @@ const getX = (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, l
1558
669
  const partialText = normalizedLine.slice(0, column + tabCount);
1559
670
  return measureTextWidth(partialText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, averageCharWidth) - halfCursorWidth + difference;
1560
671
  };
672
+
1561
673
  const getY = (row, minLineY, rowHeight) => {
1562
674
  return (row - minLineY) * rowHeight;
1563
675
  };
@@ -2067,7 +1179,7 @@ const set$5 = (id, fn) => {
2067
1179
  const get$5 = id => {
2068
1180
  return state$1$1.callbacks[id];
2069
1181
  };
2070
- const remove = id => {
1182
+ const remove$1 = id => {
2071
1183
  delete state$1$1.callbacks[id];
2072
1184
  };
2073
1185
  const state$a = {
@@ -2113,7 +1225,7 @@ const resolve = (id, args) => {
2113
1225
  return;
2114
1226
  }
2115
1227
  fn(args);
2116
- remove(id);
1228
+ remove$1(id);
2117
1229
  };
2118
1230
  const create$2$1 = (method, params) => {
2119
1231
  const {
@@ -2298,7 +1410,7 @@ const getErrorResponse = (message, error, preparePrettyError, logError) => {
2298
1410
  const errorProperty = getErrorProperty(error, prettyError);
2299
1411
  return create$1$1(message, errorProperty);
2300
1412
  };
2301
- const create$4 = (message, result) => {
1413
+ const create$5 = (message, result) => {
2302
1414
  return {
2303
1415
  jsonrpc: Two,
2304
1416
  id: message.id,
@@ -2307,7 +1419,7 @@ const create$4 = (message, result) => {
2307
1419
  };
2308
1420
  const getSuccessResponse = (message, result) => {
2309
1421
  const resultProperty = result ?? null;
2310
- return create$4(message, resultProperty);
1422
+ return create$5(message, resultProperty);
2311
1423
  };
2312
1424
  const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
2313
1425
  try {
@@ -2504,7 +1616,7 @@ const waitForFirstMessage$1 = async port => {
2504
1616
  return event;
2505
1617
  };
2506
1618
 
2507
- const create$3 = async () => {
1619
+ const create$4 = async () => {
2508
1620
  const {
2509
1621
  port1,
2510
1622
  port2
@@ -2549,7 +1661,7 @@ const wrap$3 = port => {
2549
1661
 
2550
1662
  const IpcParentWithExtensionHostWorker = {
2551
1663
  __proto__: null,
2552
- create: create$3,
1664
+ create: create$4,
2553
1665
  wrap: wrap$3
2554
1666
  };
2555
1667
 
@@ -2557,7 +1669,7 @@ const sendMessagePortToSyntaxHighlightingWorker = async port => {
2557
1669
  await invokeAndTransfer([port], 'SendMessagePortToSyntaxHighlightingWorker.sendMessagePortToSyntaxHighlightingWorker', port, 'HandleMessagePort.handleMessagePort');
2558
1670
  };
2559
1671
 
2560
- const create$2 = async () => {
1672
+ const create$3 = async () => {
2561
1673
  const {
2562
1674
  port1,
2563
1675
  port2
@@ -2602,7 +1714,7 @@ const wrap$1 = port => {
2602
1714
 
2603
1715
  const IpcParentWithSyntaxHighlightingWorker = {
2604
1716
  __proto__: null,
2605
- create: create$2,
1717
+ create: create$3,
2606
1718
  wrap: wrap$1
2607
1719
  };
2608
1720
 
@@ -2610,7 +1722,7 @@ const sendMessagePortToRendererProcess = async port => {
2610
1722
  await invokeAndTransfer([port], 'SendMessagePortToRendererProcess.sendMessagePortToRendererProcess', port, 'HandleMessagePort.handleMessagePort');
2611
1723
  };
2612
1724
 
2613
- const create$1 = async () => {
1725
+ const create$2 = async () => {
2614
1726
  const {
2615
1727
  port1,
2616
1728
  port2
@@ -2655,7 +1767,7 @@ const wrap = port => {
2655
1767
 
2656
1768
  const IpcParentWithRendererProcess = {
2657
1769
  __proto__: null,
2658
- create: create$1,
1770
+ create: create$2,
2659
1771
  wrap
2660
1772
  };
2661
1773
 
@@ -2672,7 +1784,7 @@ const getModule$1 = method => {
2672
1784
  }
2673
1785
  };
2674
1786
 
2675
- const create = async ({
1787
+ const create$1 = async ({
2676
1788
  method,
2677
1789
  ...options
2678
1790
  }) => {
@@ -2690,7 +1802,7 @@ const create = async ({
2690
1802
  const createRpc = method => {
2691
1803
  let _ipc;
2692
1804
  const listen = async () => {
2693
- const ipc = await create({
1805
+ const ipc = await create$1({
2694
1806
  method
2695
1807
  });
2696
1808
  handleIpc(ipc);
@@ -2954,6 +2066,180 @@ const editorReplaceSelections = (editor, replacement, origin) => {
2954
2066
  return replaceRange(editor, selections, replacement, origin);
2955
2067
  };
2956
2068
 
2069
+ const getAccurateColumnIndexAscii = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth) => {
2070
+ for (let i = guess; i < line.length; i++) {
2071
+ const width = measureTextWidth(line.slice(0, i), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2072
+ if (eventX - width < averageCharWidth / 2) {
2073
+ return i;
2074
+ }
2075
+ }
2076
+ return line.length;
2077
+ };
2078
+
2079
+ const supported = () => {
2080
+ return 'Segmenter' in Intl;
2081
+ };
2082
+ const create = () => {
2083
+ // @ts-ignore
2084
+ const segmenter = new Intl.Segmenter();
2085
+ return {
2086
+ at(line, index) {
2087
+ const segments = segmenter.segment(line);
2088
+ return segments.containing(index);
2089
+ },
2090
+ visualIndex(line, index) {
2091
+ const segments = segmenter.segment(line);
2092
+ let currentVisualIndex = 0;
2093
+ for (const segment of segments) {
2094
+ if (segment.index >= index) {
2095
+ return currentVisualIndex;
2096
+ }
2097
+ currentVisualIndex++;
2098
+ }
2099
+ return currentVisualIndex;
2100
+ },
2101
+ modelIndex(line, visualIndex) {
2102
+ const segments = segmenter.segment(line);
2103
+ let currentVisualIndex = 0;
2104
+ for (const segment of segments) {
2105
+ if (currentVisualIndex >= visualIndex) {
2106
+ return segment.index;
2107
+ }
2108
+ currentVisualIndex++;
2109
+ }
2110
+ return line.length;
2111
+ },
2112
+ getSegments(line) {
2113
+ return segmenter.segment(line);
2114
+ }
2115
+ };
2116
+ };
2117
+
2118
+ // @ts-ignore
2119
+ const getAccurateColumnIndexUnicode = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing) => {
2120
+ const segmenter = create();
2121
+ const segments = segmenter.getSegments(line);
2122
+ const isMonospaceFont = false;
2123
+ const charWidth = 0;
2124
+ for (const segment of segments) {
2125
+ const width = measureTextWidth(line.slice(0, segment.index), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2126
+ if (eventX - width < averageCharWidth) {
2127
+ return segment.index;
2128
+ }
2129
+ }
2130
+ return line.length;
2131
+ };
2132
+
2133
+ const RE_ASCII = /^[\p{ASCII}]*$/u;
2134
+ const isAscii = line => {
2135
+ return RE_ASCII.test(line);
2136
+ };
2137
+
2138
+ // @ts-ignore
2139
+ const guessOffset = (eventX, averageCharWidth) => {
2140
+ const guess = Math.round(eventX / averageCharWidth);
2141
+ return guess;
2142
+ };
2143
+
2144
+ // @ts-ignore
2145
+ const normalizeGuess = (line, guess, tabSize) => {
2146
+ let normalizedGuess = guess;
2147
+ for (let i = 0; i < guess; i++) {
2148
+ if (line[i] === Tab) {
2149
+ normalizedGuess -= tabSize - 1;
2150
+ }
2151
+ }
2152
+ return normalizedGuess;
2153
+ };
2154
+
2155
+ // @ts-ignore
2156
+ const getAccurateColumnIndex = (line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX) => {
2157
+ string(line);
2158
+ number$1(fontWeight);
2159
+ number$1(fontSize);
2160
+ string(fontFamily);
2161
+ number$1(letterSpacing);
2162
+ boolean(isMonospaceFont);
2163
+ number$1(charWidth);
2164
+ number$1(tabSize);
2165
+ number$1(eventX);
2166
+ // Assert.greaterZero(charWidth)
2167
+ const guess = guessOffset(eventX, charWidth);
2168
+ const normalize = shouldNormalizeText(line);
2169
+ const normalizedGuess = normalizeGuess(line, guess, tabSize);
2170
+ const text = line.slice(0, normalizedGuess);
2171
+ const normalizedText = normalizeText(text, normalize, tabSize);
2172
+ const actual = measureTextWidth(normalizedText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2173
+ const isAscii$1 = isAscii(line);
2174
+ if (isAscii$1) {
2175
+ if (Math.abs(eventX - actual) < charWidth / 2) {
2176
+ return normalizedGuess;
2177
+ }
2178
+ return getAccurateColumnIndexAscii(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2179
+ }
2180
+ return getAccurateColumnIndexUnicode(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing);
2181
+ };
2182
+
2183
+ const at = (editor, eventX, eventY) => {
2184
+ object(editor);
2185
+ number$1(eventX);
2186
+ number$1(eventY);
2187
+ const {
2188
+ y,
2189
+ deltaY,
2190
+ rowHeight,
2191
+ fontSize,
2192
+ fontWeight,
2193
+ fontFamily,
2194
+ letterSpacing,
2195
+ lines,
2196
+ tabSize,
2197
+ differences,
2198
+ isMonospaceFont,
2199
+ charWidth
2200
+ } = editor;
2201
+ const rowIndex = Math.floor((eventY - y + deltaY) / rowHeight);
2202
+ if (rowIndex < 0) {
2203
+ return {
2204
+ rowIndex: 0,
2205
+ columnIndex: 0
2206
+ };
2207
+ }
2208
+ const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
2209
+ const line = lines[clampedRowIndex];
2210
+ const columnIndex = getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX);
2211
+ return {
2212
+ rowIndex: clampedRowIndex,
2213
+ columnIndex
2214
+ };
2215
+ };
2216
+
2217
+ /**
2218
+ * @deprecated this doesn't work for variable width characters (unicode/emoji).
2219
+ * Use position computation in renderer process instead
2220
+ *
2221
+ * @param {object} editor
2222
+ * @param {number} rowIndex
2223
+ * @param {number} columnIndex
2224
+ * @returns
2225
+ */
2226
+ const x = (editor, rowIndex, columnIndex) => {
2227
+ const {
2228
+ columnWidth,
2229
+ x
2230
+ } = editor;
2231
+ const offsetX = columnIndex * columnWidth + x;
2232
+ return offsetX;
2233
+ };
2234
+ const y = (editor, rowIndex) => {
2235
+ const {
2236
+ rowHeight,
2237
+ y
2238
+ } = editor;
2239
+ const offsetY = (rowIndex + 1) * rowHeight + y;
2240
+ return offsetY;
2241
+ };
2242
+
2957
2243
  const state$7 = {
2958
2244
  timeout: -1
2959
2245
  };
@@ -3060,6 +2346,8 @@ const cancelSelection = editor => {
3060
2346
  return scheduleSelections(editor, newSelections);
3061
2347
  };
3062
2348
 
2349
+ const Completion = 'completion';
2350
+
3063
2351
  const isCompletionWidget = widget => {
3064
2352
  return widget.id === Completion;
3065
2353
  };
@@ -3388,7 +2676,7 @@ const characterLeft = (line, columnIndex) => {
3388
2676
  if (!supported()) {
3389
2677
  return 1;
3390
2678
  }
3391
- const segmenter = create$5();
2679
+ const segmenter = create();
3392
2680
  const last = segmenter.at(line, columnIndex - 1);
3393
2681
  return columnIndex - last.index;
3394
2682
  };
@@ -3401,7 +2689,7 @@ const characterRight = (line, columnIndex) => {
3401
2689
  if (!supported()) {
3402
2690
  return 1;
3403
2691
  }
3404
- const segmenter = create$5();
2692
+ const segmenter = create();
3405
2693
  const next = segmenter.at(line, columnIndex);
3406
2694
  return next.segment.length;
3407
2695
  };
@@ -3946,12 +3234,94 @@ const format = async editor => {
3946
3234
  }
3947
3235
  };
3948
3236
 
3237
+ const RE_WORD_START$1 = /^[\w\-]+/;
3238
+ const RE_WORD_END$1 = /[\w\-]+$/;
3239
+ const getWordAt$1 = (line, columnIndex) => {
3240
+ const before = line.slice(0, columnIndex);
3241
+ const matchBefore = before.match(RE_WORD_END$1);
3242
+ const after = line.slice(columnIndex);
3243
+ const matchAfter = after.match(RE_WORD_START$1);
3244
+ let word = EmptyString;
3245
+ if (matchBefore) {
3246
+ word += matchBefore[0];
3247
+ }
3248
+ if (matchAfter) {
3249
+ word += matchAfter[0];
3250
+ }
3251
+ return {
3252
+ word
3253
+ };
3254
+ };
3255
+ const getWordBefore$1 = (line, columnIndex) => {
3256
+ const before = line.slice(0, columnIndex);
3257
+ const matchBefore = before.match(RE_WORD_END$1);
3258
+ if (matchBefore) {
3259
+ return matchBefore[0];
3260
+ }
3261
+ return EmptyString;
3262
+ };
3263
+
3264
+ const getWordAt = (editor, rowIndex, columnIndex) => {
3265
+ const {
3266
+ lines
3267
+ } = editor;
3268
+ const line = lines[rowIndex];
3269
+ return getWordAt$1(line, columnIndex);
3270
+ };
3271
+ const getWordBefore = (editor, rowIndex, columnIndex) => {
3272
+ const {
3273
+ lines
3274
+ } = editor;
3275
+ const line = lines[rowIndex];
3276
+ return getWordBefore$1(line, columnIndex);
3277
+ };
3278
+
3949
3279
  // @ts-ignore
3950
3280
  const getDefinition = async (editor, offset) => {
3951
3281
  const definition = await invoke$3('ExtensionHostDefinition.executeDefinitionProvider', editor, offset);
3952
3282
  return definition;
3953
3283
  };
3954
3284
 
3285
+ const emptyObject = {};
3286
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
3287
+ const i18nString = (key, placeholders = emptyObject) => {
3288
+ if (placeholders === emptyObject) {
3289
+ return key;
3290
+ }
3291
+ const replacer = (match, rest) => {
3292
+ return placeholders[rest];
3293
+ };
3294
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
3295
+ };
3296
+
3297
+ const UiStrings = {
3298
+ GoToDefinition: 'Go to Definition',
3299
+ NoDefinitionFound: 'No definition found',
3300
+ NoDefinitionFoundFor: "No definition found for '{PH1}'",
3301
+ NoTypeDefinitionFound: 'No type definition found',
3302
+ NoTypeDefinitionFoundFor: "No type definition found for '{PH1}'",
3303
+ NoResults: 'No Results'
3304
+ };
3305
+ const noDefinitionFound = () => {
3306
+ return i18nString(UiStrings.NoDefinitionFound);
3307
+ };
3308
+ const noDefinitionFoundFor = word => {
3309
+ return i18nString(UiStrings.NoDefinitionFoundFor, {
3310
+ PH1: word
3311
+ });
3312
+ };
3313
+ const noTypeDefinitionFoundFor = word => {
3314
+ return i18nString(UiStrings.NoTypeDefinitionFoundFor, {
3315
+ PH1: word
3316
+ });
3317
+ };
3318
+ const noTypeDefinitionFound = () => {
3319
+ return i18nString(UiStrings.NoTypeDefinitionFound);
3320
+ };
3321
+ const noResults = () => {
3322
+ return i18nString(UiStrings.NoResults);
3323
+ };
3324
+
3955
3325
  // @ts-ignore
3956
3326
  const goTo = async ({
3957
3327
  editor,
@@ -5187,8 +4557,197 @@ const resolveCompletion = async (editor, name, completionItem) => {
5187
4557
  }
5188
4558
  };
5189
4559
 
5190
- const None = 1;
5191
-
4560
+ const None$1 = 1;
4561
+
4562
+ const EmptyMatches = [];
4563
+
4564
+ const Diagonal = 1;
4565
+ const Left = 2;
4566
+
4567
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4568
+
4569
+ const createTable = size => {
4570
+ const table = [];
4571
+ for (let i = 0; i < size; i++) {
4572
+ const row = new Uint8Array(size);
4573
+ table.push(row);
4574
+ }
4575
+ return table;
4576
+ };
4577
+
4578
+ const isLowerCase = char => {
4579
+ return char === char.toLowerCase();
4580
+ };
4581
+
4582
+ const isUpperCase = char => {
4583
+ return char === char.toUpperCase();
4584
+ };
4585
+
4586
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4587
+ const isGap = (columnCharBefore, columnChar) => {
4588
+ switch (columnCharBefore) {
4589
+ case Dash:
4590
+ case Underline:
4591
+ case EmptyString:
4592
+ case T:
4593
+ case Space:
4594
+ case Dot:
4595
+ return true;
4596
+ }
4597
+ if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
4598
+ return true;
4599
+ }
4600
+ return false;
4601
+ };
4602
+
4603
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4604
+ const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch) => {
4605
+ if (rowCharLow !== columnCharLow) {
4606
+ return -1;
4607
+ }
4608
+ const isMatch = rowChar === columnChar;
4609
+ if (isMatch) {
4610
+ if (isDiagonalMatch) {
4611
+ return 8;
4612
+ }
4613
+ if (isGap(columnCharBefore, columnChar)) {
4614
+ return 8;
4615
+ }
4616
+ return 5;
4617
+ }
4618
+ if (isGap(columnCharBefore, columnChar)) {
4619
+ return 8;
4620
+ }
4621
+ return 5;
4622
+ };
4623
+
4624
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4625
+
4626
+ const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
4627
+ while (patternPos < patternLen && wordPos < wordLen) {
4628
+ if (patternLow[patternPos] === wordLow[wordPos]) {
4629
+ patternPos += 1;
4630
+ }
4631
+ wordPos += 1;
4632
+ }
4633
+ return patternPos === patternLen; // pattern must be exhausted
4634
+ };
4635
+
4636
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4637
+ const traceHighlights = (table, arrows, patternLength, wordLength) => {
4638
+ let row = patternLength;
4639
+ let column = wordLength;
4640
+ const matches = [];
4641
+ while (row >= 1 && column >= 1) {
4642
+ const arrow = arrows[row][column];
4643
+ if (arrow === Left) {
4644
+ column--;
4645
+ } else if (arrow === Diagonal) {
4646
+ row--;
4647
+ column--;
4648
+ const start = column + 1;
4649
+ while (row >= 1 && column >= 1) {
4650
+ const arrow = arrows[row][column];
4651
+ if (arrow === Left) {
4652
+ break;
4653
+ }
4654
+ if (arrow === Diagonal) {
4655
+ row--;
4656
+ column--;
4657
+ }
4658
+ }
4659
+ const end = column;
4660
+ matches.unshift(end, start);
4661
+ }
4662
+ }
4663
+ matches.unshift(table[patternLength][wordLength - 1]);
4664
+ return matches;
4665
+ };
4666
+
4667
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4668
+ const gridSize = 128;
4669
+ const table = createTable(gridSize);
4670
+ const arrows = createTable(gridSize);
4671
+ const filterCompletionItem = (pattern, word) => {
4672
+ const patternLength = Math.min(pattern.length, gridSize - 1);
4673
+ const wordLength = Math.min(word.length, gridSize - 1);
4674
+ const patternLower = pattern.toLowerCase();
4675
+ const wordLower = word.toLowerCase();
4676
+ if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
4677
+ return EmptyMatches;
4678
+ }
4679
+ let strongMatch = false;
4680
+ for (let row = 1; row < patternLength + 1; row++) {
4681
+ const rowChar = pattern[row - 1];
4682
+ const rowCharLow = patternLower[row - 1];
4683
+ for (let column = 1; column < wordLength + 1; column++) {
4684
+ const columnChar = word[column - 1];
4685
+ const columnCharLow = wordLower[column - 1];
4686
+ const columnCharBefore = word[column - 2] || '';
4687
+ const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
4688
+ const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch);
4689
+ if (row === 1 && score > 5) {
4690
+ strongMatch = true;
4691
+ }
4692
+ let diagonalScore = score + table[row - 1][column - 1];
4693
+ if (isDiagonalMatch && score !== -1) {
4694
+ diagonalScore += 2;
4695
+ }
4696
+ const leftScore = table[row][column - 1];
4697
+ if (leftScore > diagonalScore) {
4698
+ table[row][column] = leftScore;
4699
+ arrows[row][column] = Left;
4700
+ } else {
4701
+ table[row][column] = diagonalScore;
4702
+ arrows[row][column] = Diagonal;
4703
+ }
4704
+ }
4705
+ }
4706
+ if (!strongMatch) {
4707
+ return EmptyMatches;
4708
+ }
4709
+ const highlights = traceHighlights(table, arrows, patternLength, wordLength);
4710
+ return highlights;
4711
+ };
4712
+
4713
+ const Deprecated = 1 << 0;
4714
+
4715
+ const addEmptyMatch = item => {
4716
+ return {
4717
+ ...item,
4718
+ matches: EmptyMatches
4719
+ };
4720
+ };
4721
+ const filterCompletionItems = (completionItems, word) => {
4722
+ if (word === EmptyString) {
4723
+ return completionItems.map(addEmptyMatch);
4724
+ }
4725
+ const filteredCompletions = [];
4726
+ const deprecated = [];
4727
+ for (const completionItem of completionItems) {
4728
+ const {
4729
+ label,
4730
+ flags
4731
+ } = completionItem;
4732
+ const result = filterCompletionItem(word, label);
4733
+ if (result !== EmptyMatches) {
4734
+ if (flags & Deprecated) {
4735
+ // TODO avoid mutation
4736
+ completionItem.matches = EmptyMatches;
4737
+ deprecated.push(completionItem);
4738
+ } else {
4739
+ // TODO avoid mutation
4740
+ completionItem.matches = result;
4741
+ filteredCompletions.push(completionItem);
4742
+ }
4743
+ }
4744
+ }
4745
+ if (deprecated.length > 0) {
4746
+ filteredCompletions.push(...deprecated);
4747
+ }
4748
+ return filteredCompletions;
4749
+ };
4750
+
5192
4751
  const getEditor = editorUid => {
5193
4752
  const instance = get$6(editorUid);
5194
4753
  if (!instance) {
@@ -5206,6 +4765,17 @@ const getFinalDeltaY = (height, itemHeight, itemsLength) => {
5206
4765
  return finalDeltaY;
5207
4766
  };
5208
4767
 
4768
+ const getListHeight = (itemsLength, itemHeight, maxHeight) => {
4769
+ number$1(itemsLength);
4770
+ number$1(itemHeight);
4771
+ number$1(maxHeight);
4772
+ if (itemsLength === 0) {
4773
+ return itemHeight;
4774
+ }
4775
+ const totalHeight = itemsLength * itemHeight;
4776
+ return Math.min(totalHeight, maxHeight);
4777
+ };
4778
+
5209
4779
  const RE_WORD = /[\w\-]+$/;
5210
4780
  const getWordAtOffset = editor => {
5211
4781
  const {
@@ -5222,7 +4792,7 @@ const getWordAtOffset = editor => {
5222
4792
  }
5223
4793
  return '';
5224
4794
  };
5225
- const handleEditorType = (editorUid, state, text) => {
4795
+ const handleEditorType$1 = (editorUid, state, text) => {
5226
4796
  const editor = getEditor(editorUid);
5227
4797
  const {
5228
4798
  unfilteredItems,
@@ -5252,7 +4822,7 @@ const handleEditorType = (editorUid, state, text) => {
5252
4822
  finalDeltaY
5253
4823
  };
5254
4824
  };
5255
- const handleEditorDeleteLeft = (editorUid, state) => {
4825
+ const handleEditorDeleteLeft$1 = (editorUid, state) => {
5256
4826
  const editor = getEditor(editorUid);
5257
4827
  const {
5258
4828
  unfilteredItems,
@@ -5266,7 +4836,7 @@ const handleEditorDeleteLeft = (editorUid, state) => {
5266
4836
  const y$1 = y(editor, rowIndex);
5267
4837
  const wordAtOffset = getWordAtOffset(editor);
5268
4838
  if (!wordAtOffset) {
5269
- editor.completionState = None;
4839
+ editor.completionState = None$1;
5270
4840
  return {
5271
4841
  ...state,
5272
4842
  disposed: true
@@ -5292,7 +4862,7 @@ const dispose = state => {
5292
4862
  };
5293
4863
  };
5294
4864
  const disposeWithEditor = (state, editor) => {
5295
- editor.completionState = None;
4865
+ editor.completionState = None$1;
5296
4866
  editor.completionUid = 0;
5297
4867
  // Focus.removeAdditionalFocus(FocusKey.EditorCompletion)
5298
4868
  return dispose(state);
@@ -5634,11 +5204,8 @@ const editorSelectAllLeft = editor => {
5634
5204
  editorSelectHorizontalLeft(editor, lineCharacterStart);
5635
5205
  };
5636
5206
 
5637
- // @ts-ignore
5638
-
5639
5207
  // TODO handle virtual space
5640
5208
 
5641
- // @ts-ignore
5642
5209
  const getNewSelectionsSingleLineWord = (lines, word) => {
5643
5210
  if (word.length === 0) {
5644
5211
  throw new Error('word length must be greater than zero');
@@ -5653,8 +5220,6 @@ const getNewSelectionsSingleLineWord = (lines, word) => {
5653
5220
  }
5654
5221
  return new Uint32Array(newSelections);
5655
5222
  };
5656
-
5657
- // @ts-ignore
5658
5223
  const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5659
5224
  let j = 0;
5660
5225
  if (!lines[rowIndex + j].endsWith(wordParts[j])) {
@@ -5675,11 +5240,11 @@ const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5675
5240
 
5676
5241
  // TODO this is very expensive, there might be a better algorithm for this
5677
5242
  // TODO if this matches the given selections, don't schedule selections/rerender
5678
- // @ts-ignore
5679
5243
  const getAllOccurrencesMultiLine = (lines, wordParts) => {
5680
5244
  const newSelections = [];
5681
5245
  for (let rowIndex = 0; rowIndex < lines.length - wordParts.length + 1; rowIndex) {
5682
5246
  if (isMultiLineMatch(lines, rowIndex, wordParts)) {
5247
+ // @ts-ignore
5683
5248
  newSelections.push(rowIndex, lines[rowIndex].length - wordParts[0].length, rowIndex + wordParts.length - 1, wordParts.at(-1).length);
5684
5249
  rowIndex += wordParts.length - 1;
5685
5250
  } else {
@@ -5691,13 +5256,9 @@ const getAllOccurrencesMultiLine = (lines, wordParts) => {
5691
5256
 
5692
5257
  // TODO duplicate code with EditorSelectNextOccurrence
5693
5258
  const RE_ALPHA_NUMERIC$1 = /[a-zA-Z\d]/;
5694
-
5695
- // @ts-ignore
5696
5259
  const isAlphaNumeric$1 = char => {
5697
5260
  return RE_ALPHA_NUMERIC$1.test(char);
5698
5261
  };
5699
-
5700
- // @ts-ignore
5701
5262
  const getWordStartIndex$1 = (line, index) => {
5702
5263
  for (let i = index - 1; i >= 0; i--) {
5703
5264
  if (!isAlphaNumeric$1(line[i])) {
@@ -5706,8 +5267,6 @@ const getWordStartIndex$1 = (line, index) => {
5706
5267
  }
5707
5268
  return 0;
5708
5269
  };
5709
-
5710
- // @ts-ignore
5711
5270
  const getWordEndIndex$1 = (line, index) => {
5712
5271
  for (let i = index; i < line.length; i++) {
5713
5272
  if (!isAlphaNumeric$1(line[i])) {
@@ -5716,8 +5275,6 @@ const getWordEndIndex$1 = (line, index) => {
5716
5275
  }
5717
5276
  return line.length - 1;
5718
5277
  };
5719
-
5720
- // @ts-ignore
5721
5278
  const getWordMatchAtPosition$1 = (lines, rowIndex, columnIndex) => {
5722
5279
  const line = lines[rowIndex];
5723
5280
  const start = getWordStartIndex$1(line, columnIndex);
@@ -5729,8 +5286,6 @@ const getWordMatchAtPosition$1 = (lines, rowIndex, columnIndex) => {
5729
5286
  word
5730
5287
  };
5731
5288
  };
5732
-
5733
- // @ts-ignore
5734
5289
  const getNewSelections$3 = (lines, selections) => {
5735
5290
  if (selections.length < 4) {
5736
5291
  throw new Error('selections must have at least one entry');
@@ -5763,12 +5318,12 @@ const getNewSelections$3 = (lines, selections) => {
5763
5318
  const newSelections = getAllOccurrencesMultiLine(lines, wordParts);
5764
5319
  return newSelections;
5765
5320
  };
5766
-
5767
- // @ts-ignore
5768
5321
  const selectAllOccurrences = editor => {
5769
5322
  // when there are no selections -> first selection is word -> find all selection that include word
5770
- const lines = editor.lines;
5771
- const selections = editor.selections;
5323
+ const {
5324
+ lines,
5325
+ selections
5326
+ } = editor;
5772
5327
  const newSelections = getNewSelections$3(lines, selections);
5773
5328
  return scheduleSelections(editor, newSelections);
5774
5329
  };
@@ -7203,7 +6758,7 @@ const select = async (editor, completionItem) => {
7203
6758
  ();
7204
6759
  if (index !== -1) {
7205
6760
  editor.widgets.splice(index, 1);
7206
- editor.completionState = None;
6761
+ editor.completionState = None$1;
7207
6762
  editor.completionUid = 0;
7208
6763
  }
7209
6764
  // TODO dispose completion widget
@@ -7266,7 +6821,7 @@ const getHover = async (editor, offset) => {
7266
6821
 
7267
6822
  let _ipc;
7268
6823
  const listen$5 = async () => {
7269
- const ipc = await create({
6824
+ const ipc = await create$1({
7270
6825
  method: RendererProcess
7271
6826
  });
7272
6827
  handleIpc(ipc);
@@ -7495,6 +7050,14 @@ const handleSashPointerUp = (state, eventX, eventY) => {
7495
7050
  return state;
7496
7051
  };
7497
7052
 
7053
+ const text = data => {
7054
+ return {
7055
+ type: Text,
7056
+ text: data,
7057
+ childCount: 0
7058
+ };
7059
+ };
7060
+
7498
7061
  const getLineInfoVirtualDom = lineInfo => {
7499
7062
  const dom = [{
7500
7063
  type: Div,
@@ -7582,7 +7145,7 @@ const renderHoverDom = {
7582
7145
  return [/* method */'Viewlet.setDom2', dom];
7583
7146
  }
7584
7147
  };
7585
- const renderBounds = {
7148
+ const renderBounds$1 = {
7586
7149
  isEqual(oldState, newState) {
7587
7150
  return oldState.x === newState.x && oldState.y === newState.y && oldState.resizedWidth === newState.resizedWidth;
7588
7151
  },
@@ -7600,10 +7163,10 @@ const renderBounds = {
7600
7163
  return [SetBounds, x, y, resizedWidth, height];
7601
7164
  }
7602
7165
  };
7603
- const render$1 = [renderHoverDom, renderBounds];
7166
+ const render$3 = [renderHoverDom, renderBounds$1];
7604
7167
  const renderHover = async (oldState, newState) => {
7605
7168
  const commands = [];
7606
- for (const item of render$1) {
7169
+ for (const item of render$3) {
7607
7170
  if (!item.isEqual(oldState, newState)) {
7608
7171
  commands.push(item.apply(oldState, newState));
7609
7172
  }
@@ -7909,7 +7472,7 @@ const moveLineUp = editor => {
7909
7472
  };
7910
7473
 
7911
7474
  const Link$1 = 'Link';
7912
- const Function = 'Function';
7475
+ const Function$1 = 'Function';
7913
7476
  const Parameter = 'Parameter';
7914
7477
  const Type = 'Type';
7915
7478
  const VariableName = 'VariableName';
@@ -7968,7 +7531,7 @@ const getDecorationClassName = type => {
7968
7531
  case Ts3073:
7969
7532
  case Ts3077:
7970
7533
  case Ts3088:
7971
- return Function;
7534
+ return Function$1;
7972
7535
  case Ts1792:
7973
7536
  case Ts1793:
7974
7537
  return Parameter;
@@ -8004,521 +7567,872 @@ const getDecorationClassName = type => {
8004
7567
  return `Unknown-${type}`;
8005
7568
  }
8006
7569
  };
8007
-
8008
- const getTokensViewportEmbedded = (langageId, lines, lineCache, linesWithEmbed) => {
8009
- const tokenizersToLoad = [];
8010
- const embeddedResults = [];
8011
- let topContext;
8012
- for (const index of linesWithEmbed) {
8013
- const result = lineCache[index + 1];
8014
- const line = lines[index];
8015
- if (result.embeddedLanguage) {
8016
- const {
8017
- embeddedLanguage,
8018
- embeddedLanguageStart,
8019
- embeddedLanguageEnd
8020
- } = result;
8021
- const embeddedTokenizer = getTokenizer(embeddedLanguage);
8022
- if (embeddedLanguageStart !== line.length && embeddedTokenizer && embeddedTokenizer !== TokenizePlainText) {
8023
- const isFull = embeddedLanguageStart === 0 && embeddedLanguageEnd === line.length;
8024
- const partialLine = line.slice(embeddedLanguageStart, embeddedLanguageEnd);
8025
- const embedResult = safeTokenizeLine(langageId, embeddedTokenizer.tokenizeLine, partialLine, topContext || getInitialLineState(embeddedTokenizer.initialLineState), embeddedTokenizer.hasArrayReturn);
8026
- topContext = embedResult;
8027
- result.embeddedResultIndex = embeddedResults.length;
8028
- embeddedResults.push({
8029
- result: embedResult,
8030
- TokenMap: embeddedTokenizer.TokenMap,
8031
- isFull
8032
- });
8033
- } else if (line.length === 0) {
8034
- const embedResult = {
8035
- tokens: []
8036
- };
8037
- result.embeddedResultIndex = embeddedResults.length;
8038
- embeddedResults.push({
8039
- result: embedResult,
8040
- isFull: true,
8041
- TokenMap: []
8042
- });
8043
- } else {
8044
- tokenizersToLoad.push(embeddedLanguage);
8045
- embeddedResults.push({
8046
- result: {},
8047
- isFull: false,
8048
- TokenMap: []
8049
- });
8050
- topContext = undefined;
8051
- }
8052
- } else {
8053
- topContext = undefined;
7570
+
7571
+ const getTokensViewportEmbedded = (langageId, lines, lineCache, linesWithEmbed) => {
7572
+ const tokenizersToLoad = [];
7573
+ const embeddedResults = [];
7574
+ let topContext;
7575
+ for (const index of linesWithEmbed) {
7576
+ const result = lineCache[index + 1];
7577
+ const line = lines[index];
7578
+ if (result.embeddedLanguage) {
7579
+ const {
7580
+ embeddedLanguage,
7581
+ embeddedLanguageStart,
7582
+ embeddedLanguageEnd
7583
+ } = result;
7584
+ const embeddedTokenizer = getTokenizer(embeddedLanguage);
7585
+ if (embeddedLanguageStart !== line.length && embeddedTokenizer && embeddedTokenizer !== TokenizePlainText) {
7586
+ const isFull = embeddedLanguageStart === 0 && embeddedLanguageEnd === line.length;
7587
+ const partialLine = line.slice(embeddedLanguageStart, embeddedLanguageEnd);
7588
+ const embedResult = safeTokenizeLine(langageId, embeddedTokenizer.tokenizeLine, partialLine, topContext || getInitialLineState(embeddedTokenizer.initialLineState), embeddedTokenizer.hasArrayReturn);
7589
+ topContext = embedResult;
7590
+ result.embeddedResultIndex = embeddedResults.length;
7591
+ embeddedResults.push({
7592
+ result: embedResult,
7593
+ TokenMap: embeddedTokenizer.TokenMap,
7594
+ isFull
7595
+ });
7596
+ } else if (line.length === 0) {
7597
+ const embedResult = {
7598
+ tokens: []
7599
+ };
7600
+ result.embeddedResultIndex = embeddedResults.length;
7601
+ embeddedResults.push({
7602
+ result: embedResult,
7603
+ isFull: true,
7604
+ TokenMap: []
7605
+ });
7606
+ } else {
7607
+ tokenizersToLoad.push(embeddedLanguage);
7608
+ embeddedResults.push({
7609
+ result: {},
7610
+ isFull: false,
7611
+ TokenMap: []
7612
+ });
7613
+ topContext = undefined;
7614
+ }
7615
+ } else {
7616
+ topContext = undefined;
7617
+ }
7618
+ }
7619
+ return {
7620
+ tokenizersToLoad,
7621
+ embeddedResults
7622
+ };
7623
+ };
7624
+ const getTokenizeEndIndex = (invalidStartIndex, endLineIndex, tokenizeStartIndex) => {
7625
+ return invalidStartIndex < endLineIndex ? endLineIndex : tokenizeStartIndex;
7626
+ };
7627
+
7628
+ // TODO only send changed lines to renderer process instead of all lines in viewport
7629
+ const getTokensViewport = (editor, startLineIndex, endLineIndex) => {
7630
+ const {
7631
+ invalidStartIndex,
7632
+ lineCache,
7633
+ tokenizerId,
7634
+ lines,
7635
+ languageId
7636
+ } = editor;
7637
+ const tokenizer = get(tokenizerId);
7638
+ const {
7639
+ hasArrayReturn,
7640
+ tokenizeLine,
7641
+ initialLineState
7642
+ } = tokenizer;
7643
+ const tokenizeStartIndex = invalidStartIndex;
7644
+ const tokenizeEndIndex = getTokenizeEndIndex(invalidStartIndex, endLineIndex, tokenizeStartIndex);
7645
+ const tokenizersToLoad = [];
7646
+ const embeddedResults = [];
7647
+ const linesWithEmbed = [];
7648
+ for (let i = tokenizeStartIndex; i < tokenizeEndIndex; i++) {
7649
+ const lineState = i === 0 ? getInitialLineState(initialLineState) : lineCache[i];
7650
+ const line = lines[i];
7651
+ const result = safeTokenizeLine(languageId, tokenizeLine, line, lineState, hasArrayReturn);
7652
+ // TODO if lineCacheEnd matches the one before, skip tokenizing lines after
7653
+ lineCache[i + 1] = result;
7654
+ if (result.embeddedLanguage) {
7655
+ result.embeddedResultIndex = linesWithEmbed.length;
7656
+ linesWithEmbed.push(i);
7657
+ }
7658
+ }
7659
+ const visibleLines = lineCache.slice(startLineIndex + 1, endLineIndex + 1);
7660
+ if (linesWithEmbed.length > 0) {
7661
+ const {
7662
+ tokenizersToLoad,
7663
+ embeddedResults
7664
+ } = getTokensViewportEmbedded(languageId, lines, lineCache, linesWithEmbed);
7665
+ // TODO support lineCache with embedded content
7666
+ editor.invalidStartIndex = 0;
7667
+ return {
7668
+ tokens: visibleLines,
7669
+ tokenizersToLoad,
7670
+ embeddedResults
7671
+ };
7672
+ }
7673
+ editor.invalidStartIndex = Math.max(invalidStartIndex, tokenizeEndIndex);
7674
+ return {
7675
+ tokens: visibleLines,
7676
+ tokenizersToLoad,
7677
+ embeddedResults
7678
+ };
7679
+ };
7680
+
7681
+ const sentLines = Object.create(null);
7682
+
7683
+ // TODO only send changed lines to renderer process instead of all lines in viewport
7684
+ const getTokensViewport2 = (editor, startLineIndex, endLineIndex, syncIncremental) => {
7685
+ if (getEnabled$1()) {
7686
+ if (syncIncremental) {
7687
+ const {
7688
+ invalidStartIndex,
7689
+ lines,
7690
+ languageId,
7691
+ id
7692
+ } = editor;
7693
+ let hasLinesToSend = true;
7694
+ let linesToSend = lines;
7695
+ if (sentLines[id] === lines) {
7696
+ hasLinesToSend = false;
7697
+ linesToSend = [];
7698
+ } else {
7699
+ sentLines[id] = lines;
7700
+ }
7701
+ const slimEditor = {
7702
+ languageId,
7703
+ invalidStartIndex
7704
+ };
7705
+ return invoke$1('GetTokensViewport.getTokensViewport', slimEditor, startLineIndex, endLineIndex, hasLinesToSend, id, linesToSend);
7706
+ }
7707
+ // TODO only send needed lines of text
7708
+ return invoke$1('GetTokensViewport.getTokensViewport', editor, startLineIndex, endLineIndex, true, editor.id, editor.lines);
7709
+ }
7710
+ return getTokensViewport(editor, startLineIndex, endLineIndex);
7711
+ };
7712
+
7713
+ const loadTokenizers = async languageIds => {
7714
+ for (const languageId of languageIds) {
7715
+ // @ts-ignore
7716
+ await loadTokenizer(languageId);
7717
+ }
7718
+ };
7719
+
7720
+ // const getTokensIncremental = (editor, min, max) => {
7721
+ // const currentLength = editor.lineStateCache.length
7722
+ // const tokens = []
7723
+ // const lines = editor.lines
7724
+ // let lineState = editor.tokenizer.initialLineState
7725
+ // for (let i = currentLength; i < max; i++) {
7726
+ // const line = lines[i]
7727
+ // try {
7728
+ // lineState = editor.tokenizer.tokenizeLine(line, lineState)
7729
+ // if (!lineState || !lineState.tokens || !lineState.state) {
7730
+ // throw new Error('invalid tokenization result')
7731
+ // }
7732
+ // } catch (error) {
7733
+ // tokens.push([{ length: line.length, type: 0 }])
7734
+ // console.error(error)
7735
+ // // renderWithoutSyntaxHighlighting(state, firstRow, lastRow)
7736
+ // continue
7737
+ // }
7738
+ // const newTokens = lineState.tokens
7739
+ // tokens.push(newTokens)
7740
+ // }
7741
+ // return tokens
7742
+ // }
7743
+
7744
+ // const getLineInfosIncremental = (editor, tokens, minLineY, maxLineY) => {
7745
+ // const result = []
7746
+ // const lines = editor.lines
7747
+ // const TokenMap = editor.tokenizer.TokenMap
7748
+ // for (let i = minLineY; i < maxLineY; i++) {
7749
+ // result.push(getLineInfo(lines[i], tokens[i], TokenMap))
7750
+ // }
7751
+ // return result
7752
+ // }
7753
+
7754
+ const getStartDefaults = (tokens, minOffset) => {
7755
+ let start = 0;
7756
+ let end = 0;
7757
+ let startIndex = 0;
7758
+ const tokensLength = tokens.length;
7759
+ for (let i = 0; i < tokensLength; i += 2) {
7760
+ const tokenLength = tokens[i + 1];
7761
+ end += tokenLength;
7762
+ start = end;
7763
+ if (start >= minOffset) {
7764
+ start -= tokenLength;
7765
+ end -= tokenLength;
7766
+ startIndex = i;
7767
+ break;
7768
+ }
7769
+ }
7770
+ return {
7771
+ start,
7772
+ end,
7773
+ startIndex
7774
+ };
7775
+ };
7776
+ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
7777
+ const lineInfo = [];
7778
+ const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
7779
+ const embeddedTokens = embeddedResult.result.tokens;
7780
+ const embeddedTokenMap = embeddedResult.TokenMap;
7781
+ const tokensLength = embeddedTokens.length;
7782
+ let {
7783
+ startIndex,
7784
+ start,
7785
+ end
7786
+ } = getStartDefaults(embeddedTokens, minOffset);
7787
+ const difference = getDifference(start, averageCharWidth, deltaX);
7788
+ for (let i = startIndex; i < tokensLength; i += 2) {
7789
+ const tokenType = embeddedTokens[i];
7790
+ const tokenLength = embeddedTokens[i + 1];
7791
+ end += tokenLength;
7792
+ const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
7793
+ const text = line.slice(start, end);
7794
+ const normalizedText = normalizeText(text, normalize, tabSize);
7795
+ lineInfo.push(normalizedText, className);
7796
+ start = end;
7797
+ if (end >= maxOffset) {
7798
+ break;
8054
7799
  }
8055
7800
  }
8056
7801
  return {
8057
- tokenizersToLoad,
8058
- embeddedResults
7802
+ lineInfo,
7803
+ difference
8059
7804
  };
8060
7805
  };
8061
- const getTokenizeEndIndex = (invalidStartIndex, endLineIndex, tokenizeStartIndex) => {
8062
- return invalidStartIndex < endLineIndex ? endLineIndex : tokenizeStartIndex;
7806
+ const getOffsets = (deltaX, width, averageCharWidth) => {
7807
+ // TODO accurately measure char widths using offscreen canvas
7808
+ // and use fast measurements for monospace ascii text
7809
+ if (deltaX === 0) {
7810
+ return {
7811
+ minOffset: 0,
7812
+ maxOffset: Math.ceil(width / averageCharWidth)
7813
+ };
7814
+ }
7815
+ const minOffset = Math.ceil(deltaX / averageCharWidth);
7816
+ const maxOffset = minOffset + Math.ceil(width / averageCharWidth);
7817
+ return {
7818
+ minOffset,
7819
+ maxOffset
7820
+ };
7821
+ };
7822
+ const getDifference = (start, averageCharWidth, deltaX) => {
7823
+ const beforeWidth = start * averageCharWidth;
7824
+ const difference = beforeWidth - deltaX;
7825
+ return difference;
7826
+ };
7827
+ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
7828
+ const lineInfo = [];
7829
+ let decorationIndex = 0;
7830
+ for (; decorationIndex < decorations.length; decorationIndex += 3) {
7831
+ const decorationOffset = decorations[decorationIndex];
7832
+ if (decorationOffset >= lineOffset) {
7833
+ break;
7834
+ }
7835
+ }
7836
+ const tokens = tokenResults.tokens;
7837
+ let {
7838
+ startIndex,
7839
+ start,
7840
+ end
7841
+ } = getStartDefaults(tokens, minOffset);
7842
+ const difference = getDifference(start, averageCharWidth, deltaX);
7843
+ const tokensLength = tokens.length;
7844
+ for (let i = startIndex; i < tokensLength; i += 2) {
7845
+ const tokenType = tokens[i];
7846
+ const tokenLength = tokens[i + 1];
7847
+ const decorationOffset = decorations[decorationIndex];
7848
+ let extraClassName = '';
7849
+ if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
7850
+ // @ts-ignore
7851
+ decorations[++decorationIndex];
7852
+ const decorationType = decorations[++decorationIndex];
7853
+ // @ts-ignore
7854
+ decorations[++decorationIndex];
7855
+ // console.log('MATCHING DECORATION', {
7856
+ // decorationIndex,
7857
+ // decorationLength,
7858
+ // decorationType,
7859
+ // decorationModifiers,
7860
+ // })
7861
+ extraClassName = getDecorationClassName(decorationType);
7862
+ }
7863
+ end += tokenLength;
7864
+ const text = line.slice(start, end);
7865
+ const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
7866
+ const normalizedText = normalizeText(text, normalize, tabSize);
7867
+ lineInfo.push(normalizedText, className);
7868
+ start = end;
7869
+ if (end >= maxOffset) {
7870
+ break;
7871
+ }
7872
+ }
7873
+ return {
7874
+ lineInfo,
7875
+ difference
7876
+ };
7877
+ };
7878
+ const getLineInfo = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
7879
+ const {
7880
+ minOffset,
7881
+ maxOffset
7882
+ } = getOffsets(deltaX, width, averageCharWidth);
7883
+ if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
7884
+ const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
7885
+ if (embeddedResult && embeddedResult.isFull) {
7886
+ return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
7887
+ }
7888
+ }
7889
+ return getLineInfoDefault(line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8063
7890
  };
8064
7891
 
8065
- // TODO only send changed lines to renderer process instead of all lines in viewport
8066
- const getTokensViewport = (editor, startLineIndex, endLineIndex) => {
7892
+ // TODO need lots of tests for this
7893
+ const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
7894
+ const result = [];
7895
+ const differences = [];
8067
7896
  const {
8068
- invalidStartIndex,
8069
- lineCache,
8070
- tokenizerId,
8071
7897
  lines,
7898
+ decorations,
8072
7899
  languageId
8073
7900
  } = editor;
8074
- const tokenizer = get(tokenizerId);
8075
- const {
8076
- hasArrayReturn,
8077
- tokenizeLine,
8078
- initialLineState
8079
- } = tokenizer;
8080
- const tokenizeStartIndex = invalidStartIndex;
8081
- const tokenizeEndIndex = getTokenizeEndIndex(invalidStartIndex, endLineIndex, tokenizeStartIndex);
8082
- const tokenizersToLoad = [];
8083
- const embeddedResults = [];
8084
- const linesWithEmbed = [];
8085
- for (let i = tokenizeStartIndex; i < tokenizeEndIndex; i++) {
8086
- const lineState = i === 0 ? getInitialLineState(initialLineState) : lineCache[i];
7901
+ const tokenMap = get$1(languageId);
7902
+ let offset = minLineOffset;
7903
+ const tabSize = 2;
7904
+ for (let i = minLineY; i < maxLineY; i++) {
8087
7905
  const line = lines[i];
8088
- const result = safeTokenizeLine(languageId, tokenizeLine, line, lineState, hasArrayReturn);
8089
- // TODO if lineCacheEnd matches the one before, skip tokenizing lines after
8090
- lineCache[i + 1] = result;
8091
- if (result.embeddedLanguage) {
8092
- result.embeddedResultIndex = linesWithEmbed.length;
8093
- linesWithEmbed.push(i);
8094
- }
8095
- }
8096
- const visibleLines = lineCache.slice(startLineIndex + 1, endLineIndex + 1);
8097
- if (linesWithEmbed.length > 0) {
7906
+ const normalize = shouldNormalizeText(line);
8098
7907
  const {
8099
- tokenizersToLoad,
8100
- embeddedResults
8101
- } = getTokensViewportEmbedded(languageId, lines, lineCache, linesWithEmbed);
8102
- // TODO support lineCache with embedded content
8103
- editor.invalidStartIndex = 0;
8104
- return {
8105
- tokens: visibleLines,
8106
- tokenizersToLoad,
8107
- embeddedResults
8108
- };
7908
+ lineInfo,
7909
+ difference
7910
+ } = getLineInfo(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
7911
+ result.push(lineInfo);
7912
+ differences.push(difference);
7913
+ offset += line.length + 1;
8109
7914
  }
8110
- editor.invalidStartIndex = Math.max(invalidStartIndex, tokenizeEndIndex);
8111
7915
  return {
8112
- tokens: visibleLines,
7916
+ result,
7917
+ differences
7918
+ };
7919
+ };
7920
+ const getVisible = async (editor, syncIncremental) => {
7921
+ // console.log({ editor })
7922
+ // TODO should separate rendering from business logic somehow
7923
+ // currently hard to test because need to mock editor height, top, left,
7924
+ // invalidStartIndex, lineCache, etc. just for testing editorType
7925
+ // editor.invalidStartIndex = changes[0].start.rowIndex
7926
+ // @ts-ignore
7927
+ const {
7928
+ minLineY,
7929
+ numberOfVisibleLines,
7930
+ lines,
7931
+ width,
7932
+ deltaX,
7933
+ fontWeight,
7934
+ fontSize,
7935
+ fontFamily,
7936
+ letterSpacing,
7937
+ charWidth
7938
+ } = editor;
7939
+ const maxLineY = Math.min(minLineY + numberOfVisibleLines, lines.length);
7940
+ const {
7941
+ tokens,
8113
7942
  tokenizersToLoad,
8114
7943
  embeddedResults
7944
+ } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
7945
+ const minLineOffset = offsetAtSync(editor, minLineY, 0);
7946
+ const averageCharWidth = charWidth;
7947
+ const {
7948
+ result,
7949
+ differences
7950
+ } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
7951
+ if (tokenizersToLoad.length > 0) {
7952
+ loadTokenizers(tokenizersToLoad);
7953
+ }
7954
+ return {
7955
+ textInfos: result,
7956
+ differences
8115
7957
  };
8116
7958
  };
8117
7959
 
8118
- const sentLines = Object.create(null);
8119
-
8120
- // TODO only send changed lines to renderer process instead of all lines in viewport
8121
- const getTokensViewport2 = (editor, startLineIndex, endLineIndex, syncIncremental) => {
8122
- if (getEnabled$1()) {
8123
- if (syncIncremental) {
8124
- const {
8125
- invalidStartIndex,
8126
- lines,
8127
- languageId,
8128
- id
8129
- } = editor;
8130
- let hasLinesToSend = true;
8131
- let linesToSend = lines;
8132
- if (sentLines[id] === lines) {
8133
- hasLinesToSend = false;
8134
- linesToSend = [];
8135
- } else {
8136
- sentLines[id] = lines;
8137
- }
8138
- const slimEditor = {
8139
- languageId,
8140
- invalidStartIndex
8141
- };
8142
- return invoke$1('GetTokensViewport.getTokensViewport', slimEditor, startLineIndex, endLineIndex, hasLinesToSend, id, linesToSend);
8143
- }
8144
- // TODO only send needed lines of text
8145
- return invoke$1('GetTokensViewport.getTokensViewport', editor, startLineIndex, endLineIndex, true, editor.id, editor.lines);
7960
+ const getCursorsVirtualDom = cursors => {
7961
+ const dom = [];
7962
+ for (const translate of cursors) {
7963
+ dom.push({
7964
+ type: Div,
7965
+ className: EditorCursor,
7966
+ translate
7967
+ });
8146
7968
  }
8147
- return getTokensViewport(editor, startLineIndex, endLineIndex);
7969
+ return dom;
8148
7970
  };
8149
7971
 
8150
- const loadTokenizers = async languageIds => {
8151
- for (const languageId of languageIds) {
8152
- // @ts-ignore
8153
- await loadTokenizer(languageId);
7972
+ const Error$1 = 'error';
7973
+ const Warning = 'warning';
7974
+
7975
+ const getDiagnosticClassName = type => {
7976
+ // TODO use classnames enum
7977
+ switch (type) {
7978
+ case Error$1:
7979
+ return 'DiagnosticError';
7980
+ case Warning:
7981
+ return 'DiagnosticWarning';
7982
+ default:
7983
+ return 'DiagnosticError';
8154
7984
  }
8155
7985
  };
7986
+ const getDiagnosticVirtualDom = diagnostic => {
7987
+ const {
7988
+ x,
7989
+ y,
7990
+ width,
7991
+ height,
7992
+ type
7993
+ } = diagnostic;
7994
+ const extraClassName = getDiagnosticClassName(type);
7995
+ return [{
7996
+ type: Div,
7997
+ className: `${Diagnostic} ${extraClassName}`,
7998
+ width,
7999
+ height,
8000
+ top: y,
8001
+ left: x,
8002
+ childCount: 0
8003
+ }];
8004
+ };
8156
8005
 
8157
- // const getTokensIncremental = (editor, min, max) => {
8158
- // const currentLength = editor.lineStateCache.length
8159
- // const tokens = []
8160
- // const lines = editor.lines
8161
- // let lineState = editor.tokenizer.initialLineState
8162
- // for (let i = currentLength; i < max; i++) {
8163
- // const line = lines[i]
8164
- // try {
8165
- // lineState = editor.tokenizer.tokenizeLine(line, lineState)
8166
- // if (!lineState || !lineState.tokens || !lineState.state) {
8167
- // throw new Error('invalid tokenization result')
8168
- // }
8169
- // } catch (error) {
8170
- // tokens.push([{ length: line.length, type: 0 }])
8171
- // console.error(error)
8172
- // // renderWithoutSyntaxHighlighting(state, firstRow, lastRow)
8173
- // continue
8174
- // }
8175
- // const newTokens = lineState.tokens
8176
- // tokens.push(newTokens)
8177
- // }
8178
- // return tokens
8179
- // }
8006
+ const getDiagnosticsVirtualDom = diagnostics => {
8007
+ const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
8008
+ return dom;
8009
+ };
8180
8010
 
8181
- // const getLineInfosIncremental = (editor, tokens, minLineY, maxLineY) => {
8182
- // const result = []
8183
- // const lines = editor.lines
8184
- // const TokenMap = editor.tokenizer.TokenMap
8185
- // for (let i = minLineY; i < maxLineY; i++) {
8186
- // result.push(getLineInfo(lines[i], tokens[i], TokenMap))
8187
- // }
8188
- // return result
8189
- // }
8011
+ const getGutterInfoVirtualDom = gutterInfo => {
8012
+ return [{
8013
+ type: Span,
8014
+ className: 'LineNumber',
8015
+ childCount: 1
8016
+ }, text(gutterInfo)];
8017
+ };
8018
+ const getEditorGutterVirtualDom = gutterInfos => {
8019
+ const dom = gutterInfos.flatMap(getGutterInfoVirtualDom);
8020
+ return dom;
8021
+ };
8190
8022
 
8191
- const getStartDefaults = (tokens, minOffset) => {
8192
- let start = 0;
8193
- let end = 0;
8194
- let startIndex = 0;
8195
- const tokensLength = tokens.length;
8196
- for (let i = 0; i < tokensLength; i += 2) {
8197
- const tokenLength = tokens[i + 1];
8198
- end += tokenLength;
8199
- start = end;
8200
- if (start >= minOffset) {
8201
- start -= tokenLength;
8202
- end -= tokenLength;
8203
- startIndex = i;
8204
- break;
8023
+ const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true) => {
8024
+ const dom = [];
8025
+ for (let i = 0; i < textInfos.length; i++) {
8026
+ const textInfo = textInfos[i];
8027
+ const difference = differences[i];
8028
+ dom.push({
8029
+ type: Div,
8030
+ className: EditorRow,
8031
+ translate: px(difference),
8032
+ childCount: textInfo.length / 2
8033
+ });
8034
+ for (let j = 0; j < textInfo.length; j += 2) {
8035
+ const tokenText = textInfo[j];
8036
+ const className = textInfo[j + 1];
8037
+ dom.push({
8038
+ type: Span,
8039
+ className,
8040
+ childCount: 1
8041
+ }, text(tokenText));
8205
8042
  }
8206
8043
  }
8207
- return {
8208
- start,
8209
- end,
8210
- startIndex
8211
- };
8044
+ return dom;
8212
8045
  };
8213
- const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
8214
- const lineInfo = [];
8215
- const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
8216
- const embeddedTokens = embeddedResult.result.tokens;
8217
- const embeddedTokenMap = embeddedResult.TokenMap;
8218
- const tokensLength = embeddedTokens.length;
8219
- let {
8220
- startIndex,
8221
- start,
8222
- end
8223
- } = getStartDefaults(embeddedTokens, minOffset);
8224
- const difference = getDifference(start, averageCharWidth, deltaX);
8225
- for (let i = startIndex; i < tokensLength; i += 2) {
8226
- const tokenType = embeddedTokens[i];
8227
- const tokenLength = embeddedTokens[i + 1];
8228
- end += tokenLength;
8229
- const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
8230
- const text = line.slice(start, end);
8231
- const normalizedText = normalizeText(text, normalize, tabSize);
8232
- lineInfo.push(normalizedText, className);
8233
- start = end;
8234
- if (end >= maxOffset) {
8235
- break;
8046
+
8047
+ const getIncrementalEdits = async (oldState, newState) => {
8048
+ if (!newState.undoStack) {
8049
+ return undefined;
8050
+ }
8051
+ const lastChanges = newState.undoStack.at(-1);
8052
+ if (lastChanges && lastChanges.length === 1) {
8053
+ const lastChange = lastChanges[0];
8054
+ if (lastChange.origin === EditorType) {
8055
+ const rowIndex = lastChange.start.rowIndex;
8056
+ const lines = newState.lines;
8057
+ const oldLine = oldState.lines[rowIndex];
8058
+ const newLine = lines[rowIndex];
8059
+ const incrementalEdits = await invoke$1('TokenizeIncremental.tokenizeIncremental', newState.uid, newState.languageId, oldLine, newLine, rowIndex, newState.minLineY);
8060
+ if (incrementalEdits && incrementalEdits.length === 1) {
8061
+ return incrementalEdits;
8062
+ }
8236
8063
  }
8237
8064
  }
8065
+ return undefined;
8066
+ };
8067
+
8068
+ const getSelectionsVirtualDom = selections => {
8069
+ const dom = [];
8070
+ for (let i = 0; i < selections.length; i += 4) {
8071
+ const x = selections[i];
8072
+ const y = selections[i + 1];
8073
+ const width = selections[i + 2];
8074
+ const height = selections[i + 3];
8075
+ dom.push({
8076
+ type: Div,
8077
+ className: EditorSelection,
8078
+ left: x,
8079
+ top: y,
8080
+ width,
8081
+ height
8082
+ });
8083
+ }
8084
+ return dom;
8085
+ };
8086
+
8087
+ const None = 'none';
8088
+ const Option = 'option';
8089
+
8090
+ const getFileIconVirtualDom = icon => {
8238
8091
  return {
8239
- lineInfo,
8240
- difference
8092
+ type: Img,
8093
+ className: FileIcon,
8094
+ src: icon,
8095
+ role: None,
8096
+ childCount: 0
8241
8097
  };
8242
8098
  };
8243
- const getOffsets = (deltaX, width, averageCharWidth) => {
8244
- // TODO accurately measure char widths using offscreen canvas
8245
- // and use fast measurements for monospace ascii text
8246
- if (deltaX === 0) {
8247
- return {
8248
- minOffset: 0,
8249
- maxOffset: Math.ceil(width / averageCharWidth)
8250
- };
8099
+
8100
+ const getIconDom = (fileIcon, symbolName) => {
8101
+ if (fileIcon) {
8102
+ return getFileIconVirtualDom(fileIcon);
8251
8103
  }
8252
- const minOffset = Math.ceil(deltaX / averageCharWidth);
8253
- const maxOffset = minOffset + Math.ceil(width / averageCharWidth);
8254
8104
  return {
8255
- minOffset,
8256
- maxOffset
8105
+ type: Div,
8106
+ className: `${ColoredMaskIcon} ${symbolName}`,
8107
+ childCount: 0
8257
8108
  };
8258
8109
  };
8259
- const getDifference = (start, averageCharWidth, deltaX) => {
8260
- const beforeWidth = start * averageCharWidth;
8261
- const difference = beforeWidth - deltaX;
8262
- return difference;
8110
+
8111
+ const label1 = {
8112
+ type: Div,
8113
+ className: Label,
8114
+ childCount: 1
8263
8115
  };
8264
- const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
8265
- const lineInfo = [];
8266
- let decorationIndex = 0;
8267
- for (; decorationIndex < decorations.length; decorationIndex += 3) {
8268
- const decorationOffset = decorations[decorationIndex];
8269
- if (decorationOffset >= lineOffset) {
8270
- break;
8271
- }
8116
+ const completionHighlight = {
8117
+ type: Span,
8118
+ className: EditorCompletionItemHighlight,
8119
+ childCount: 1
8120
+ };
8121
+ const getHighlightedLabelDom = (label, highlights) => {
8122
+ if (highlights.length === 0) {
8123
+ return [label1, text(label)];
8272
8124
  }
8273
- const tokens = tokenResults.tokens;
8274
- let {
8275
- startIndex,
8276
- start,
8277
- end
8278
- } = getStartDefaults(tokens, minOffset);
8279
- const difference = getDifference(start, averageCharWidth, deltaX);
8280
- const tokensLength = tokens.length;
8281
- for (let i = startIndex; i < tokensLength; i += 2) {
8282
- const tokenType = tokens[i];
8283
- const tokenLength = tokens[i + 1];
8284
- const decorationOffset = decorations[decorationIndex];
8285
- let extraClassName = '';
8286
- if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
8287
- // @ts-ignore
8288
- decorations[++decorationIndex];
8289
- const decorationType = decorations[++decorationIndex];
8290
- // @ts-ignore
8291
- decorations[++decorationIndex];
8292
- // console.log('MATCHING DECORATION', {
8293
- // decorationIndex,
8294
- // decorationLength,
8295
- // decorationType,
8296
- // decorationModifiers,
8297
- // })
8298
- extraClassName = getDecorationClassName(decorationType);
8299
- }
8300
- end += tokenLength;
8301
- const text = line.slice(start, end);
8302
- const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
8303
- const normalizedText = normalizeText(text, normalize, tabSize);
8304
- lineInfo.push(normalizedText, className);
8305
- start = end;
8306
- if (end >= maxOffset) {
8307
- break;
8125
+ const dom = [];
8126
+ const labelDom = {
8127
+ type: Div,
8128
+ className: Label,
8129
+ childCount: 0
8130
+ };
8131
+ dom.push(labelDom);
8132
+ let position = 0;
8133
+ for (let i = 0; i < highlights.length; i += 2) {
8134
+ const highlightStart = highlights[i];
8135
+ const highlightEnd = highlights[i + 1];
8136
+ if (position < highlightStart) {
8137
+ const beforeText = label.slice(position, highlightStart);
8138
+ labelDom.childCount++;
8139
+ dom.push(text(beforeText));
8308
8140
  }
8141
+ const highlightText = label.slice(highlightStart, highlightEnd);
8142
+ labelDom.childCount++;
8143
+ dom.push(completionHighlight, text(highlightText));
8144
+ position = highlightEnd;
8309
8145
  }
8310
- return {
8311
- lineInfo,
8312
- difference
8313
- };
8146
+ if (position < label.length) {
8147
+ const afterText = label.slice(position);
8148
+ labelDom.childCount++;
8149
+ dom.push(text(afterText));
8150
+ }
8151
+ return dom;
8314
8152
  };
8315
- const getLineInfo = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
8153
+
8154
+ const getCompletionItemVirtualDom = visibleItem => {
8316
8155
  const {
8317
- minOffset,
8318
- maxOffset
8319
- } = getOffsets(deltaX, width, averageCharWidth);
8320
- if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
8321
- const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
8322
- if (embeddedResult && embeddedResult.isFull) {
8323
- return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8324
- }
8156
+ top,
8157
+ label,
8158
+ symbolName,
8159
+ highlights,
8160
+ focused,
8161
+ deprecated,
8162
+ fileIcon
8163
+ } = visibleItem;
8164
+ let className = EditorCompletionItem;
8165
+ if (focused) {
8166
+ className += ' ' + EditorCompletionItemFocused;
8325
8167
  }
8326
- return getLineInfoDefault(line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8168
+ if (deprecated) {
8169
+ className += ' ' + EditorCompletionItemDeprecated;
8170
+ }
8171
+ return [{
8172
+ type: Div,
8173
+ role: Option,
8174
+ className,
8175
+ top,
8176
+ childCount: 2
8177
+ }, getIconDom(fileIcon, symbolName), ...getHighlightedLabelDom(label, highlights)];
8327
8178
  };
8328
8179
 
8329
- // TODO need lots of tests for this
8330
- const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
8331
- const result = [];
8332
- const differences = [];
8333
- const {
8334
- lines,
8335
- decorations,
8336
- languageId
8337
- } = editor;
8338
- const tokenMap = get$1(languageId);
8339
- let offset = minLineOffset;
8340
- const tabSize = 2;
8341
- for (let i = minLineY; i < maxLineY; i++) {
8342
- const line = lines[i];
8343
- const normalize = shouldNormalizeText(line);
8344
- const {
8345
- lineInfo,
8346
- difference
8347
- } = getLineInfo(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
8348
- result.push(lineInfo);
8349
- differences.push(difference);
8350
- offset += line.length + 1;
8180
+ const getCompletionItemsVirtualDom = visibleItems => {
8181
+ if (visibleItems.length === 0) {
8182
+ return [{
8183
+ type: Div,
8184
+ childCount: 1
8185
+ }, text(noResults())];
8351
8186
  }
8352
- return {
8353
- result,
8354
- differences
8187
+ const root = {
8188
+ type: Div,
8189
+ childCount: visibleItems.length
8355
8190
  };
8191
+ const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
8192
+ return dom;
8356
8193
  };
8357
- const getVisible = async (editor, syncIncremental) => {
8358
- // console.log({ editor })
8359
- // TODO should separate rendering from business logic somehow
8360
- // currently hard to test because need to mock editor height, top, left,
8361
- // invalidStartIndex, lineCache, etc. just for testing editorType
8362
- // editor.invalidStartIndex = changes[0].start.rowIndex
8363
- // @ts-ignore
8364
- const {
8365
- minLineY,
8366
- numberOfVisibleLines,
8367
- lines,
8368
- width,
8369
- deltaX,
8370
- fontWeight,
8371
- fontSize,
8372
- fontFamily,
8373
- letterSpacing,
8374
- charWidth
8375
- } = editor;
8376
- const maxLineY = Math.min(minLineY + numberOfVisibleLines, lines.length);
8377
- const {
8378
- tokens,
8379
- tokenizersToLoad,
8380
- embeddedResults
8381
- } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
8382
- const minLineOffset = offsetAtSync(editor, minLineY, 0);
8383
- const averageCharWidth = charWidth;
8384
- const {
8385
- result,
8386
- differences
8387
- } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
8388
- if (tokenizersToLoad.length > 0) {
8389
- loadTokenizers(tokenizersToLoad);
8194
+
8195
+ const Property = 1;
8196
+ const Value = 2;
8197
+ const Function = 3;
8198
+ const Variable = 4;
8199
+ const Keyword = 5;
8200
+ const Folder = 6;
8201
+ const File = 7;
8202
+ const Field = 8;
8203
+
8204
+ const SymbolProperty = 'SymbolProperty';
8205
+ const SymbolValue = 'SymbolValue';
8206
+ const SymbolFunction = 'SymbolFunction';
8207
+ const SymbolVariable = 'SymbolVariable';
8208
+ const SymbolKeyword = 'SymbolKeyword';
8209
+ const SymbolDefault = 'SymbolDefault';
8210
+ const SymbolField = 'SymbolField';
8211
+ const SymbolNone = '';
8212
+
8213
+ const getSymbolName = kind => {
8214
+ switch (kind) {
8215
+ case Property:
8216
+ return SymbolProperty;
8217
+ case Value:
8218
+ return SymbolValue;
8219
+ case Function:
8220
+ return SymbolFunction;
8221
+ case Variable:
8222
+ return SymbolVariable;
8223
+ case Keyword:
8224
+ return SymbolKeyword;
8225
+ case Field:
8226
+ return SymbolField;
8227
+ case File:
8228
+ return SymbolNone;
8229
+ default:
8230
+ return SymbolDefault;
8231
+ }
8232
+ };
8233
+
8234
+ // TODO
8235
+ const getCompletionFileIcon = kind => {
8236
+ switch (kind) {
8237
+ case File:
8238
+ return EmptyString;
8239
+ case Folder:
8240
+ return EmptyString;
8241
+ default:
8242
+ return EmptyString;
8390
8243
  }
8244
+ };
8245
+
8246
+ const getHighlights = item => {
8247
+ const {
8248
+ matches
8249
+ } = item;
8250
+ return matches.slice(1);
8251
+ };
8252
+
8253
+ const getLabel = item => {
8254
+ return item.label;
8255
+ };
8256
+ const getVisibleIem = (item, itemHeight, leadingWord, i, focusedIndex) => {
8391
8257
  return {
8392
- textInfos: result,
8393
- differences
8258
+ label: getLabel(item),
8259
+ symbolName: getSymbolName(item.kind),
8260
+ top: i * itemHeight,
8261
+ highlights: getHighlights(item),
8262
+ focused: i === focusedIndex,
8263
+ deprecated: item.flags & Deprecated,
8264
+ fileIcon: getCompletionFileIcon(item.kind)
8394
8265
  };
8395
8266
  };
8396
8267
 
8397
- const getCursorsVirtualDom = cursors => {
8398
- const dom = [];
8399
- for (const translate of cursors) {
8400
- dom.push({
8401
- type: Div,
8402
- className: EditorCursor,
8403
- translate
8404
- });
8268
+ const getVisibleItems = (filteredItems, itemHeight, leadingWord, minLineY, maxLineY, focusedIndex) => {
8269
+ const visibleItems = [];
8270
+ for (let i = minLineY; i < maxLineY; i++) {
8271
+ const filteredItem = filteredItems[i];
8272
+ visibleItems.push(getVisibleIem(filteredItem, itemHeight, leadingWord, i, focusedIndex));
8405
8273
  }
8406
- return dom;
8274
+ return visibleItems;
8407
8275
  };
8408
8276
 
8409
- const Error$1 = 'error';
8410
- const Warning = 'warning';
8411
-
8412
- const getDiagnosticClassName = type => {
8413
- // TODO use classnames enum
8414
- switch (type) {
8415
- case Error$1:
8416
- return 'DiagnosticError';
8417
- case Warning:
8418
- return 'DiagnosticWarning';
8419
- default:
8420
- return 'DiagnosticError';
8277
+ const renderItems = {
8278
+ isEqual(oldState, newState) {
8279
+ return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
8280
+ },
8281
+ apply(oldState, newState) {
8282
+ const visibleItems = getVisibleItems(newState.items, newState.itemHeight, newState.leadingWord, newState.minLineY, newState.maxLineY, newState.focusedIndex);
8283
+ const dom = getCompletionItemsVirtualDom(visibleItems);
8284
+ return ['setDom', dom];
8421
8285
  }
8422
8286
  };
8423
- const getDiagnosticVirtualDom = diagnostic => {
8424
- const {
8425
- x,
8426
- y,
8427
- width,
8428
- height,
8429
- type
8430
- } = diagnostic;
8431
- const extraClassName = getDiagnosticClassName(type);
8432
- return [{
8433
- type: Div,
8434
- className: `${Diagnostic} ${extraClassName}`,
8435
- width,
8436
- height,
8437
- top: y,
8438
- left: x,
8439
- childCount: 0
8440
- }];
8287
+ const renderBounds = {
8288
+ isEqual(oldState, newState) {
8289
+ return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.x === newState.x && oldState.y === newState.y;
8290
+ },
8291
+ apply(oldState, newState) {
8292
+ const {
8293
+ x,
8294
+ y,
8295
+ width,
8296
+ height
8297
+ } = newState;
8298
+ return [/* method */SetBounds, /* x */x, /* y */y, /* width */width, /* height */height];
8299
+ }
8441
8300
  };
8442
-
8443
- const getDiagnosticsVirtualDom = diagnostics => {
8444
- const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
8445
- return dom;
8301
+ const renderHeight = {
8302
+ isEqual(oldState, newState) {
8303
+ return oldState.items.length === newState.items.length;
8304
+ },
8305
+ apply(oldState, newState) {
8306
+ const {
8307
+ itemHeight
8308
+ } = newState;
8309
+ const contentHeight = newState.items.length * itemHeight;
8310
+ return [/* method */SetContentHeight, /* contentHeight */contentHeight];
8311
+ }
8446
8312
  };
8447
-
8448
- const getGutterInfoVirtualDom = gutterInfo => {
8449
- return [{
8450
- type: Span,
8451
- className: 'LineNumber',
8452
- childCount: 1
8453
- }, text(gutterInfo)];
8313
+ const renderNegativeMargin = {
8314
+ isEqual(oldState, newState) {
8315
+ return oldState.deltaY === newState.deltaY;
8316
+ },
8317
+ apply(oldState, newState) {
8318
+ return [/* method */SetNegativeMargin, /* negativeMargin */-newState.deltaY];
8319
+ }
8454
8320
  };
8455
- const getEditorGutterVirtualDom = gutterInfos => {
8456
- const dom = gutterInfos.flatMap(getGutterInfoVirtualDom);
8457
- return dom;
8321
+ const renderScrollBar = {
8322
+ isEqual(oldState, newState) {
8323
+ return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length;
8324
+ },
8325
+ apply(oldState, newState) {
8326
+ const total = newState.items.length;
8327
+ const contentHeight = total * newState.itemHeight;
8328
+ const scrollBarHeight = getScrollBarSize(newState.height, contentHeight, newState.minimumSliderSize);
8329
+ const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
8330
+ return [/* method */SetScrollBar, /* scrollBarY */scrollBarY, /* scrollBarHeight */scrollBarHeight];
8331
+ }
8458
8332
  };
8459
-
8460
- const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true) => {
8461
- const dom = [];
8462
- for (let i = 0; i < textInfos.length; i++) {
8463
- const textInfo = textInfos[i];
8464
- const difference = differences[i];
8465
- dom.push({
8466
- type: Div,
8467
- className: EditorRow,
8468
- translate: px(difference),
8469
- childCount: textInfo.length / 2
8470
- });
8471
- for (let j = 0; j < textInfo.length; j += 2) {
8472
- const tokenText = textInfo[j];
8473
- const className = textInfo[j + 1];
8474
- dom.push({
8475
- type: Span,
8476
- className,
8477
- childCount: 1
8478
- }, text(tokenText));
8333
+ const render$2 = [renderItems, renderBounds, renderHeight, renderNegativeMargin, renderScrollBar];
8334
+ const renderCompletion = (oldState, newState) => {
8335
+ const commands = [];
8336
+ for (const item of render$2) {
8337
+ if (!item.isEqual(oldState, newState)) {
8338
+ commands.push(item.apply(oldState, newState));
8479
8339
  }
8480
8340
  }
8481
- return dom;
8341
+ return commands;
8482
8342
  };
8483
8343
 
8484
- const getIncrementalEdits = async (oldState, newState) => {
8485
- if (!newState.undoStack) {
8486
- return undefined;
8344
+ const render$1 = (oldState, newState) => {
8345
+ const commands = renderCompletion(oldState, newState);
8346
+ const wrappedCommands = [];
8347
+ const uid = newState.uid;
8348
+ for (const command of commands) {
8349
+ wrappedCommands.push(['Viewlet.send', uid, ...command]);
8487
8350
  }
8488
- const lastChanges = newState.undoStack.at(-1);
8489
- if (lastChanges && lastChanges.length === 1) {
8490
- const lastChange = lastChanges[0];
8491
- if (lastChange.origin === EditorType) {
8492
- const rowIndex = lastChange.start.rowIndex;
8493
- const lines = newState.lines;
8494
- const oldLine = oldState.lines[rowIndex];
8495
- const newLine = lines[rowIndex];
8496
- const incrementalEdits = await invoke$1('TokenizeIncremental.tokenizeIncremental', newState.uid, newState.languageId, oldLine, newLine, rowIndex, newState.minLineY);
8497
- if (incrementalEdits && incrementalEdits.length === 1) {
8498
- return incrementalEdits;
8499
- }
8500
- }
8351
+ return wrappedCommands;
8352
+ };
8353
+ const add = widget => {
8354
+ const commands = render$1(widget.oldState, widget.newState);
8355
+ const id = 'EditorCompletion';
8356
+ // TODO how to generate a unique integer id
8357
+ // that doesn't collide with ids created in renderer worker?
8358
+ const uid = widget.newState.uid;
8359
+ const allCommands = [];
8360
+ allCommands.push(['Viewlet.create', id, uid]);
8361
+ allCommands.push(...commands);
8362
+ return allCommands;
8363
+ };
8364
+ const remove = widget => {
8365
+ return [['Viewlet.send', widget.newState.uid, 'dispose']];
8366
+ };
8367
+ const handleEditorType = (editor, state) => {
8368
+ const {
8369
+ unfilteredItems,
8370
+ itemHeight,
8371
+ maxHeight
8372
+ } = state;
8373
+ const {
8374
+ selections
8375
+ } = editor;
8376
+ const rowIndex = selections[0];
8377
+ const columnIndex = selections[1];
8378
+ const x$1 = x(editor, rowIndex, columnIndex);
8379
+ const y$1 = y(editor, rowIndex);
8380
+ const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
8381
+ const items = filterCompletionItems(unfilteredItems, wordAtOffset);
8382
+ const newMinLineY = 0;
8383
+ const newMaxLineY = Math.min(items.length, 8);
8384
+ const height = getListHeight(items.length, itemHeight, maxHeight);
8385
+ const finalDeltaY = items.length * itemHeight - height;
8386
+ return {
8387
+ ...state,
8388
+ items,
8389
+ x: x$1,
8390
+ y: y$1,
8391
+ minLineY: newMinLineY,
8392
+ maxLineY: newMaxLineY,
8393
+ leadingWord: wordAtOffset,
8394
+ height,
8395
+ finalDeltaY
8396
+ };
8397
+ };
8398
+ const handleEditorDeleteLeft = (editor, state) => {
8399
+ const {
8400
+ unfilteredItems,
8401
+ itemHeight,
8402
+ maxHeight
8403
+ } = state;
8404
+ const {
8405
+ selections
8406
+ } = editor;
8407
+ const rowIndex = selections[0];
8408
+ const columnIndex = selections[1];
8409
+ const x$1 = x(editor, rowIndex, columnIndex);
8410
+ const y$1 = y(editor, rowIndex);
8411
+ const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
8412
+ if (!wordAtOffset) {
8413
+ return undefined;
8501
8414
  }
8502
- return undefined;
8415
+ const items = filterCompletionItems(unfilteredItems, wordAtOffset);
8416
+ const newMaxLineY = Math.min(items.length, 8);
8417
+ const height = getListHeight(items.length, itemHeight, maxHeight);
8418
+ return {
8419
+ ...state,
8420
+ items,
8421
+ x: x$1,
8422
+ y: y$1,
8423
+ maxLineY: newMaxLineY,
8424
+ leadingWord: wordAtOffset,
8425
+ height
8426
+ };
8503
8427
  };
8504
8428
 
8505
- const getSelectionsVirtualDom = selections => {
8506
- const dom = [];
8507
- for (let i = 0; i < selections.length; i += 4) {
8508
- const x = selections[i];
8509
- const y = selections[i + 1];
8510
- const width = selections[i + 2];
8511
- const height = selections[i + 3];
8512
- dom.push({
8513
- type: Div,
8514
- className: EditorSelection,
8515
- left: x,
8516
- top: y,
8517
- width,
8518
- height
8519
- });
8520
- }
8521
- return dom;
8429
+ const EditorCompletionWidget = {
8430
+ __proto__: null,
8431
+ add,
8432
+ handleEditorDeleteLeft,
8433
+ handleEditorType,
8434
+ remove,
8435
+ render: render$1
8522
8436
  };
8523
8437
 
8524
8438
  const addWidget = widget => {
@@ -8538,7 +8452,7 @@ const renderWidget = widget => {
8538
8452
  } = widget;
8539
8453
  switch (id) {
8540
8454
  case Completion:
8541
- return render$2(widget.oldState, widget.newState);
8455
+ return render$1(widget.oldState, widget.newState);
8542
8456
  default:
8543
8457
  throw new Error(`unsupported widget`);
8544
8458
  }
@@ -8549,7 +8463,7 @@ const removeWidget = widget => {
8549
8463
  } = widget;
8550
8464
  switch (id) {
8551
8465
  case Completion:
8552
- return remove$1(widget);
8466
+ return remove(widget);
8553
8467
  default:
8554
8468
  throw new Error('unsupported widget');
8555
8469
  }
@@ -8897,8 +8811,8 @@ const commandMap = {
8897
8811
  'EditorCompletion.focusPrevious': focusPrevious$1,
8898
8812
  'EditorCompletion.handleEditorBlur': handleEditorBlur,
8899
8813
  'EditorCompletion.handleEditorClick': handleEditorClick,
8900
- 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft,
8901
- 'EditorCompletion.handleEditorType': handleEditorType,
8814
+ 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft$1,
8815
+ 'EditorCompletion.handleEditorType': handleEditorType$1,
8902
8816
  'EditorCompletion.loadContent': loadContent$2,
8903
8817
  'EditorCompletion.selectCurrent': selectCurrent,
8904
8818
  'EditorCompletion.selectIndex': selectIndex,