@lvce-editor/editor-worker 2.2.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,1096 +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
- // @ts-ignore
250
- const getFontString = (fontWeight, fontSize, fontFamily) => {
251
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
241
+ const modules = Object.create(null);
242
+ const register = (id, value) => {
243
+ modules[id] = value;
252
244
  };
253
-
254
- const getLetterSpacingString = letterSpacing => {
255
- return `${letterSpacing}px`;
245
+ const get$7 = id => {
246
+ return modules[id];
256
247
  };
257
248
 
258
- const state$b = {
259
- ctx: undefined
249
+ const getModule$2 = id => {
250
+ return get$7(id);
260
251
  };
261
252
 
262
- /**
263
- * @param {()=>OffscreenCanvasRenderingContext2D} createCtx
264
- * @returns {OffscreenCanvasRenderingContext2D}
265
- */
266
- const getOrCreate = createCtx => {
267
- if (state$b.ctx) {
268
- return state$b.ctx;
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
+ };
269
261
  }
270
- state$b.ctx = createCtx();
271
- return state$b.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;
272
270
  };
273
271
 
274
- const createCtx = () => {
275
- const canvas = new OffscreenCanvas(0, 0);
276
- const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
277
- if (!ctx) {
278
- throw new Error('Failed to get canvas context 2d');
272
+ const applyWidgetChanges = (editor, changes) => {
273
+ const widgets = editor.widgets || [];
274
+ if (widgets.length === 0) {
275
+ return widgets;
279
276
  }
280
- return 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;
281
285
  };
282
- const getContext = () => {
283
- const ctx = getOrCreate(createCtx);
284
- return ctx;
286
+
287
+ const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
288
+ const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
289
+ return scrollBarOffset;
285
290
  };
291
+ const getScrollBarY = getScrollBarOffset;
286
292
 
287
- const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
288
- string(text);
289
- number$1(fontWeight);
290
- number$1(fontSize);
291
- string(fontFamily);
292
- boolean(isMonoSpaceFont);
293
- number$1(charWidth);
294
- if (isMonoSpaceFont) {
295
- return text.length * charWidth;
296
- }
297
- if (typeof letterSpacing !== 'number') {
298
- throw new TypeError('letterSpacing must be of type number');
293
+ const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
294
+ if (size >= contentSize) {
295
+ return 0;
299
296
  }
300
- const letterSpacingString = getLetterSpacingString(letterSpacing);
301
- const fontString = getFontString(fontWeight, fontSize, fontFamily);
302
- const ctx = getContext();
303
- // @ts-ignore
304
- ctx.letterSpacing = letterSpacingString;
305
- // @ts-ignore
306
- ctx.font = fontString;
307
- // @ts-ignore
308
- const metrics = ctx.measureText(text);
309
- const width = metrics.width;
310
- return width;
297
+ return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
311
298
  };
312
299
 
313
- const getAccurateColumnIndexAscii = (
314
- // @ts-ignore
315
- line,
316
- // @ts-ignore
317
- guess,
318
- // @ts-ignore
319
- averageCharWidth,
320
- // @ts-ignore
321
- eventX,
322
- // @ts-ignore
323
- fontWeight,
324
- // @ts-ignore
325
- fontSize,
326
- // @ts-ignore
327
- fontFamily,
328
- // @ts-ignore
329
- letterSpacing,
330
- // @ts-ignore
331
- isMonospaceFont,
332
- // @ts-ignore
333
- charWidth) => {
334
- for (let i = guess; i < line.length; i++) {
335
- const width = measureTextWidth(line.slice(0, i), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
336
- if (eventX - width < averageCharWidth / 2) {
337
- return i;
338
- }
300
+ const getScrollBarWidth = (width, longestLineWidth) => {
301
+ if (width > longestLineWidth) {
302
+ return 0;
339
303
  }
340
- return line.length;
304
+ return width ** 2 / longestLineWidth;
341
305
  };
342
306
 
343
- const supported = () => {
344
- return 'Segmenter' in Intl;
345
- };
346
- const create$5 = () => {
347
- // @ts-ignore
348
- 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
349
324
  return {
350
- at(line, index) {
351
- const segments = segmenter.segment(line);
352
- return segments.containing(index);
353
- },
354
- visualIndex(line, index) {
355
- const segments = segmenter.segment(line);
356
- let currentVisualIndex = 0;
357
- for (const segment of segments) {
358
- if (segment.index >= index) {
359
- return currentVisualIndex;
360
- }
361
- currentVisualIndex++;
362
- }
363
- return currentVisualIndex;
364
- },
365
- modelIndex(line, visualIndex) {
366
- const segments = segmenter.segment(line);
367
- let currentVisualIndex = 0;
368
- for (const segment of segments) {
369
- if (currentVisualIndex >= visualIndex) {
370
- return segment.index;
371
- }
372
- currentVisualIndex++;
373
- }
374
- return line.length;
375
- },
376
- getSegments(line) {
377
- return segmenter.segment(line);
378
- }
325
+ percent: 1,
326
+ handleOffset: scrollBarHeight - height + relativeY
379
327
  };
380
328
  };
381
329
 
382
- // @ts-ignore
383
- const getAccurateColumnIndexUnicode = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing) => {
384
- const segmenter = create$5();
385
- const segments = segmenter.getSegments(line);
386
- const isMonospaceFont = false;
387
- const charWidth = 0;
388
- for (const segment of segments) {
389
- const width = measureTextWidth(line.slice(0, segment.index), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
390
- if (eventX - width < averageCharWidth) {
391
- return segment.index;
392
- }
330
+ const splitLines$2 = lines => {
331
+ if (!lines) {
332
+ return [];
393
333
  }
394
- return line.length;
395
- };
396
-
397
- const RE_ASCII = /^[\p{ASCII}]*$/u;
398
- const isAscii = line => {
399
- return RE_ASCII.test(line);
334
+ return lines.split('\n');
400
335
  };
401
336
 
402
- const normalizeText = (text, normalize, tabSize) => {
403
- if (normalize) {
404
- return text.replaceAll(Tab, Space.repeat(tabSize));
405
- }
406
- return text;
407
- };
408
- const shouldNormalizeText = text => {
409
- return text.includes(Tab);
410
- };
337
+ // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
411
338
 
412
339
  // @ts-ignore
413
- const guessOffset = (eventX, averageCharWidth) => {
414
- const guess = Math.round(eventX / averageCharWidth);
415
- return guess;
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];
350
+ }
416
351
  };
417
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
+ */
418
357
  // @ts-ignore
419
- const normalizeGuess = (line, guess, tabSize) => {
420
- let normalizedGuess = guess;
421
- for (let i = 0; i < guess; i++) {
422
- if (line[i] === Tab) {
423
- normalizedGuess -= tabSize - 1;
424
- }
425
- }
426
- return normalizedGuess;
358
+ const spliceLargeArray = (array, start, deleteCount, newItems) => {
359
+ const result = array.splice(start, deleteCount);
360
+ insertInto(array, start, newItems);
361
+ return result;
427
362
  };
428
363
 
429
- // @ts-ignore
430
- const getAccurateColumnIndex = (line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX) => {
431
- string(line);
432
- number$1(fontWeight);
433
- number$1(fontSize);
434
- string(fontFamily);
435
- number$1(letterSpacing);
436
- boolean(isMonospaceFont);
437
- number$1(charWidth);
438
- number$1(tabSize);
439
- number$1(eventX);
440
- // Assert.greaterZero(charWidth)
441
- const guess = guessOffset(eventX, charWidth);
442
- const normalize = shouldNormalizeText(line);
443
- const normalizedGuess = normalizeGuess(line, guess, tabSize);
444
- const text = line.slice(0, normalizedGuess);
445
- const normalizedText = normalizeText(text, normalize, tabSize);
446
- const actual = measureTextWidth(normalizedText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
447
- const isAscii$1 = isAscii(line);
448
- if (isAscii$1) {
449
- if (Math.abs(eventX - actual) < charWidth / 2) {
450
- return normalizedGuess;
451
- }
452
- return getAccurateColumnIndexAscii(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
453
- }
454
- return getAccurateColumnIndexUnicode(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing);
455
- };
456
-
457
- // @ts-ignore
458
- const at = (editor, eventX, eventY) => {
459
- object(editor);
460
- number$1(eventX);
461
- number$1(eventY);
462
- const {
463
- y,
464
- deltaY,
465
- rowHeight,
466
- fontSize,
467
- fontWeight,
468
- fontFamily,
469
- letterSpacing,
470
- lines,
471
- tabSize,
472
- differences,
473
- isMonospaceFont,
474
- charWidth
475
- } = editor;
476
- const rowIndex = Math.floor((eventY - y + deltaY) / rowHeight);
477
- if (rowIndex < 0) {
478
- return {
479
- rowIndex: 0,
480
- columnIndex: 0
481
- };
482
- }
483
- const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
484
- const line = lines[clampedRowIndex];
485
- const columnIndex = getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX);
486
- return {
487
- rowIndex: clampedRowIndex,
488
- columnIndex
489
- };
490
- };
491
-
492
- /**
493
- * @deprecated this doesn't work for variable width characters (unicode/emoji).
494
- * Use position computation in renderer process instead
495
- *
496
- * @param {object} editor
497
- * @param {number} rowIndex
498
- * @param {number} columnIndex
499
- * @returns
500
- */
501
- // @ts-ignore
502
- const x = (editor, rowIndex, columnIndex) => {
503
- const {
504
- columnWidth,
505
- x
506
- } = editor;
507
- const offsetX = columnIndex * columnWidth + x;
508
- return offsetX;
509
- };
510
-
511
- // @ts-ignore
512
- const y = (editor, rowIndex) => {
513
- const {
514
- rowHeight,
515
- y
516
- } = editor;
517
- const offsetY = (rowIndex + 1) * rowHeight + y;
518
- return offsetY;
519
- };
520
-
521
- const emptyObject = {};
522
- const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
523
- const i18nString = (key, placeholders = emptyObject) => {
524
- if (placeholders === emptyObject) {
525
- return key;
526
- }
527
- const replacer = (match, rest) => {
528
- return placeholders[rest];
529
- };
530
- return key.replaceAll(RE_PLACEHOLDER, replacer);
531
- };
532
-
533
- /**
534
- * @enum {string}
535
- */
536
- const UiStrings = {
537
- OrganizeImports: 'Organize Imports',
538
- Copy: 'Copy',
539
- CopyLineDown: 'Copy Line Down',
540
- CopyLineUp: 'Copy Line Up',
541
- Cut: 'Cut',
542
- DuplicateSelection: 'Duplicate Selection',
543
- FindAllImplementations: 'Find All Implementations',
544
- FindAllReferences: 'Find All References',
545
- GoToDefinition: 'Go to Definition',
546
- GoToTypeDefinition: 'Go to Type Definition',
547
- MoveLineDown: 'Move Line Down',
548
- MoveLineUp: 'Move Line Up',
549
- NoDefinitionFound: 'No definition found',
550
- NoDefinitionFoundFor: "No definition found for '{PH1}'",
551
- NoTypeDefinitionFound: 'No type definition found',
552
- NoTypeDefinitionFoundFor: "No type definition found for '{PH1}'",
553
- Paste: 'Paste',
554
- Redo: 'Redo',
555
- SelectAll: 'Select All',
556
- Separator: 'Separator',
557
- ToggleBlockComment: 'Toggle Block Comment',
558
- ToggleLineComment: 'Toggle Line Comment',
559
- Undo: 'Undo',
560
- FormatDocument: 'Format Document',
561
- SourceActions: 'Source Actions',
562
- EditorShowHover: 'Show Hover',
563
- EditorFormatDocumentForced: 'Editor: Format Document (forced)',
564
- EditorSelectNextOccurrence: 'Editor: Select Next Occurrence',
565
- EditorSelectAllOccurrences: 'Editor: Select All Occurrences',
566
- EditorGoToDefinition: 'Editor: Go To Definition',
567
- EditorGoToTypeDefinition: 'Editor: Go To Type Definition',
568
- EditorSelectInsideString: 'Editor: Select Inside String',
569
- EditorIndent: 'Editor: Indent',
570
- EditorUnindent: 'Editor: Unindent',
571
- EditorSortLinesAscending: 'Editor: Sort Lines Ascending',
572
- EditorToggleComment: 'Editor: Toggle Comment',
573
- EditorSelectUp: 'Editor: Select Up',
574
- EditorSelectDown: 'Editor: Select Down',
575
- EditorToggleBlockComment: 'Editor: Toggle Block Comment',
576
- EditorOpenColorPicker: 'Editor: Open Color Picker',
577
- EditorCloseColorPicker: 'Editor: Close Color Picker',
578
- EditorCopyLineDown: 'Editor: Copy Line Down',
579
- EditorCopyLineUp: 'Editor: Copy Line Up',
580
- Replace: 'replace',
581
- NoResults: 'No Results'
582
- };
583
- const noDefinitionFound = () => {
584
- return i18nString(UiStrings.NoDefinitionFound);
585
- };
586
-
587
- // @ts-ignore
588
- const noDefinitionFoundFor = word => {
589
- return i18nString(UiStrings.NoDefinitionFoundFor, {
590
- PH1: word
591
- });
592
- };
593
-
594
- // @ts-ignore
595
- const noTypeDefinitionFoundFor = word => {
596
- return i18nString(UiStrings.NoTypeDefinitionFoundFor, {
597
- PH1: word
598
- });
599
- };
600
- const noTypeDefinitionFound = () => {
601
- return i18nString(UiStrings.NoTypeDefinitionFound);
602
- };
603
- const noResults = () => {
604
- return i18nString(UiStrings.NoResults);
605
- };
606
-
607
- const None$1 = 'none';
608
- const Option = 'option';
609
-
610
- const getFileIconVirtualDom = icon => {
611
- return {
612
- type: Img,
613
- className: FileIcon,
614
- src: icon,
615
- role: None$1,
616
- childCount: 0
617
- };
618
- };
619
-
620
- const getIconDom = (fileIcon, symbolName) => {
621
- if (fileIcon) {
622
- return getFileIconVirtualDom(fileIcon);
623
- }
624
- return {
625
- type: Div,
626
- className: `${ColoredMaskIcon} ${symbolName}`,
627
- childCount: 0
628
- };
629
- };
630
-
631
- const text = data => {
632
- return {
633
- type: Text,
634
- text: data,
635
- childCount: 0
636
- };
637
- };
638
-
639
- const label1 = {
640
- type: Div,
641
- className: Label,
642
- childCount: 1
643
- };
644
- const completionHighlight = {
645
- type: Span,
646
- className: EditorCompletionItemHighlight,
647
- childCount: 1
648
- };
649
- const getHighlightedLabelDom = (label, highlights) => {
650
- if (highlights.length === 0) {
651
- return [label1, text(label)];
652
- }
653
- const dom = [];
654
- const labelDom = {
655
- type: Div,
656
- className: Label,
657
- childCount: 0
658
- };
659
- dom.push(labelDom);
660
- let position = 0;
661
- for (let i = 0; i < highlights.length; i += 2) {
662
- const highlightStart = highlights[i];
663
- const highlightEnd = highlights[i + 1];
664
- if (position < highlightStart) {
665
- const beforeText = label.slice(position, highlightStart);
666
- labelDom.childCount++;
667
- dom.push(text(beforeText));
668
- }
669
- const highlightText = label.slice(highlightStart, highlightEnd);
670
- labelDom.childCount++;
671
- dom.push(completionHighlight, text(highlightText));
672
- position = highlightEnd;
673
- }
674
- if (position < label.length) {
675
- const afterText = label.slice(position);
676
- labelDom.childCount++;
677
- dom.push(text(afterText));
678
- }
679
- return dom;
680
- };
681
-
682
- const getCompletionItemVirtualDom = visibleItem => {
683
- const {
684
- top,
685
- label,
686
- symbolName,
687
- highlights,
688
- focused,
689
- deprecated,
690
- fileIcon
691
- } = visibleItem;
692
- let className = EditorCompletionItem;
693
- if (focused) {
694
- className += ' ' + EditorCompletionItemFocused;
695
- }
696
- if (deprecated) {
697
- className += ' ' + EditorCompletionItemDeprecated;
698
- }
699
- return [{
700
- type: Div,
701
- role: Option,
702
- className,
703
- top,
704
- childCount: 2
705
- }, getIconDom(fileIcon, symbolName), ...getHighlightedLabelDom(label, highlights)];
706
- };
707
-
708
- const getCompletionItemsVirtualDom = visibleItems => {
709
- if (visibleItems.length === 0) {
710
- return [{
711
- type: Div,
712
- childCount: 1
713
- }, text(noResults())];
714
- }
715
- const root = {
716
- type: Div,
717
- childCount: visibleItems.length
718
- };
719
- const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
720
- return dom;
721
- };
722
-
723
- const Deprecated = 1 << 0;
724
-
725
- const Property = 1;
726
- const Value = 2;
727
- const Function$1 = 3;
728
- const Variable = 4;
729
- const Keyword = 5;
730
- const Folder = 6;
731
- const File = 7;
732
- const Field = 8;
733
-
734
- const SymbolProperty = 'SymbolProperty';
735
- const SymbolValue = 'SymbolValue';
736
- const SymbolFunction = 'SymbolFunction';
737
- const SymbolVariable = 'SymbolVariable';
738
- const SymbolKeyword = 'SymbolKeyword';
739
- const SymbolDefault = 'SymbolDefault';
740
- const SymbolField = 'SymbolField';
741
- const SymbolNone = '';
742
-
743
- const getSymbolName = item => {
744
- switch (item.kind) {
745
- case Property:
746
- return SymbolProperty;
747
- case Value:
748
- return SymbolValue;
749
- case Function$1:
750
- return SymbolFunction;
751
- case Variable:
752
- return SymbolVariable;
753
- case Keyword:
754
- return SymbolKeyword;
755
- case Field:
756
- return SymbolField;
757
- case File:
758
- return SymbolNone;
759
- default:
760
- return SymbolDefault;
761
- }
762
- };
763
-
764
- const getHighlights = item => {
765
- const {
766
- matches
767
- } = item;
768
- return matches.slice(1);
769
- };
770
-
771
- // import * as IconTheme from '../IconTheme/IconTheme.ts'
772
-
773
- const getLabel = item => {
774
- return item.label;
775
- };
776
- const getFileIcon = item => {
777
- switch (item.kind) {
778
- case File:
779
- // TODO IconTheme.getFileNameIcon(item.label)
780
- return '';
781
- case Folder:
782
- // TODO IconTheme.getFolderNameIcon(item.label)
783
- return '';
784
- default:
785
- return '';
786
- }
787
- };
788
- const getVisibleIem = (item, itemHeight, leadingWord, i, focusedIndex) => {
789
- return {
790
- label: getLabel(item),
791
- symbolName: getSymbolName(item),
792
- top: i * itemHeight,
793
- highlights: getHighlights(item),
794
- focused: i === focusedIndex,
795
- deprecated: item.flags & Deprecated,
796
- fileIcon: getFileIcon(item)
797
- };
798
- };
799
-
800
- const getVisibleItems = (filteredItems, itemHeight, leadingWord, minLineY, maxLineY, focusedIndex) => {
801
- const visibleItems = [];
802
- for (let i = minLineY; i < maxLineY; i++) {
803
- const filteredItem = filteredItems[i];
804
- visibleItems.push(getVisibleIem(filteredItem, itemHeight, leadingWord, i, focusedIndex));
805
- }
806
- return visibleItems;
807
- };
808
-
809
- /**
810
- *
811
- * @param {number} size
812
- * @param {number} contentSize
813
- * @param {number} minimumSliderSize
814
- * @returns
815
- */
816
- const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
817
- if (size >= contentSize) {
818
- return 0;
819
- }
820
- return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
821
- };
822
- const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
823
- const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
824
- return scrollBarOffset;
825
- };
826
- const getScrollBarY = getScrollBarOffset;
827
- const getScrollBarWidth = (width, longestLineWidth) => {
828
- if (width > longestLineWidth) {
829
- return 0;
830
- }
831
- return width ** 2 / longestLineWidth;
832
- };
833
- const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
834
- const halfScrollBarHeight = scrollBarHeight / 2;
835
- if (relativeY <= halfScrollBarHeight) {
836
- // clicked at top
837
- return {
838
- percent: 0,
839
- handleOffset: relativeY
840
- };
841
- }
842
- if (relativeY <= height - halfScrollBarHeight) {
843
- // clicked in middle
844
- return {
845
- percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight),
846
- handleOffset: halfScrollBarHeight
847
- };
848
- }
849
- // clicked at bottom
850
- return {
851
- percent: 1,
852
- handleOffset: scrollBarHeight - height + relativeY
853
- };
854
- };
855
-
856
- const renderItems = {
857
- isEqual(oldState, newState) {
858
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
859
- },
860
- apply(oldState, newState) {
861
- const visibleItems = getVisibleItems(newState.items, newState.itemHeight, newState.leadingWord, newState.minLineY, newState.maxLineY, newState.focusedIndex);
862
- const dom = getCompletionItemsVirtualDom(visibleItems);
863
- return ['setDom', dom];
864
- }
865
- };
866
- const renderBounds$1 = {
867
- isEqual(oldState, newState) {
868
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.x === newState.x && oldState.y === newState.y;
869
- },
870
- apply(oldState, newState) {
871
- const {
872
- x,
873
- y,
874
- width,
875
- height
876
- } = newState;
877
- return [/* method */SetBounds, /* x */x, /* y */y, /* width */width, /* height */height];
878
- }
879
- };
880
- const renderHeight = {
881
- isEqual(oldState, newState) {
882
- return oldState.items.length === newState.items.length;
883
- },
884
- apply(oldState, newState) {
885
- const {
886
- itemHeight
887
- } = newState;
888
- const contentHeight = newState.items.length * itemHeight;
889
- return [/* method */SetContentHeight, /* contentHeight */contentHeight];
890
- }
891
- };
892
- const renderNegativeMargin = {
893
- isEqual(oldState, newState) {
894
- return oldState.deltaY === newState.deltaY;
895
- },
896
- apply(oldState, newState) {
897
- return [/* method */SetNegativeMargin, /* negativeMargin */-newState.deltaY];
898
- }
899
- };
900
- const renderScrollBar = {
901
- isEqual(oldState, newState) {
902
- return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length;
903
- },
904
- apply(oldState, newState) {
905
- const total = newState.items.length;
906
- const contentHeight = total * newState.itemHeight;
907
- const scrollBarHeight = getScrollBarSize(newState.height, contentHeight, newState.minimumSliderSize);
908
- const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
909
- return [/* method */SetScrollBar, /* scrollBarY */scrollBarY, /* scrollBarHeight */scrollBarHeight];
910
- }
911
- };
912
- const render$3 = [renderItems, renderBounds$1, renderHeight, renderNegativeMargin, renderScrollBar];
913
- const renderCompletion = (oldState, newState) => {
914
- const commands = [];
915
- for (const item of render$3) {
916
- if (!item.isEqual(oldState, newState)) {
917
- commands.push(item.apply(oldState, newState));
918
- }
919
- }
920
- return commands;
921
- };
922
-
923
- const EmptyMatches = [];
924
-
925
- const Diagonal = 1;
926
- const Left = 2;
927
-
928
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
929
-
930
- const createTable = size => {
931
- const table = [];
932
- for (let i = 0; i < size; i++) {
933
- const row = new Uint8Array(size);
934
- table.push(row);
935
- }
936
- return table;
937
- };
938
-
939
- const isLowerCase = char => {
940
- return char === char.toLowerCase();
941
- };
942
-
943
- const isUpperCase = char => {
944
- return char === char.toUpperCase();
945
- };
946
-
947
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
948
- const isGap = (columnCharBefore, columnChar) => {
949
- switch (columnCharBefore) {
950
- case Dash:
951
- case Underline:
952
- case EmptyString:
953
- case T:
954
- case Space:
955
- case Dot:
956
- return true;
957
- }
958
- if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
959
- return true;
960
- }
961
- return false;
962
- };
963
-
964
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
965
- const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch) => {
966
- if (rowCharLow !== columnCharLow) {
967
- return -1;
968
- }
969
- const isMatch = rowChar === columnChar;
970
- if (isMatch) {
971
- if (isDiagonalMatch) {
972
- return 8;
973
- }
974
- if (isGap(columnCharBefore, columnChar)) {
975
- return 8;
976
- }
977
- return 5;
978
- }
979
- if (isGap(columnCharBefore, columnChar)) {
980
- return 8;
981
- }
982
- return 5;
983
- };
984
-
985
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
986
-
987
- const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
988
- while (patternPos < patternLen && wordPos < wordLen) {
989
- if (patternLow[patternPos] === wordLow[wordPos]) {
990
- patternPos += 1;
991
- }
992
- wordPos += 1;
993
- }
994
- return patternPos === patternLen; // pattern must be exhausted
995
- };
996
-
997
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
998
- const traceHighlights = (table, arrows, patternLength, wordLength) => {
999
- let row = patternLength;
1000
- let column = wordLength;
1001
- const matches = [];
1002
- while (row >= 1 && column >= 1) {
1003
- const arrow = arrows[row][column];
1004
- if (arrow === Left) {
1005
- column--;
1006
- } else if (arrow === Diagonal) {
1007
- row--;
1008
- column--;
1009
- const start = column + 1;
1010
- while (row >= 1 && column >= 1) {
1011
- const arrow = arrows[row][column];
1012
- if (arrow === Left) {
1013
- break;
1014
- }
1015
- if (arrow === Diagonal) {
1016
- row--;
1017
- column--;
1018
- }
1019
- }
1020
- const end = column;
1021
- matches.unshift(end, start);
1022
- }
1023
- }
1024
- matches.unshift(table[patternLength][wordLength - 1]);
1025
- return matches;
1026
- };
1027
-
1028
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
1029
- const gridSize = 128;
1030
- const table = createTable(gridSize);
1031
- const arrows = createTable(gridSize);
1032
- // @ts-ignore
1033
- createTable(gridSize);
1034
- const filterCompletionItem = (pattern, word) => {
1035
- const patternLength = Math.min(pattern.length, gridSize - 1);
1036
- const wordLength = Math.min(word.length, gridSize - 1);
1037
- const patternLower = pattern.toLowerCase();
1038
- const wordLower = word.toLowerCase();
1039
- if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
1040
- return EmptyMatches;
1041
- }
1042
- let strongMatch = false;
1043
- for (let row = 1; row < patternLength + 1; row++) {
1044
- const rowChar = pattern[row - 1];
1045
- const rowCharLow = patternLower[row - 1];
1046
- for (let column = 1; column < wordLength + 1; column++) {
1047
- const columnChar = word[column - 1];
1048
- const columnCharLow = wordLower[column - 1];
1049
- const columnCharBefore = word[column - 2] || '';
1050
- const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
1051
- const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch);
1052
- if (row === 1 && score > 5) {
1053
- strongMatch = true;
1054
- }
1055
- let diagonalScore = score + table[row - 1][column - 1];
1056
- if (isDiagonalMatch && score !== -1) {
1057
- diagonalScore += 2;
1058
- }
1059
- const leftScore = table[row][column - 1];
1060
- if (leftScore > diagonalScore) {
1061
- table[row][column] = leftScore;
1062
- arrows[row][column] = Left;
1063
- } else {
1064
- table[row][column] = diagonalScore;
1065
- arrows[row][column] = Diagonal;
1066
- }
1067
- }
1068
- }
1069
- if (!strongMatch) {
1070
- return EmptyMatches;
1071
- }
1072
- // printTables(pattern, 0, word, 0)
1073
- const highlights = traceHighlights(table, arrows, patternLength, wordLength);
1074
- return highlights;
1075
- };
1076
-
1077
- const addEmptyMatch = item => {
1078
- return {
1079
- ...item,
1080
- matches: EmptyMatches
1081
- };
1082
- };
1083
- const filterCompletionItems = (completionItems, word) => {
1084
- if (word === EmptyString) {
1085
- return completionItems.map(addEmptyMatch);
1086
- }
1087
- const filteredCompletions = [];
1088
- const deprecated = [];
1089
- for (const completionItem of completionItems) {
1090
- const {
1091
- label,
1092
- flags
1093
- } = completionItem;
1094
- const result = filterCompletionItem(word, label);
1095
- if (result !== EmptyMatches) {
1096
- if (flags & Deprecated) {
1097
- // TODO avoid mutation
1098
- completionItem.matches = EmptyMatches;
1099
- deprecated.push(completionItem);
1100
- } else {
1101
- // TODO avoid mutation
1102
- completionItem.matches = result;
1103
- filteredCompletions.push(completionItem);
1104
- }
1105
- }
1106
- }
1107
- if (deprecated.length > 0) {
1108
- filteredCompletions.push(...deprecated);
1109
- }
1110
- return filteredCompletions;
1111
- };
1112
-
1113
- const getListHeight = (itemsLength, itemHeight, maxHeight) => {
1114
- number$1(itemsLength);
1115
- number$1(itemHeight);
1116
- number$1(maxHeight);
1117
- if (itemsLength === 0) {
1118
- return itemHeight;
1119
- }
1120
- const totalHeight = itemsLength * itemHeight;
1121
- return Math.min(totalHeight, maxHeight);
1122
- };
1123
-
1124
- const RE_WORD_START$1 = /^\w+/;
1125
- const RE_WORD_END$1 = /\w+$/;
1126
- const getWordAt = (editor, rowIndex, columnIndex) => {
1127
- const {
1128
- lines
1129
- } = editor;
1130
- const line = lines[rowIndex];
1131
- const before = line.slice(0, columnIndex);
1132
- const matchBefore = before.match(RE_WORD_END$1);
1133
- const after = line.slice(columnIndex);
1134
- const matchAfter = after.match(RE_WORD_START$1);
1135
- let word = EmptyString;
1136
- if (matchBefore) {
1137
- word += matchBefore[0];
1138
- }
1139
- if (matchAfter) {
1140
- word += matchAfter[0];
1141
- }
1142
- return {
1143
- word
1144
- };
1145
- };
1146
- const getWordBefore = (editor, rowIndex, columnIndex) => {
1147
- const {
1148
- lines
1149
- } = editor;
1150
- const line = lines[rowIndex];
1151
- const before = line.slice(0, columnIndex);
1152
- const matchBefore = before.match(RE_WORD_END$1);
1153
- if (matchBefore) {
1154
- return matchBefore[0];
1155
- }
1156
- return EmptyString;
1157
- };
1158
-
1159
- const render$2 = (oldState, newState) => {
1160
- const commands = renderCompletion(oldState, newState);
1161
- const wrappedCommands = [];
1162
- const uid = newState.uid;
1163
- for (const command of commands) {
1164
- wrappedCommands.push(['Viewlet.send', uid, ...command]);
1165
- }
1166
- return wrappedCommands;
1167
- };
1168
- const add = widget => {
1169
- const commands = render$2(widget.oldState, widget.newState);
1170
- const id = 'EditorCompletion';
1171
- // TODO how to generate a unique integer id
1172
- // that doesn't collide with ids created in renderer worker?
1173
- const uid = widget.newState.uid;
1174
- const allCommands = [];
1175
- allCommands.push(['Viewlet.create', id, uid]);
1176
- allCommands.push(...commands);
1177
- return allCommands;
1178
- };
1179
- const remove$1 = widget => {
1180
- return [['Viewlet.send', widget.newState.uid, 'dispose']];
1181
- };
1182
- const handleEditorType$1 = (editor, state) => {
1183
- const {
1184
- unfilteredItems,
1185
- itemHeight,
1186
- maxHeight
1187
- } = state;
1188
- const {
1189
- selections
1190
- } = editor;
1191
- const rowIndex = selections[0];
1192
- const columnIndex = selections[1];
1193
- const x$1 = x(editor, rowIndex, columnIndex);
1194
- const y$1 = y(editor, rowIndex);
1195
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1196
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1197
- const newMinLineY = 0;
1198
- const newMaxLineY = Math.min(items.length, 8);
1199
- const height = getListHeight(items.length, itemHeight, maxHeight);
1200
- const finalDeltaY = items.length * itemHeight - height;
1201
- return {
1202
- ...state,
1203
- items,
1204
- x: x$1,
1205
- y: y$1,
1206
- minLineY: newMinLineY,
1207
- maxLineY: newMaxLineY,
1208
- leadingWord: wordAtOffset,
1209
- height,
1210
- finalDeltaY
1211
- };
1212
- };
1213
- const handleEditorDeleteLeft$1 = (editor, state) => {
1214
- const {
1215
- unfilteredItems,
1216
- itemHeight,
1217
- maxHeight
1218
- } = state;
1219
- const {
1220
- selections
1221
- } = editor;
1222
- const rowIndex = selections[0];
1223
- const columnIndex = selections[1];
1224
- const x$1 = x(editor, rowIndex, columnIndex);
1225
- const y$1 = y(editor, rowIndex);
1226
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1227
- if (!wordAtOffset) {
1228
- return state;
1229
- }
1230
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1231
- const newMaxLineY = Math.min(items.length, 8);
1232
- const height = getListHeight(items.length, itemHeight, maxHeight);
1233
- return {
1234
- ...state,
1235
- items,
1236
- x: x$1,
1237
- y: y$1,
1238
- maxLineY: newMaxLineY,
1239
- leadingWord: wordAtOffset,
1240
- height
1241
- };
1242
- };
1243
-
1244
- const EditorCompletionWidget = {
1245
- __proto__: null,
1246
- add,
1247
- handleEditorDeleteLeft: handleEditorDeleteLeft$1,
1248
- handleEditorType: handleEditorType$1,
1249
- remove: remove$1,
1250
- render: render$2
1251
- };
1252
-
1253
- const Completion = 'completion';
1254
-
1255
- const getModule$2 = id => {
1256
- switch (id) {
1257
- case Completion:
1258
- return EditorCompletionWidget;
1259
- default:
1260
- throw new Error('unsupported widget');
1261
- }
1262
- };
1263
-
1264
- const applyWidgetChange = (editor, widget, changes) => {
1265
- const module = getModule$2(widget.id);
1266
- if (changes.length === 1 && changes[0].origin === EditorType && module.handleEditorType) {
1267
- const newState = module.handleEditorType(editor, widget.newState);
1268
- return {
1269
- ...widget,
1270
- newState
1271
- };
1272
- }
1273
- if (changes.length === 1 && changes[0].origin === DeleteLeft && module.handleEditorDeleteLeft) {
1274
- const newState = module.handleEditorDeleteLeft(editor, widget.newState);
1275
- return {
1276
- ...widget,
1277
- newState
1278
- };
1279
- }
1280
- return widget;
1281
- };
1282
-
1283
- const applyWidgetChanges = (editor, changes) => {
1284
- const widgets = editor.widgets || [];
1285
- if (widgets.length === 0) {
1286
- return widgets;
1287
- }
1288
- const newWidgets = widgets.map(widget => {
1289
- const newWidget = applyWidgetChange(editor, widget, changes);
1290
- return newWidget;
1291
- });
1292
- return newWidgets;
1293
- };
1294
-
1295
- const splitLines$2 = lines => {
1296
- if (!lines) {
1297
- return [];
1298
- }
1299
- return lines.split('\n');
1300
- };
1301
-
1302
- // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
1303
-
1304
- // @ts-ignore
1305
- const insertInto = (array, start, newItems) => {
1306
- const originalLength = array.length;
1307
- const newItemsLength = newItems.length;
1308
- array.length = originalLength + newItemsLength;
1309
- // Move the items after the start index, start from the end so that we don't overwrite any value.
1310
- for (let i = originalLength - 1; i >= start; i--) {
1311
- array[i + newItemsLength] = array[i];
1312
- }
1313
- for (let i = 0; i < newItemsLength; i++) {
1314
- array[i + start] = newItems[i];
1315
- }
1316
- };
1317
-
1318
- /**
1319
- * Alternative to the native Array.splice method, it
1320
- * can only support limited number of items due to the maximum call stack size limit.
1321
- */
1322
- // @ts-ignore
1323
- const spliceLargeArray = (array, start, deleteCount, newItems) => {
1324
- const result = array.splice(start, deleteCount);
1325
- insertInto(array, start, newItems);
1326
- return result;
1327
- };
1328
-
1329
- const joinLines$2 = lines => {
1330
- return lines.join('\n');
364
+ const joinLines$2 = lines => {
365
+ return lines.join('\n');
1331
366
  };
1332
367
 
1333
368
  // TODO have function for single edit (most common, avoid one array)
@@ -1471,67 +506,145 @@ const positionAt = (textDocument, offset) => {
1471
506
  } else {
1472
507
  columnIndex = currentOffset - offset;
1473
508
  }
1474
- // for (let i = 0; i < textDocument.lines.length; i++) {
1475
- // if(currentOffset)
1476
- // }
1477
- // TODO
1478
- return {
1479
- rowIndex,
1480
- columnIndex
1481
- };
509
+ // for (let i = 0; i < textDocument.lines.length; i++) {
510
+ // if(currentOffset)
511
+ // }
512
+ // TODO
513
+ return {
514
+ rowIndex,
515
+ columnIndex
516
+ };
517
+ };
518
+
519
+ // TODO this should be in a separate scrolling module
520
+ const setDeltaY$2 = (state, value) => {
521
+ object(state);
522
+ number$1(value);
523
+ const {
524
+ finalDeltaY,
525
+ deltaY,
526
+ numberOfVisibleLines,
527
+ height,
528
+ scrollBarHeight,
529
+ itemHeight
530
+ } = state;
531
+ const newDeltaY = clamp(value, 0, finalDeltaY);
532
+ if (deltaY === newDeltaY) {
533
+ return state;
534
+ }
535
+ const newMinLineY = Math.floor(newDeltaY / itemHeight);
536
+ const newMaxLineY = newMinLineY + numberOfVisibleLines;
537
+ const scrollBarY = getScrollBarY(newDeltaY, finalDeltaY, height, scrollBarHeight);
538
+ return {
539
+ ...state,
540
+ minLineY: newMinLineY,
541
+ maxLineY: newMaxLineY,
542
+ deltaY: newDeltaY,
543
+ scrollBarY
544
+ };
545
+ };
546
+
547
+ const getSelectionPairs = (selections, i) => {
548
+ const first = selections[i];
549
+ const second = selections[i + 1];
550
+ const third = selections[i + 2];
551
+ const fourth = selections[i + 3];
552
+ if (first > third || first === third && second >= fourth) {
553
+ return [third, fourth, first, second, 1];
554
+ }
555
+ return [first, second, third, fourth, 0];
556
+ };
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
+
566
+ const getTabCount = string => {
567
+ let count = 0;
568
+ for (const element of string) {
569
+ if (element === Tab) {
570
+ count++;
571
+ }
572
+ }
573
+ return count;
574
+ };
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;
1482
633
  };
1483
634
 
1484
- // TODO this should be in a separate scrolling module
1485
- // @ts-ignore
1486
- const setDeltaY$3 = (state, value) => {
1487
- object(state);
1488
- number$1(value);
1489
- const {
1490
- finalDeltaY,
1491
- deltaY,
1492
- numberOfVisibleLines,
1493
- height,
1494
- scrollBarHeight,
1495
- itemHeight
1496
- } = state;
1497
- const newDeltaY = clamp(value, 0, finalDeltaY);
1498
- if (deltaY === newDeltaY) {
1499
- return state;
635
+ const normalizeText = (text, normalize, tabSize) => {
636
+ if (normalize) {
637
+ return text.replaceAll(Tab, Space.repeat(tabSize));
1500
638
  }
1501
- const newMinLineY = Math.floor(newDeltaY / itemHeight);
1502
- const newMaxLineY = newMinLineY + numberOfVisibleLines;
1503
- const scrollBarY = getScrollBarY(newDeltaY, finalDeltaY, height, scrollBarHeight);
1504
- return {
1505
- ...state,
1506
- minLineY: newMinLineY,
1507
- maxLineY: newMaxLineY,
1508
- deltaY: newDeltaY,
1509
- scrollBarY
1510
- };
639
+ return text;
1511
640
  };
1512
-
1513
- const getSelectionPairs = (selections, i) => {
1514
- const first = selections[i];
1515
- const second = selections[i + 1];
1516
- const third = selections[i + 2];
1517
- const fourth = selections[i + 3];
1518
- if (first > third || first === third && second >= fourth) {
1519
- return [third, fourth, first, second, 1];
1520
- }
1521
- return [first, second, third, fourth, 0];
641
+ const shouldNormalizeText = text => {
642
+ return text.includes(Tab);
1522
643
  };
1523
644
 
1524
645
  // TODO visible selections could also be uint16array
1525
646
  // [top1, left1, width1, height1, top2, left2, width2, height2...]
1526
- const getTabCount = string => {
1527
- let count = 0;
1528
- for (const element of string) {
1529
- if (element === '\t') {
1530
- count++;
1531
- }
1532
- }
1533
- return count;
1534
- };
647
+
1535
648
  const getX = (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, letterSpacing, tabSize, halfCursorWidth, width, averageCharWidth, difference = 0) => {
1536
649
  if (!line) {
1537
650
  return 0;
@@ -1556,6 +669,7 @@ const getX = (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, l
1556
669
  const partialText = normalizedLine.slice(0, column + tabCount);
1557
670
  return measureTextWidth(partialText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, averageCharWidth) - halfCursorWidth + difference;
1558
671
  };
672
+
1559
673
  const getY = (row, minLineY, rowHeight) => {
1560
674
  return (row - minLineY) * rowHeight;
1561
675
  };
@@ -1643,6 +757,9 @@ const push = (selections, startRowIndex, startColumnIndex, endRowIndex, endColum
1643
757
  newSelections[oldLength + 4] = endColumnIndex;
1644
758
  return newSelections;
1645
759
  };
760
+
761
+ // TODO maybe only accept sorted selection edits in the first place
762
+
1646
763
  const emptyCursors = [];
1647
764
  const getCursorArray = (visibleCursors, isFocused) => {
1648
765
  if (!isFocused) {
@@ -1808,9 +925,9 @@ const applyEdit$1 = (editor, changes) => {
1808
925
 
1809
926
  // TODO
1810
927
  const setDeltaYFixedValue$1 = (editor, value) => {
1811
- return setDeltaY$3(editor, value);
928
+ return setDeltaY$2(editor, value);
1812
929
  };
1813
- const setDeltaY$2 = (editor, value) => {
930
+ const setDeltaY$1 = (editor, value) => {
1814
931
  return setDeltaYFixedValue$1(editor, editor.deltaY + value);
1815
932
  };
1816
933
  const isAutoClosingChange = change => {
@@ -2018,34 +1135,6 @@ const set$6 = (id, oldEditor, newEditor) => {
2018
1135
  };
2019
1136
  };
2020
1137
 
2021
- // TODO this should be in a separate scrolling module
2022
- const setDeltaY$1 = (state, value) => {
2023
- object(state);
2024
- number$1(value);
2025
- const {
2026
- finalDeltaY,
2027
- deltaY,
2028
- numberOfVisibleLines,
2029
- height,
2030
- scrollBarHeight,
2031
- itemHeight
2032
- } = state;
2033
- const newDeltaY = clamp(value, 0, finalDeltaY);
2034
- if (deltaY === newDeltaY) {
2035
- return state;
2036
- }
2037
- const newMinLineY = Math.floor(newDeltaY / itemHeight);
2038
- const newMaxLineY = newMinLineY + numberOfVisibleLines;
2039
- const scrollBarY = getScrollBarY(newDeltaY, finalDeltaY, height, scrollBarHeight);
2040
- return {
2041
- ...state,
2042
- minLineY: newMinLineY,
2043
- maxLineY: newMaxLineY,
2044
- deltaY: newDeltaY,
2045
- scrollBarY
2046
- };
2047
- };
2048
-
2049
1138
  const Two = '2.0';
2050
1139
  class AssertionError extends Error {
2051
1140
  constructor(message) {
@@ -2090,7 +1179,7 @@ const set$5 = (id, fn) => {
2090
1179
  const get$5 = id => {
2091
1180
  return state$1$1.callbacks[id];
2092
1181
  };
2093
- const remove = id => {
1182
+ const remove$1 = id => {
2094
1183
  delete state$1$1.callbacks[id];
2095
1184
  };
2096
1185
  const state$a = {
@@ -2136,7 +1225,7 @@ const resolve = (id, args) => {
2136
1225
  return;
2137
1226
  }
2138
1227
  fn(args);
2139
- remove(id);
1228
+ remove$1(id);
2140
1229
  };
2141
1230
  const create$2$1 = (method, params) => {
2142
1231
  const {
@@ -2160,7 +1249,7 @@ class JsonRpcError extends Error {
2160
1249
  this.name = 'JsonRpcError';
2161
1250
  }
2162
1251
  }
2163
- const NewLine$2 = '\n';
1252
+ const NewLine$3 = '\n';
2164
1253
  const DomException = 'DOMException';
2165
1254
  const ReferenceError$1 = 'ReferenceError';
2166
1255
  const SyntaxError$1 = 'SyntaxError';
@@ -2208,20 +1297,20 @@ const constructError = (message, type, name) => {
2208
1297
  return new ErrorConstructor(message);
2209
1298
  };
2210
1299
  const getNewLineIndex$2 = (string, startIndex = undefined) => {
2211
- return string.indexOf(NewLine$2, startIndex);
1300
+ return string.indexOf(NewLine$3, startIndex);
2212
1301
  };
2213
1302
  const joinLines$1 = lines => {
2214
- return lines.join(NewLine$2);
1303
+ return lines.join(NewLine$3);
2215
1304
  };
2216
1305
  const MethodNotFound = -32601;
2217
1306
  const Custom = -32001;
2218
1307
  const splitLines$1 = lines => {
2219
- return lines.split(NewLine$2);
1308
+ return lines.split(NewLine$3);
2220
1309
  };
2221
1310
  const getParentStack = error => {
2222
1311
  let parentStack = error.stack || error.data || error.message || '';
2223
1312
  if (parentStack.startsWith(' at')) {
2224
- parentStack = error.message + NewLine$2 + parentStack;
1313
+ parentStack = error.message + NewLine$3 + parentStack;
2225
1314
  }
2226
1315
  return parentStack;
2227
1316
  };
@@ -2233,14 +1322,14 @@ const restoreJsonRpcError = error => {
2233
1322
  if (error && error.code && error.code === MethodNotFound) {
2234
1323
  const restoredError = new JsonRpcError(error.message);
2235
1324
  const parentStack = getParentStack(error);
2236
- restoredError.stack = parentStack + NewLine$2 + currentStack;
1325
+ restoredError.stack = parentStack + NewLine$3 + currentStack;
2237
1326
  return restoredError;
2238
1327
  }
2239
1328
  if (error && error.message) {
2240
1329
  const restoredError = constructError(error.message, error.type, error.name);
2241
1330
  if (error.data) {
2242
1331
  if (error.data.stack && error.data.type && error.message) {
2243
- restoredError.stack = error.data.type + ': ' + error.message + NewLine$2 + error.data.stack + NewLine$2 + currentStack;
1332
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine$3 + error.data.stack + NewLine$3 + currentStack;
2244
1333
  } else if (error.data.stack) {
2245
1334
  restoredError.stack = error.data.stack;
2246
1335
  }
@@ -2321,7 +1410,7 @@ const getErrorResponse = (message, error, preparePrettyError, logError) => {
2321
1410
  const errorProperty = getErrorProperty(error, prettyError);
2322
1411
  return create$1$1(message, errorProperty);
2323
1412
  };
2324
- const create$4 = (message, result) => {
1413
+ const create$5 = (message, result) => {
2325
1414
  return {
2326
1415
  jsonrpc: Two,
2327
1416
  id: message.id,
@@ -2330,7 +1419,7 @@ const create$4 = (message, result) => {
2330
1419
  };
2331
1420
  const getSuccessResponse = (message, result) => {
2332
1421
  const resultProperty = result ?? null;
2333
- return create$4(message, resultProperty);
1422
+ return create$5(message, resultProperty);
2334
1423
  };
2335
1424
  const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
2336
1425
  try {
@@ -2527,7 +1616,7 @@ const waitForFirstMessage$1 = async port => {
2527
1616
  return event;
2528
1617
  };
2529
1618
 
2530
- const create$3 = async () => {
1619
+ const create$4 = async () => {
2531
1620
  const {
2532
1621
  port1,
2533
1622
  port2
@@ -2572,7 +1661,7 @@ const wrap$3 = port => {
2572
1661
 
2573
1662
  const IpcParentWithExtensionHostWorker = {
2574
1663
  __proto__: null,
2575
- create: create$3,
1664
+ create: create$4,
2576
1665
  wrap: wrap$3
2577
1666
  };
2578
1667
 
@@ -2580,7 +1669,7 @@ const sendMessagePortToSyntaxHighlightingWorker = async port => {
2580
1669
  await invokeAndTransfer([port], 'SendMessagePortToSyntaxHighlightingWorker.sendMessagePortToSyntaxHighlightingWorker', port, 'HandleMessagePort.handleMessagePort');
2581
1670
  };
2582
1671
 
2583
- const create$2 = async () => {
1672
+ const create$3 = async () => {
2584
1673
  const {
2585
1674
  port1,
2586
1675
  port2
@@ -2625,7 +1714,7 @@ const wrap$1 = port => {
2625
1714
 
2626
1715
  const IpcParentWithSyntaxHighlightingWorker = {
2627
1716
  __proto__: null,
2628
- create: create$2,
1717
+ create: create$3,
2629
1718
  wrap: wrap$1
2630
1719
  };
2631
1720
 
@@ -2633,7 +1722,7 @@ const sendMessagePortToRendererProcess = async port => {
2633
1722
  await invokeAndTransfer([port], 'SendMessagePortToRendererProcess.sendMessagePortToRendererProcess', port, 'HandleMessagePort.handleMessagePort');
2634
1723
  };
2635
1724
 
2636
- const create$1 = async () => {
1725
+ const create$2 = async () => {
2637
1726
  const {
2638
1727
  port1,
2639
1728
  port2
@@ -2678,7 +1767,7 @@ const wrap = port => {
2678
1767
 
2679
1768
  const IpcParentWithRendererProcess = {
2680
1769
  __proto__: null,
2681
- create: create$1,
1770
+ create: create$2,
2682
1771
  wrap
2683
1772
  };
2684
1773
 
@@ -2695,7 +1784,7 @@ const getModule$1 = method => {
2695
1784
  }
2696
1785
  };
2697
1786
 
2698
- const create = async ({
1787
+ const create$1 = async ({
2699
1788
  method,
2700
1789
  ...options
2701
1790
  }) => {
@@ -2713,7 +1802,7 @@ const create = async ({
2713
1802
  const createRpc = method => {
2714
1803
  let _ipc;
2715
1804
  const listen = async () => {
2716
- const ipc = await create({
1805
+ const ipc = await create$1({
2717
1806
  method
2718
1807
  });
2719
1808
  handleIpc(ipc);
@@ -2856,7 +1945,7 @@ const createEditor = async ({
2856
1945
  // TODO avoid creating intermediate editors here
2857
1946
  const newEditor1 = setBounds(editor, x, y, width, height, 9);
2858
1947
  const newEditor2 = setText(newEditor1, content);
2859
- const newEditor3 = setDeltaY$1(newEditor2, 0);
1948
+ const newEditor3 = setDeltaY$2(newEditor2, 0);
2860
1949
  const newEditor4 = {
2861
1950
  ...newEditor3,
2862
1951
  focused: true
@@ -2967,14 +2056,188 @@ const replaceRange = (editor, ranges, replacement, origin) => {
2967
2056
  origin
2968
2057
  });
2969
2058
  }
2970
- return changes;
2059
+ return changes;
2060
+ };
2061
+
2062
+ const editorReplaceSelections = (editor, replacement, origin) => {
2063
+ const {
2064
+ selections
2065
+ } = editor;
2066
+ return replaceRange(editor, selections, replacement, origin);
2067
+ };
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
+ };
2971
2215
  };
2972
2216
 
2973
- const editorReplaceSelections = (editor, replacement, origin) => {
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) => {
2974
2227
  const {
2975
- selections
2228
+ columnWidth,
2229
+ x
2976
2230
  } = editor;
2977
- return replaceRange(editor, selections, replacement, origin);
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;
2978
2241
  };
2979
2242
 
2980
2243
  const state$7 = {
@@ -3083,8 +2346,10 @@ const cancelSelection = editor => {
3083
2346
  return scheduleSelections(editor, newSelections);
3084
2347
  };
3085
2348
 
2349
+ const Completion = 'completion';
2350
+
3086
2351
  const isCompletionWidget = widget => {
3087
- return widget.id === 'completion';
2352
+ return widget.id === Completion;
3088
2353
  };
3089
2354
  const closeCompletion = editor => {
3090
2355
  const {
@@ -3156,17 +2421,6 @@ const compositionEnd = (editor, data) => {
3156
2421
  return scheduleDocumentAndCursorsSelections(editor, changes);
3157
2422
  };
3158
2423
 
3159
- /**
3160
- *
3161
- * @param {string} string
3162
- * @param {number|undefined} startIndex
3163
- * @returns
3164
- */
3165
- // @ts-ignore
3166
- const getNewLineIndex$1 = (string, startIndex = undefined) => {
3167
- return string.indexOf('\n', startIndex);
3168
- };
3169
-
3170
2424
  const normalizeLine$1 = line => {
3171
2425
  if (line.startsWith('Error: ')) {
3172
2426
  return line.slice(`Error: `.length);
@@ -3176,7 +2430,17 @@ const normalizeLine$1 = line => {
3176
2430
  }
3177
2431
  return line;
3178
2432
  };
3179
-
2433
+ const getCombinedMessage$1 = (error, message) => {
2434
+ const stringifiedError = normalizeLine$1(`${error}`);
2435
+ if (message) {
2436
+ return `${message}: ${stringifiedError}`;
2437
+ }
2438
+ return stringifiedError;
2439
+ };
2440
+ const NewLine$2 = '\n';
2441
+ const getNewLineIndex$1 = (string, startIndex = undefined) => {
2442
+ return string.indexOf(NewLine$2, startIndex);
2443
+ };
3180
2444
  const mergeStacks$1 = (parent, child) => {
3181
2445
  if (!child) {
3182
2446
  return parent;
@@ -3194,28 +2458,6 @@ const mergeStacks$1 = (parent, child) => {
3194
2458
  }
3195
2459
  return child;
3196
2460
  };
3197
-
3198
- // @ts-nocheck
3199
- const stringifyError = error => {
3200
- if (error instanceof DOMException && error.message) {
3201
- return `DOMException: ${error.message}`;
3202
- }
3203
- const errorPrefixes = ['Error: ', 'VError: '];
3204
- const stringifiedError = `${error}`;
3205
- for (const errorPrefix of errorPrefixes) {
3206
- if (stringifiedError.startsWith(errorPrefix)) {
3207
- return stringifiedError.slice(errorPrefix.length);
3208
- }
3209
- }
3210
- return stringifiedError;
3211
- };
3212
- const getCombinedMessage$1 = (error, message) => {
3213
- const stringifiedError = stringifyError(error);
3214
- if (message) {
3215
- return `${message}: ${stringifiedError}`;
3216
- }
3217
- return `${stringifiedError}`;
3218
- };
3219
2461
  let VError$1 = class VError extends Error {
3220
2462
  constructor(error, message) {
3221
2463
  const combinedMessage = getCombinedMessage$1(error, message);
@@ -3225,12 +2467,16 @@ let VError$1 = class VError extends Error {
3225
2467
  this.stack = mergeStacks$1(this.stack, error.stack);
3226
2468
  }
3227
2469
  if (error.codeFrame) {
2470
+ // @ts-ignore
3228
2471
  this.codeFrame = error.codeFrame;
3229
2472
  }
2473
+ if (error.code) {
2474
+ // @ts-ignore
2475
+ this.code = error.code;
2476
+ }
3230
2477
  }
3231
2478
  };
3232
2479
 
3233
- // @ts-ignore
3234
2480
  const writeText = async text => {
3235
2481
  try {
3236
2482
  string(text);
@@ -3430,7 +2676,7 @@ const characterLeft = (line, columnIndex) => {
3430
2676
  if (!supported()) {
3431
2677
  return 1;
3432
2678
  }
3433
- const segmenter = create$5();
2679
+ const segmenter = create();
3434
2680
  const last = segmenter.at(line, columnIndex - 1);
3435
2681
  return columnIndex - last.index;
3436
2682
  };
@@ -3443,7 +2689,7 @@ const characterRight = (line, columnIndex) => {
3443
2689
  if (!supported()) {
3444
2690
  return 1;
3445
2691
  }
3446
- const segmenter = create$5();
2692
+ const segmenter = create();
3447
2693
  const next = segmenter.at(line, columnIndex);
3448
2694
  return next.segment.length;
3449
2695
  };
@@ -3647,9 +2893,6 @@ const cursorSet = (editor, rowIndex, columnIndex) => {
3647
2893
  return scheduleSelections(editor, selectionEdits);
3648
2894
  };
3649
2895
 
3650
- // @ts-ignore
3651
-
3652
- // @ts-ignore
3653
2896
  const moveSelectionWithoutIntlSegmenter = (selections, i, selectionStartRow, selectionStartColumn, selectionEndRow, selectionEndColumn) => {
3654
2897
  if (selectionStartRow === 0) {
3655
2898
  moveRangeToPosition$1(selections, i, 0, 0);
@@ -3657,13 +2900,9 @@ const moveSelectionWithoutIntlSegmenter = (selections, i, selectionStartRow, sel
3657
2900
  moveRangeToPosition$1(selections, i, selectionStartRow - 1, selectionStartColumn);
3658
2901
  }
3659
2902
  };
3660
-
3661
- // @ts-ignore
3662
2903
  const getNewSelections$8 = selections => {
3663
2904
  return map(selections, moveSelectionWithoutIntlSegmenter);
3664
2905
  };
3665
-
3666
- // @ts-ignore
3667
2906
  const cursorVertical = (editor, getPosition, getEdgePosition, delta) => {
3668
2907
  // if (TextSegmenter.supported()) {
3669
2908
  // return editorCursorsVerticalWithIntlSegmenter(
@@ -3679,7 +2918,6 @@ const cursorVertical = (editor, getPosition, getEdgePosition, delta) => {
3679
2918
  return scheduleSelections(editor, newSelections);
3680
2919
  };
3681
2920
 
3682
- // @ts-ignore
3683
2921
  const cursorUp = editor => {
3684
2922
  return cursorVertical(editor);
3685
2923
  };
@@ -3707,7 +2945,6 @@ const cursorWordRight = editor => {
3707
2945
  return editorCursorHorizontalRight(editor, wordRight);
3708
2946
  };
3709
2947
 
3710
- // @ts-ignore
3711
2948
  const cutLine = async editor => {
3712
2949
  const {
3713
2950
  lines,
@@ -3719,7 +2956,6 @@ const cutLine = async editor => {
3719
2956
  const changes = replaceRange(editor, replaceRange$1, [''], EditorCut);
3720
2957
  const selectionChanges = new Uint32Array([startRowIndex, 0, startRowIndex, 0]);
3721
2958
  await writeText(line);
3722
- // @ts-ignore
3723
2959
  return scheduleDocumentAndCursorsSelections(editor, changes, selectionChanges);
3724
2960
  };
3725
2961
 
@@ -3739,7 +2975,6 @@ const cutSelectedText = async editor => {
3739
2975
  return scheduleDocumentAndCursorsSelections(editor, changes, selectionChanges);
3740
2976
  };
3741
2977
 
3742
- // @ts-ignore
3743
2978
  const cut = editor => {
3744
2979
  const {
3745
2980
  selections
@@ -3999,12 +3234,94 @@ const format = async editor => {
3999
3234
  }
4000
3235
  };
4001
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
+
4002
3279
  // @ts-ignore
4003
3280
  const getDefinition = async (editor, offset) => {
4004
3281
  const definition = await invoke$3('ExtensionHostDefinition.executeDefinitionProvider', editor, offset);
4005
3282
  return definition;
4006
3283
  };
4007
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
+
4008
3325
  // @ts-ignore
4009
3326
  const goTo = async ({
4010
3327
  editor,
@@ -4834,7 +4151,7 @@ const handleTouchEnd = (editor, touchEvent) => {
4834
4151
 
4835
4152
  // @ts-ignore
4836
4153
  const setDeltaY = (editor, deltaY) => {
4837
- return setDeltaY$2(editor, deltaY);
4154
+ return setDeltaY$1(editor, deltaY);
4838
4155
  };
4839
4156
 
4840
4157
  // @ts-ignore
@@ -5210,42 +4527,225 @@ const executeResolveCompletionItem = (editor, offset, name, completionItem) => {
5210
4527
  });
5211
4528
  };
5212
4529
 
5213
- // TODO possible to do this with events/state machine instead of promises -> enables canceling operations / concurrent calls
5214
- const getCompletions = async editor => {
5215
- const {
5216
- selections
5217
- } = editor;
5218
- const rowIndex = selections[0];
5219
- const columnIndex = selections[1];
5220
- // Editor.sync(editor)
5221
- const offset = await offsetAt(editor, rowIndex, columnIndex);
5222
- const completions = await executeCompletionProvider(editor, offset);
5223
- return completions;
4530
+ // TODO possible to do this with events/state machine instead of promises -> enables canceling operations / concurrent calls
4531
+ const getCompletions = async editor => {
4532
+ const {
4533
+ selections
4534
+ } = editor;
4535
+ const rowIndex = selections[0];
4536
+ const columnIndex = selections[1];
4537
+ // Editor.sync(editor)
4538
+ const offset = await offsetAt(editor, rowIndex, columnIndex);
4539
+ const completions = await executeCompletionProvider(editor, offset);
4540
+ return completions;
4541
+ };
4542
+
4543
+ // TODO don't send unnecessary parts of completion item like matches
4544
+ const resolveCompletion = async (editor, name, completionItem) => {
4545
+ try {
4546
+ object(editor);
4547
+ string(name);
4548
+ object(completionItem);
4549
+ const rowIndex = editor.selections[0];
4550
+ const columnIndex = editor.selections[1];
4551
+ const offset = await offsetAt(editor, rowIndex, columnIndex);
4552
+ // @ts-ignore
4553
+ const resolvedCompletionItem = await executeResolveCompletionItem(editor, offset, name, completionItem);
4554
+ return resolvedCompletionItem;
4555
+ } catch {
4556
+ return undefined;
4557
+ }
4558
+ };
4559
+
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;
5224
4665
  };
5225
4666
 
5226
- // TODO don't send unnecessary parts of completion item like matches
5227
- const resolveCompletion = async (editor, name, completionItem) => {
5228
- try {
5229
- object(editor);
5230
- string(name);
5231
- object(completionItem);
5232
- const rowIndex = editor.selections[0];
5233
- const columnIndex = editor.selections[1];
5234
- const offset = await offsetAt(editor, rowIndex, columnIndex);
5235
- // @ts-ignore
5236
- const resolvedCompletionItem = await executeResolveCompletionItem(editor, offset, name, completionItem);
5237
- return resolvedCompletionItem;
5238
- } catch {
5239
- return undefined;
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;
5240
4708
  }
4709
+ const highlights = traceHighlights(table, arrows, patternLength, wordLength);
4710
+ return highlights;
5241
4711
  };
5242
4712
 
5243
- const None = 1;
4713
+ const Deprecated = 1 << 0;
5244
4714
 
5245
- const getFinalDeltaY = (height, itemHeight, itemsLength) => {
5246
- const contentHeight = itemsLength * itemHeight;
5247
- const finalDeltaY = Math.max(contentHeight - height, 0);
5248
- return finalDeltaY;
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;
5249
4749
  };
5250
4750
 
5251
4751
  const getEditor = editorUid => {
@@ -5259,6 +4759,23 @@ const getEditor = editorUid => {
5259
4759
  return newState;
5260
4760
  };
5261
4761
 
4762
+ const getFinalDeltaY = (height, itemHeight, itemsLength) => {
4763
+ const contentHeight = itemsLength * itemHeight;
4764
+ const finalDeltaY = Math.max(contentHeight - height, 0);
4765
+ return finalDeltaY;
4766
+ };
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
+
5262
4779
  const RE_WORD = /[\w\-]+$/;
5263
4780
  const getWordAtOffset = editor => {
5264
4781
  const {
@@ -5275,7 +4792,7 @@ const getWordAtOffset = editor => {
5275
4792
  }
5276
4793
  return '';
5277
4794
  };
5278
- const handleEditorType = (editorUid, state, text) => {
4795
+ const handleEditorType$1 = (editorUid, state, text) => {
5279
4796
  const editor = getEditor(editorUid);
5280
4797
  const {
5281
4798
  unfilteredItems,
@@ -5305,7 +4822,7 @@ const handleEditorType = (editorUid, state, text) => {
5305
4822
  finalDeltaY
5306
4823
  };
5307
4824
  };
5308
- const handleEditorDeleteLeft = (editorUid, state) => {
4825
+ const handleEditorDeleteLeft$1 = (editorUid, state) => {
5309
4826
  const editor = getEditor(editorUid);
5310
4827
  const {
5311
4828
  unfilteredItems,
@@ -5319,7 +4836,7 @@ const handleEditorDeleteLeft = (editorUid, state) => {
5319
4836
  const y$1 = y(editor, rowIndex);
5320
4837
  const wordAtOffset = getWordAtOffset(editor);
5321
4838
  if (!wordAtOffset) {
5322
- editor.completionState = None;
4839
+ editor.completionState = None$1;
5323
4840
  return {
5324
4841
  ...state,
5325
4842
  disposed: true
@@ -5345,7 +4862,7 @@ const dispose = state => {
5345
4862
  };
5346
4863
  };
5347
4864
  const disposeWithEditor = (state, editor) => {
5348
- editor.completionState = None;
4865
+ editor.completionState = None$1;
5349
4866
  editor.completionUid = 0;
5350
4867
  // Focus.removeAdditionalFocus(FocusKey.EditorCompletion)
5351
4868
  return dispose(state);
@@ -5399,14 +4916,26 @@ const advance = (state, word) => {
5399
4916
  };
5400
4917
  };
5401
4918
 
4919
+ const hasWidget = (widgets, id) => {
4920
+ for (const widget of widgets) {
4921
+ if (widget.id === id) {
4922
+ return true;
4923
+ }
4924
+ }
4925
+ return false;
4926
+ };
4927
+
5402
4928
  const openCompletion = async editor => {
5403
4929
  const {
5404
4930
  widgets,
5405
4931
  uid
5406
4932
  } = editor;
4933
+ if (hasWidget(widgets, Completion)) {
4934
+ return editor;
4935
+ }
5407
4936
  const completionUid = Math.random();
5408
4937
  const completionWidget = {
5409
- id: 'completion',
4938
+ id: Completion,
5410
4939
  oldState: {
5411
4940
  items: [],
5412
4941
  itemHeight: 20,
@@ -5675,11 +5204,8 @@ const editorSelectAllLeft = editor => {
5675
5204
  editorSelectHorizontalLeft(editor, lineCharacterStart);
5676
5205
  };
5677
5206
 
5678
- // @ts-ignore
5679
-
5680
5207
  // TODO handle virtual space
5681
5208
 
5682
- // @ts-ignore
5683
5209
  const getNewSelectionsSingleLineWord = (lines, word) => {
5684
5210
  if (word.length === 0) {
5685
5211
  throw new Error('word length must be greater than zero');
@@ -5694,8 +5220,6 @@ const getNewSelectionsSingleLineWord = (lines, word) => {
5694
5220
  }
5695
5221
  return new Uint32Array(newSelections);
5696
5222
  };
5697
-
5698
- // @ts-ignore
5699
5223
  const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5700
5224
  let j = 0;
5701
5225
  if (!lines[rowIndex + j].endsWith(wordParts[j])) {
@@ -5716,11 +5240,11 @@ const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5716
5240
 
5717
5241
  // TODO this is very expensive, there might be a better algorithm for this
5718
5242
  // TODO if this matches the given selections, don't schedule selections/rerender
5719
- // @ts-ignore
5720
5243
  const getAllOccurrencesMultiLine = (lines, wordParts) => {
5721
5244
  const newSelections = [];
5722
5245
  for (let rowIndex = 0; rowIndex < lines.length - wordParts.length + 1; rowIndex) {
5723
5246
  if (isMultiLineMatch(lines, rowIndex, wordParts)) {
5247
+ // @ts-ignore
5724
5248
  newSelections.push(rowIndex, lines[rowIndex].length - wordParts[0].length, rowIndex + wordParts.length - 1, wordParts.at(-1).length);
5725
5249
  rowIndex += wordParts.length - 1;
5726
5250
  } else {
@@ -5732,13 +5256,9 @@ const getAllOccurrencesMultiLine = (lines, wordParts) => {
5732
5256
 
5733
5257
  // TODO duplicate code with EditorSelectNextOccurrence
5734
5258
  const RE_ALPHA_NUMERIC$1 = /[a-zA-Z\d]/;
5735
-
5736
- // @ts-ignore
5737
5259
  const isAlphaNumeric$1 = char => {
5738
5260
  return RE_ALPHA_NUMERIC$1.test(char);
5739
5261
  };
5740
-
5741
- // @ts-ignore
5742
5262
  const getWordStartIndex$1 = (line, index) => {
5743
5263
  for (let i = index - 1; i >= 0; i--) {
5744
5264
  if (!isAlphaNumeric$1(line[i])) {
@@ -5747,8 +5267,6 @@ const getWordStartIndex$1 = (line, index) => {
5747
5267
  }
5748
5268
  return 0;
5749
5269
  };
5750
-
5751
- // @ts-ignore
5752
5270
  const getWordEndIndex$1 = (line, index) => {
5753
5271
  for (let i = index; i < line.length; i++) {
5754
5272
  if (!isAlphaNumeric$1(line[i])) {
@@ -5757,8 +5275,6 @@ const getWordEndIndex$1 = (line, index) => {
5757
5275
  }
5758
5276
  return line.length - 1;
5759
5277
  };
5760
-
5761
- // @ts-ignore
5762
5278
  const getWordMatchAtPosition$1 = (lines, rowIndex, columnIndex) => {
5763
5279
  const line = lines[rowIndex];
5764
5280
  const start = getWordStartIndex$1(line, columnIndex);
@@ -5770,8 +5286,6 @@ const getWordMatchAtPosition$1 = (lines, rowIndex, columnIndex) => {
5770
5286
  word
5771
5287
  };
5772
5288
  };
5773
-
5774
- // @ts-ignore
5775
5289
  const getNewSelections$3 = (lines, selections) => {
5776
5290
  if (selections.length < 4) {
5777
5291
  throw new Error('selections must have at least one entry');
@@ -5804,12 +5318,12 @@ const getNewSelections$3 = (lines, selections) => {
5804
5318
  const newSelections = getAllOccurrencesMultiLine(lines, wordParts);
5805
5319
  return newSelections;
5806
5320
  };
5807
-
5808
- // @ts-ignore
5809
5321
  const selectAllOccurrences = editor => {
5810
5322
  // when there are no selections -> first selection is word -> find all selection that include word
5811
- const lines = editor.lines;
5812
- const selections = editor.selections;
5323
+ const {
5324
+ lines,
5325
+ selections
5326
+ } = editor;
5813
5327
  const newSelections = getNewSelections$3(lines, selections);
5814
5328
  return scheduleSelections(editor, newSelections);
5815
5329
  };
@@ -6377,7 +5891,6 @@ const showSourceActions = async editor => {
6377
5891
  return editor;
6378
5892
  };
6379
5893
 
6380
- // @ts-ignore
6381
5894
  const compareString = (a, b) => {
6382
5895
  return a.localeCompare(b);
6383
5896
  };
@@ -7162,7 +6675,7 @@ const editorUnindent = editor => {
7162
6675
  // editor.lines //?
7163
6676
 
7164
6677
  const isCompletion$2 = widget => {
7165
- return widget.id === 'completion';
6678
+ return widget.id === Completion;
7166
6679
  };
7167
6680
  const getCompletionState = editor => {
7168
6681
  const {
@@ -7173,7 +6686,7 @@ const getCompletionState = editor => {
7173
6686
  };
7174
6687
 
7175
6688
  const isCompletion$1 = widget => {
7176
- return widget.id === 'completion';
6689
+ return widget.id === Completion;
7177
6690
  };
7178
6691
  const focusIndex$1 = (editor, index) => {
7179
6692
  const child = getCompletionState(editor);
@@ -7236,7 +6749,7 @@ const getEdits = async (editor, completionItem) => {
7236
6749
  return changes;
7237
6750
  };
7238
6751
  const isCompletion = widget => {
7239
- return widget.id === 'completion';
6752
+ return widget.id === Completion;
7240
6753
  };
7241
6754
  const select = async (editor, completionItem) => {
7242
6755
  const changes = await getEdits(editor, completionItem);
@@ -7245,7 +6758,7 @@ const select = async (editor, completionItem) => {
7245
6758
  ();
7246
6759
  if (index !== -1) {
7247
6760
  editor.widgets.splice(index, 1);
7248
- editor.completionState = None;
6761
+ editor.completionState = None$1;
7249
6762
  editor.completionUid = 0;
7250
6763
  }
7251
6764
  // TODO dispose completion widget
@@ -7308,7 +6821,7 @@ const getHover = async (editor, offset) => {
7308
6821
 
7309
6822
  let _ipc;
7310
6823
  const listen$5 = async () => {
7311
- const ipc = await create({
6824
+ const ipc = await create$1({
7312
6825
  method: RendererProcess
7313
6826
  });
7314
6827
  handleIpc(ipc);
@@ -7537,6 +7050,14 @@ const handleSashPointerUp = (state, eventX, eventY) => {
7537
7050
  return state;
7538
7051
  };
7539
7052
 
7053
+ const text = data => {
7054
+ return {
7055
+ type: Text,
7056
+ text: data,
7057
+ childCount: 0
7058
+ };
7059
+ };
7060
+
7540
7061
  const getLineInfoVirtualDom = lineInfo => {
7541
7062
  const dom = [{
7542
7063
  type: Div,
@@ -7624,7 +7145,7 @@ const renderHoverDom = {
7624
7145
  return [/* method */'Viewlet.setDom2', dom];
7625
7146
  }
7626
7147
  };
7627
- const renderBounds = {
7148
+ const renderBounds$1 = {
7628
7149
  isEqual(oldState, newState) {
7629
7150
  return oldState.x === newState.x && oldState.y === newState.y && oldState.resizedWidth === newState.resizedWidth;
7630
7151
  },
@@ -7642,10 +7163,10 @@ const renderBounds = {
7642
7163
  return [SetBounds, x, y, resizedWidth, height];
7643
7164
  }
7644
7165
  };
7645
- const render$1 = [renderHoverDom, renderBounds];
7166
+ const render$3 = [renderHoverDom, renderBounds$1];
7646
7167
  const renderHover = async (oldState, newState) => {
7647
7168
  const commands = [];
7648
- for (const item of render$1) {
7169
+ for (const item of render$3) {
7649
7170
  if (!item.isEqual(oldState, newState)) {
7650
7171
  commands.push(item.apply(oldState, newState));
7651
7172
  }
@@ -7951,7 +7472,7 @@ const moveLineUp = editor => {
7951
7472
  };
7952
7473
 
7953
7474
  const Link$1 = 'Link';
7954
- const Function = 'Function';
7475
+ const Function$1 = 'Function';
7955
7476
  const Parameter = 'Parameter';
7956
7477
  const Type = 'Type';
7957
7478
  const VariableName = 'VariableName';
@@ -8010,7 +7531,7 @@ const getDecorationClassName = type => {
8010
7531
  case Ts3073:
8011
7532
  case Ts3077:
8012
7533
  case Ts3088:
8013
- return Function;
7534
+ return Function$1;
8014
7535
  case Ts1792:
8015
7536
  case Ts1793:
8016
7537
  return Parameter;
@@ -8262,14 +7783,86 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize,
8262
7783
  startIndex,
8263
7784
  start,
8264
7785
  end
8265
- } = getStartDefaults(embeddedTokens, minOffset);
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;
7799
+ }
7800
+ }
7801
+ return {
7802
+ lineInfo,
7803
+ difference
7804
+ };
7805
+ };
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);
8266
7842
  const difference = getDifference(start, averageCharWidth, deltaX);
7843
+ const tokensLength = tokens.length;
8267
7844
  for (let i = startIndex; i < tokensLength; i += 2) {
8268
- const tokenType = embeddedTokens[i];
8269
- const tokenLength = embeddedTokens[i + 1];
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
+ }
8270
7863
  end += tokenLength;
8271
- const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
8272
7864
  const text = line.slice(start, end);
7865
+ const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
8273
7866
  const normalizedText = normalizeText(text, normalize, tabSize);
8274
7867
  lineInfo.push(normalizedText, className);
8275
7868
  start = end;
@@ -8282,285 +7875,564 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize,
8282
7875
  difference
8283
7876
  };
8284
7877
  };
8285
- const getOffsets = (deltaX, width, averageCharWidth) => {
8286
- // TODO accurately measure char widths using offscreen canvas
8287
- // and use fast measurements for monospace ascii text
8288
- if (deltaX === 0) {
8289
- return {
8290
- minOffset: 0,
8291
- maxOffset: Math.ceil(width / averageCharWidth)
8292
- };
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);
7890
+ };
7891
+
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 = [];
7896
+ const {
7897
+ lines,
7898
+ decorations,
7899
+ languageId
7900
+ } = editor;
7901
+ const tokenMap = get$1(languageId);
7902
+ let offset = minLineOffset;
7903
+ const tabSize = 2;
7904
+ for (let i = minLineY; i < maxLineY; i++) {
7905
+ const line = lines[i];
7906
+ const normalize = shouldNormalizeText(line);
7907
+ const {
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;
7914
+ }
7915
+ return {
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,
7942
+ tokenizersToLoad,
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
7957
+ };
7958
+ };
7959
+
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
+ });
7968
+ }
7969
+ return dom;
7970
+ };
7971
+
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';
7984
+ }
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
+ };
8005
+
8006
+ const getDiagnosticsVirtualDom = diagnostics => {
8007
+ const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
8008
+ return dom;
8009
+ };
8010
+
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
+ };
8022
+
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));
8042
+ }
8043
+ }
8044
+ return dom;
8045
+ };
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
+ }
8063
+ }
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 => {
8091
+ return {
8092
+ type: Img,
8093
+ className: FileIcon,
8094
+ src: icon,
8095
+ role: None,
8096
+ childCount: 0
8097
+ };
8098
+ };
8099
+
8100
+ const getIconDom = (fileIcon, symbolName) => {
8101
+ if (fileIcon) {
8102
+ return getFileIconVirtualDom(fileIcon);
8293
8103
  }
8294
- const minOffset = Math.ceil(deltaX / averageCharWidth);
8295
- const maxOffset = minOffset + Math.ceil(width / averageCharWidth);
8296
8104
  return {
8297
- minOffset,
8298
- maxOffset
8105
+ type: Div,
8106
+ className: `${ColoredMaskIcon} ${symbolName}`,
8107
+ childCount: 0
8299
8108
  };
8300
8109
  };
8301
- const getDifference = (start, averageCharWidth, deltaX) => {
8302
- const beforeWidth = start * averageCharWidth;
8303
- const difference = beforeWidth - deltaX;
8304
- return difference;
8110
+
8111
+ const label1 = {
8112
+ type: Div,
8113
+ className: Label,
8114
+ childCount: 1
8305
8115
  };
8306
- const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
8307
- const lineInfo = [];
8308
- let decorationIndex = 0;
8309
- for (; decorationIndex < decorations.length; decorationIndex += 3) {
8310
- const decorationOffset = decorations[decorationIndex];
8311
- if (decorationOffset >= lineOffset) {
8312
- break;
8313
- }
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)];
8314
8124
  }
8315
- const tokens = tokenResults.tokens;
8316
- let {
8317
- startIndex,
8318
- start,
8319
- end
8320
- } = getStartDefaults(tokens, minOffset);
8321
- const difference = getDifference(start, averageCharWidth, deltaX);
8322
- const tokensLength = tokens.length;
8323
- for (let i = startIndex; i < tokensLength; i += 2) {
8324
- const tokenType = tokens[i];
8325
- const tokenLength = tokens[i + 1];
8326
- const decorationOffset = decorations[decorationIndex];
8327
- let extraClassName = '';
8328
- if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
8329
- // @ts-ignore
8330
- decorations[++decorationIndex];
8331
- const decorationType = decorations[++decorationIndex];
8332
- // @ts-ignore
8333
- decorations[++decorationIndex];
8334
- // console.log('MATCHING DECORATION', {
8335
- // decorationIndex,
8336
- // decorationLength,
8337
- // decorationType,
8338
- // decorationModifiers,
8339
- // })
8340
- extraClassName = getDecorationClassName(decorationType);
8341
- }
8342
- end += tokenLength;
8343
- const text = line.slice(start, end);
8344
- const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
8345
- const normalizedText = normalizeText(text, normalize, tabSize);
8346
- lineInfo.push(normalizedText, className);
8347
- start = end;
8348
- if (end >= maxOffset) {
8349
- 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));
8350
8140
  }
8141
+ const highlightText = label.slice(highlightStart, highlightEnd);
8142
+ labelDom.childCount++;
8143
+ dom.push(completionHighlight, text(highlightText));
8144
+ position = highlightEnd;
8351
8145
  }
8352
- return {
8353
- lineInfo,
8354
- difference
8355
- };
8146
+ if (position < label.length) {
8147
+ const afterText = label.slice(position);
8148
+ labelDom.childCount++;
8149
+ dom.push(text(afterText));
8150
+ }
8151
+ return dom;
8356
8152
  };
8357
- const getLineInfo = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
8153
+
8154
+ const getCompletionItemVirtualDom = visibleItem => {
8358
8155
  const {
8359
- minOffset,
8360
- maxOffset
8361
- } = getOffsets(deltaX, width, averageCharWidth);
8362
- if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
8363
- const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
8364
- if (embeddedResult && embeddedResult.isFull) {
8365
- return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8366
- }
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;
8367
8167
  }
8368
- 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)];
8369
8178
  };
8370
8179
 
8371
- // TODO need lots of tests for this
8372
- const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
8373
- const result = [];
8374
- const differences = [];
8375
- const {
8376
- lines,
8377
- decorations,
8378
- languageId
8379
- } = editor;
8380
- const tokenMap = get$1(languageId);
8381
- let offset = minLineOffset;
8382
- const tabSize = 2;
8383
- for (let i = minLineY; i < maxLineY; i++) {
8384
- const line = lines[i];
8385
- const normalize = shouldNormalizeText(line);
8386
- const {
8387
- lineInfo,
8388
- difference
8389
- } = getLineInfo(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
8390
- result.push(lineInfo);
8391
- differences.push(difference);
8392
- offset += line.length + 1;
8180
+ const getCompletionItemsVirtualDom = visibleItems => {
8181
+ if (visibleItems.length === 0) {
8182
+ return [{
8183
+ type: Div,
8184
+ childCount: 1
8185
+ }, text(noResults())];
8393
8186
  }
8394
- return {
8395
- result,
8396
- differences
8187
+ const root = {
8188
+ type: Div,
8189
+ childCount: visibleItems.length
8397
8190
  };
8191
+ const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
8192
+ return dom;
8398
8193
  };
8399
- const getVisible = async (editor, syncIncremental) => {
8400
- // console.log({ editor })
8401
- // TODO should separate rendering from business logic somehow
8402
- // currently hard to test because need to mock editor height, top, left,
8403
- // invalidStartIndex, lineCache, etc. just for testing editorType
8404
- // editor.invalidStartIndex = changes[0].start.rowIndex
8405
- // @ts-ignore
8406
- const {
8407
- minLineY,
8408
- numberOfVisibleLines,
8409
- lines,
8410
- width,
8411
- deltaX,
8412
- fontWeight,
8413
- fontSize,
8414
- fontFamily,
8415
- letterSpacing,
8416
- charWidth
8417
- } = editor;
8418
- const maxLineY = Math.min(minLineY + numberOfVisibleLines, lines.length);
8419
- const {
8420
- tokens,
8421
- tokenizersToLoad,
8422
- embeddedResults
8423
- } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
8424
- const minLineOffset = offsetAtSync(editor, minLineY, 0);
8425
- const averageCharWidth = charWidth;
8426
- const {
8427
- result,
8428
- differences
8429
- } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
8430
- if (tokenizersToLoad.length > 0) {
8431
- 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;
8432
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) => {
8433
8257
  return {
8434
- textInfos: result,
8435
- 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)
8436
8265
  };
8437
8266
  };
8438
8267
 
8439
- const getCursorsVirtualDom = cursors => {
8440
- const dom = [];
8441
- for (const translate of cursors) {
8442
- dom.push({
8443
- type: Div,
8444
- className: EditorCursor,
8445
- translate
8446
- });
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));
8447
8273
  }
8448
- return dom;
8274
+ return visibleItems;
8449
8275
  };
8450
8276
 
8451
- const Error$1 = 'error';
8452
- const Warning = 'warning';
8453
-
8454
- const getDiagnosticClassName = type => {
8455
- // TODO use classnames enum
8456
- switch (type) {
8457
- case Error$1:
8458
- return 'DiagnosticError';
8459
- case Warning:
8460
- return 'DiagnosticWarning';
8461
- default:
8462
- 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];
8463
8285
  }
8464
8286
  };
8465
- const getDiagnosticVirtualDom = diagnostic => {
8466
- const {
8467
- x,
8468
- y,
8469
- width,
8470
- height,
8471
- type
8472
- } = diagnostic;
8473
- const extraClassName = getDiagnosticClassName(type);
8474
- return [{
8475
- type: Div,
8476
- className: `${Diagnostic} ${extraClassName}`,
8477
- width,
8478
- height,
8479
- top: y,
8480
- left: x,
8481
- childCount: 0
8482
- }];
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
+ }
8483
8300
  };
8484
-
8485
- const getDiagnosticsVirtualDom = diagnostics => {
8486
- const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
8487
- 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
+ }
8488
8312
  };
8489
-
8490
- const getGutterInfoVirtualDom = gutterInfo => {
8491
- return [{
8492
- type: Span,
8493
- className: 'LineNumber',
8494
- childCount: 1
8495
- }, 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
+ }
8496
8320
  };
8497
- const getEditorGutterVirtualDom = gutterInfos => {
8498
- const dom = gutterInfos.flatMap(getGutterInfoVirtualDom);
8499
- 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
+ }
8500
8332
  };
8501
-
8502
- const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true) => {
8503
- const dom = [];
8504
- for (let i = 0; i < textInfos.length; i++) {
8505
- const textInfo = textInfos[i];
8506
- const difference = differences[i];
8507
- dom.push({
8508
- type: Div,
8509
- className: EditorRow,
8510
- translate: px(difference),
8511
- childCount: textInfo.length / 2
8512
- });
8513
- for (let j = 0; j < textInfo.length; j += 2) {
8514
- const tokenText = textInfo[j];
8515
- const className = textInfo[j + 1];
8516
- dom.push({
8517
- type: Span,
8518
- className,
8519
- childCount: 1
8520
- }, 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));
8521
8339
  }
8522
8340
  }
8523
- return dom;
8341
+ return commands;
8524
8342
  };
8525
8343
 
8526
- const getIncrementalEdits = async (oldState, newState) => {
8527
- if (!newState.undoStack) {
8528
- 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]);
8529
8350
  }
8530
- const lastChanges = newState.undoStack.at(-1);
8531
- if (lastChanges && lastChanges.length === 1) {
8532
- const lastChange = lastChanges[0];
8533
- if (lastChange.origin === EditorType) {
8534
- const rowIndex = lastChange.start.rowIndex;
8535
- const lines = newState.lines;
8536
- const oldLine = oldState.lines[rowIndex];
8537
- const newLine = lines[rowIndex];
8538
- const incrementalEdits = await invoke$1('TokenizeIncremental.tokenizeIncremental', newState.uid, newState.languageId, oldLine, newLine, rowIndex, newState.minLineY);
8539
- if (incrementalEdits && incrementalEdits.length === 1) {
8540
- return incrementalEdits;
8541
- }
8542
- }
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;
8543
8414
  }
8544
- 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
+ };
8545
8427
  };
8546
8428
 
8547
- const getSelectionsVirtualDom = selections => {
8548
- const dom = [];
8549
- for (let i = 0; i < selections.length; i += 4) {
8550
- const x = selections[i];
8551
- const y = selections[i + 1];
8552
- const width = selections[i + 2];
8553
- const height = selections[i + 3];
8554
- dom.push({
8555
- type: Div,
8556
- className: EditorSelection,
8557
- left: x,
8558
- top: y,
8559
- width,
8560
- height
8561
- });
8562
- }
8563
- return dom;
8429
+ const EditorCompletionWidget = {
8430
+ __proto__: null,
8431
+ add,
8432
+ handleEditorDeleteLeft,
8433
+ handleEditorType,
8434
+ remove,
8435
+ render: render$1
8564
8436
  };
8565
8437
 
8566
8438
  const addWidget = widget => {
@@ -8580,7 +8452,7 @@ const renderWidget = widget => {
8580
8452
  } = widget;
8581
8453
  switch (id) {
8582
8454
  case Completion:
8583
- return render$2(widget.oldState, widget.newState);
8455
+ return render$1(widget.oldState, widget.newState);
8584
8456
  default:
8585
8457
  throw new Error(`unsupported widget`);
8586
8458
  }
@@ -8591,7 +8463,7 @@ const removeWidget = widget => {
8591
8463
  } = widget;
8592
8464
  switch (id) {
8593
8465
  case Completion:
8594
- return remove$1(widget);
8466
+ return remove(widget);
8595
8467
  default:
8596
8468
  throw new Error('unsupported widget');
8597
8469
  }
@@ -8939,8 +8811,8 @@ const commandMap = {
8939
8811
  'EditorCompletion.focusPrevious': focusPrevious$1,
8940
8812
  'EditorCompletion.handleEditorBlur': handleEditorBlur,
8941
8813
  'EditorCompletion.handleEditorClick': handleEditorClick,
8942
- 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft,
8943
- 'EditorCompletion.handleEditorType': handleEditorType,
8814
+ 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft$1,
8815
+ 'EditorCompletion.handleEditorType': handleEditorType$1,
8944
8816
  'EditorCompletion.loadContent': loadContent$2,
8945
8817
  'EditorCompletion.selectCurrent': selectCurrent,
8946
8818
  'EditorCompletion.selectIndex': selectIndex,
@@ -9381,6 +9253,7 @@ const listen$1 = async ({
9381
9253
 
9382
9254
  const listen = async () => {
9383
9255
  registerCommands(commandMap);
9256
+ register(Completion, EditorCompletionWidget);
9384
9257
  const ipc = await listen$1({
9385
9258
  method: Auto()
9386
9259
  });