@lvce-editor/editor-worker 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -238,1097 +238,131 @@ const IndentMore = 'indentMore';
238
238
  const InsertLineBreak = 'insertLineBreak';
239
239
  const ToggleBlockComment = 'toggleBlockComment';
240
240
 
241
- const Dash = '-';
242
- const Dot = '.';
243
- const EmptyString = '';
244
- const Space = ' ';
245
- const Tab = '\t';
246
- const Underline = '_';
247
- const T = 't';
248
-
249
- const getFontString = (fontWeight, fontSize, fontFamily) => {
250
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
241
+ const modules = Object.create(null);
242
+ const register = (id, value) => {
243
+ modules[id] = value;
244
+ };
245
+ const get$7 = id => {
246
+ return modules[id];
251
247
  };
252
248
 
253
- const getLetterSpacingString = letterSpacing => {
254
- return `${letterSpacing}px`;
249
+ const getModule$2 = id => {
250
+ return get$7(id);
255
251
  };
256
252
 
257
- const createMeasureContext = () => {
258
- const canvas = new OffscreenCanvas(0, 0);
259
- const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
260
- if (!ctx) {
261
- throw new Error('Failed to get canvas context 2d');
253
+ const applyWidgetChange = (editor, widget, changes) => {
254
+ const module = getModule$2(widget.id);
255
+ if (changes.length === 1 && changes[0].origin === EditorType && module.handleEditorType) {
256
+ const newState = module.handleEditorType(editor, widget.newState);
257
+ return {
258
+ ...widget,
259
+ newState
260
+ };
262
261
  }
263
- return ctx;
262
+ if (changes.length === 1 && changes[0].origin === DeleteLeft && module.handleEditorDeleteLeft) {
263
+ const newState = module.handleEditorDeleteLeft(editor, widget.newState);
264
+ return {
265
+ ...widget,
266
+ newState
267
+ };
268
+ }
269
+ return widget;
264
270
  };
265
271
 
266
- const state$b = {
267
- ctx: undefined
268
- };
269
- const getOrCreate = createCtx => {
270
- if (state$b.ctx) {
271
- return state$b.ctx;
272
+ const applyWidgetChanges = (editor, changes) => {
273
+ const widgets = editor.widgets || [];
274
+ if (widgets.length === 0) {
275
+ return widgets;
272
276
  }
273
- state$b.ctx = createCtx();
274
- return state$b.ctx;
277
+ const newWidgets = [];
278
+ for (const widget of widgets) {
279
+ const newWidget = applyWidgetChange(editor, widget, changes);
280
+ if (newWidget.newState) {
281
+ newWidgets.push(newWidget);
282
+ }
283
+ }
284
+ return newWidgets;
275
285
  };
276
286
 
277
- const getContext = () => {
278
- const ctx = getOrCreate(createMeasureContext);
279
- return ctx;
287
+ const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
288
+ const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
289
+ return scrollBarOffset;
280
290
  };
291
+ const getScrollBarY = getScrollBarOffset;
281
292
 
282
- const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
283
- string(text);
284
- number$1(fontWeight);
285
- number$1(fontSize);
286
- string(fontFamily);
287
- boolean(isMonoSpaceFont);
288
- number$1(charWidth);
289
- if (isMonoSpaceFont) {
290
- return text.length * charWidth;
291
- }
292
- if (typeof letterSpacing !== 'number') {
293
- throw new TypeError('letterSpacing must be of type number');
293
+ const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
294
+ if (size >= contentSize) {
295
+ return 0;
294
296
  }
295
- const letterSpacingString = getLetterSpacingString(letterSpacing);
296
- const fontString = getFontString(fontWeight, fontSize, fontFamily);
297
- const ctx = getContext();
298
- // @ts-ignore
299
- ctx.letterSpacing = letterSpacingString;
300
- // @ts-ignore
301
- ctx.font = fontString;
302
- // @ts-ignore
303
- const metrics = ctx.measureText(text);
304
- const width = metrics.width;
305
- return width;
297
+ return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
306
298
  };
307
299
 
308
- const getAccurateColumnIndexAscii = (
309
- // @ts-ignore
310
- line,
311
- // @ts-ignore
312
- guess,
313
- // @ts-ignore
314
- averageCharWidth,
315
- // @ts-ignore
316
- eventX,
317
- // @ts-ignore
318
- fontWeight,
319
- // @ts-ignore
320
- fontSize,
321
- // @ts-ignore
322
- fontFamily,
323
- // @ts-ignore
324
- letterSpacing,
325
- // @ts-ignore
326
- isMonospaceFont,
327
- // @ts-ignore
328
- charWidth) => {
329
- for (let i = guess; i < line.length; i++) {
330
- const width = measureTextWidth(line.slice(0, i), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
331
- if (eventX - width < averageCharWidth / 2) {
332
- return i;
333
- }
300
+ const getScrollBarWidth = (width, longestLineWidth) => {
301
+ if (width > longestLineWidth) {
302
+ return 0;
334
303
  }
335
- return line.length;
304
+ return width ** 2 / longestLineWidth;
336
305
  };
337
306
 
338
- const supported = () => {
339
- return 'Segmenter' in Intl;
340
- };
341
- const create$5 = () => {
342
- // @ts-ignore
343
- const segmenter = new Intl.Segmenter();
307
+ const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
308
+ const halfScrollBarHeight = scrollBarHeight / 2;
309
+ if (relativeY <= halfScrollBarHeight) {
310
+ // clicked at top
311
+ return {
312
+ percent: 0,
313
+ handleOffset: relativeY
314
+ };
315
+ }
316
+ if (relativeY <= height - halfScrollBarHeight) {
317
+ // clicked in middle
318
+ return {
319
+ percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight),
320
+ handleOffset: halfScrollBarHeight
321
+ };
322
+ }
323
+ // clicked at bottom
344
324
  return {
345
- at(line, index) {
346
- const segments = segmenter.segment(line);
347
- return segments.containing(index);
348
- },
349
- visualIndex(line, index) {
350
- const segments = segmenter.segment(line);
351
- let currentVisualIndex = 0;
352
- for (const segment of segments) {
353
- if (segment.index >= index) {
354
- return currentVisualIndex;
355
- }
356
- currentVisualIndex++;
357
- }
358
- return currentVisualIndex;
359
- },
360
- modelIndex(line, visualIndex) {
361
- const segments = segmenter.segment(line);
362
- let currentVisualIndex = 0;
363
- for (const segment of segments) {
364
- if (currentVisualIndex >= visualIndex) {
365
- return segment.index;
366
- }
367
- currentVisualIndex++;
368
- }
369
- return line.length;
370
- },
371
- getSegments(line) {
372
- return segmenter.segment(line);
373
- }
325
+ percent: 1,
326
+ handleOffset: scrollBarHeight - height + relativeY
374
327
  };
375
328
  };
376
329
 
377
- // @ts-ignore
378
- const getAccurateColumnIndexUnicode = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing) => {
379
- const segmenter = create$5();
380
- const segments = segmenter.getSegments(line);
381
- const isMonospaceFont = false;
382
- const charWidth = 0;
383
- for (const segment of segments) {
384
- const width = measureTextWidth(line.slice(0, segment.index), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
385
- if (eventX - width < averageCharWidth) {
386
- return segment.index;
387
- }
330
+ const splitLines$2 = lines => {
331
+ if (!lines) {
332
+ return [];
388
333
  }
389
- return line.length;
334
+ return lines.split('\n');
390
335
  };
391
336
 
392
- const RE_ASCII = /^[\p{ASCII}]*$/u;
393
- const isAscii = line => {
394
- return RE_ASCII.test(line);
395
- };
337
+ // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
396
338
 
397
- const normalizeText = (text, normalize, tabSize) => {
398
- if (normalize) {
399
- return text.replaceAll(Tab, Space.repeat(tabSize));
339
+ // @ts-ignore
340
+ const insertInto = (array, start, newItems) => {
341
+ const originalLength = array.length;
342
+ const newItemsLength = newItems.length;
343
+ array.length = originalLength + newItemsLength;
344
+ // Move the items after the start index, start from the end so that we don't overwrite any value.
345
+ for (let i = originalLength - 1; i >= start; i--) {
346
+ array[i + newItemsLength] = array[i];
347
+ }
348
+ for (let i = 0; i < newItemsLength; i++) {
349
+ array[i + start] = newItems[i];
400
350
  }
401
- return text;
402
- };
403
- const shouldNormalizeText = text => {
404
- return text.includes(Tab);
405
351
  };
406
352
 
353
+ /**
354
+ * Alternative to the native Array.splice method, it
355
+ * can only support limited number of items due to the maximum call stack size limit.
356
+ */
407
357
  // @ts-ignore
408
- const guessOffset = (eventX, averageCharWidth) => {
409
- const guess = Math.round(eventX / averageCharWidth);
410
- return guess;
358
+ const spliceLargeArray = (array, start, deleteCount, newItems) => {
359
+ const result = array.splice(start, deleteCount);
360
+ insertInto(array, start, newItems);
361
+ return result;
411
362
  };
412
363
 
413
- // @ts-ignore
414
- const normalizeGuess = (line, guess, tabSize) => {
415
- let normalizedGuess = guess;
416
- for (let i = 0; i < guess; i++) {
417
- if (line[i] === Tab) {
418
- normalizedGuess -= tabSize - 1;
419
- }
420
- }
421
- return normalizedGuess;
422
- };
423
-
424
- // @ts-ignore
425
- const getAccurateColumnIndex = (line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX) => {
426
- string(line);
427
- number$1(fontWeight);
428
- number$1(fontSize);
429
- string(fontFamily);
430
- number$1(letterSpacing);
431
- boolean(isMonospaceFont);
432
- number$1(charWidth);
433
- number$1(tabSize);
434
- number$1(eventX);
435
- // Assert.greaterZero(charWidth)
436
- const guess = guessOffset(eventX, charWidth);
437
- const normalize = shouldNormalizeText(line);
438
- const normalizedGuess = normalizeGuess(line, guess, tabSize);
439
- const text = line.slice(0, normalizedGuess);
440
- const normalizedText = normalizeText(text, normalize, tabSize);
441
- const actual = measureTextWidth(normalizedText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
442
- const isAscii$1 = isAscii(line);
443
- if (isAscii$1) {
444
- if (Math.abs(eventX - actual) < charWidth / 2) {
445
- return normalizedGuess;
446
- }
447
- return getAccurateColumnIndexAscii(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
448
- }
449
- return getAccurateColumnIndexUnicode(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing);
450
- };
451
-
452
- const at = (editor, eventX, eventY) => {
453
- object(editor);
454
- number$1(eventX);
455
- number$1(eventY);
456
- const {
457
- y,
458
- deltaY,
459
- rowHeight,
460
- fontSize,
461
- fontWeight,
462
- fontFamily,
463
- letterSpacing,
464
- lines,
465
- tabSize,
466
- differences,
467
- isMonospaceFont,
468
- charWidth
469
- } = editor;
470
- const rowIndex = Math.floor((eventY - y + deltaY) / rowHeight);
471
- if (rowIndex < 0) {
472
- return {
473
- rowIndex: 0,
474
- columnIndex: 0
475
- };
476
- }
477
- const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
478
- const line = lines[clampedRowIndex];
479
- const columnIndex = getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX);
480
- return {
481
- rowIndex: clampedRowIndex,
482
- columnIndex
483
- };
484
- };
485
-
486
- /**
487
- * @deprecated this doesn't work for variable width characters (unicode/emoji).
488
- * Use position computation in renderer process instead
489
- *
490
- * @param {object} editor
491
- * @param {number} rowIndex
492
- * @param {number} columnIndex
493
- * @returns
494
- */
495
- const x = (editor, rowIndex, columnIndex) => {
496
- const {
497
- columnWidth,
498
- x
499
- } = editor;
500
- const offsetX = columnIndex * columnWidth + x;
501
- return offsetX;
502
- };
503
- const y = (editor, rowIndex) => {
504
- const {
505
- rowHeight,
506
- y
507
- } = editor;
508
- const offsetY = (rowIndex + 1) * rowHeight + y;
509
- return offsetY;
510
- };
511
-
512
- const emptyObject = {};
513
- const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
514
- const i18nString = (key, placeholders = emptyObject) => {
515
- if (placeholders === emptyObject) {
516
- return key;
517
- }
518
- const replacer = (match, rest) => {
519
- return placeholders[rest];
520
- };
521
- return key.replaceAll(RE_PLACEHOLDER, replacer);
522
- };
523
-
524
- /**
525
- * @enum {string}
526
- */
527
- const UiStrings = {
528
- OrganizeImports: 'Organize Imports',
529
- Copy: 'Copy',
530
- CopyLineDown: 'Copy Line Down',
531
- CopyLineUp: 'Copy Line Up',
532
- Cut: 'Cut',
533
- DuplicateSelection: 'Duplicate Selection',
534
- FindAllImplementations: 'Find All Implementations',
535
- FindAllReferences: 'Find All References',
536
- GoToDefinition: 'Go to Definition',
537
- GoToTypeDefinition: 'Go to Type Definition',
538
- MoveLineDown: 'Move Line Down',
539
- MoveLineUp: 'Move Line Up',
540
- NoDefinitionFound: 'No definition found',
541
- NoDefinitionFoundFor: "No definition found for '{PH1}'",
542
- NoTypeDefinitionFound: 'No type definition found',
543
- NoTypeDefinitionFoundFor: "No type definition found for '{PH1}'",
544
- Paste: 'Paste',
545
- Redo: 'Redo',
546
- SelectAll: 'Select All',
547
- Separator: 'Separator',
548
- ToggleBlockComment: 'Toggle Block Comment',
549
- ToggleLineComment: 'Toggle Line Comment',
550
- Undo: 'Undo',
551
- FormatDocument: 'Format Document',
552
- SourceActions: 'Source Actions',
553
- EditorShowHover: 'Show Hover',
554
- EditorFormatDocumentForced: 'Editor: Format Document (forced)',
555
- EditorSelectNextOccurrence: 'Editor: Select Next Occurrence',
556
- EditorSelectAllOccurrences: 'Editor: Select All Occurrences',
557
- EditorGoToDefinition: 'Editor: Go To Definition',
558
- EditorGoToTypeDefinition: 'Editor: Go To Type Definition',
559
- EditorSelectInsideString: 'Editor: Select Inside String',
560
- EditorIndent: 'Editor: Indent',
561
- EditorUnindent: 'Editor: Unindent',
562
- EditorSortLinesAscending: 'Editor: Sort Lines Ascending',
563
- EditorToggleComment: 'Editor: Toggle Comment',
564
- EditorSelectUp: 'Editor: Select Up',
565
- EditorSelectDown: 'Editor: Select Down',
566
- EditorToggleBlockComment: 'Editor: Toggle Block Comment',
567
- EditorOpenColorPicker: 'Editor: Open Color Picker',
568
- EditorCloseColorPicker: 'Editor: Close Color Picker',
569
- EditorCopyLineDown: 'Editor: Copy Line Down',
570
- EditorCopyLineUp: 'Editor: Copy Line Up',
571
- Replace: 'replace',
572
- NoResults: 'No Results'
573
- };
574
- const noDefinitionFound = () => {
575
- return i18nString(UiStrings.NoDefinitionFound);
576
- };
577
-
578
- // @ts-ignore
579
- const noDefinitionFoundFor = word => {
580
- return i18nString(UiStrings.NoDefinitionFoundFor, {
581
- PH1: word
582
- });
583
- };
584
-
585
- // @ts-ignore
586
- const noTypeDefinitionFoundFor = word => {
587
- return i18nString(UiStrings.NoTypeDefinitionFoundFor, {
588
- PH1: word
589
- });
590
- };
591
- const noTypeDefinitionFound = () => {
592
- return i18nString(UiStrings.NoTypeDefinitionFound);
593
- };
594
- const noResults = () => {
595
- return i18nString(UiStrings.NoResults);
596
- };
597
-
598
- const None$1 = 'none';
599
- const Option = 'option';
600
-
601
- const getFileIconVirtualDom = icon => {
602
- return {
603
- type: Img,
604
- className: FileIcon,
605
- src: icon,
606
- role: None$1,
607
- childCount: 0
608
- };
609
- };
610
-
611
- const getIconDom = (fileIcon, symbolName) => {
612
- if (fileIcon) {
613
- return getFileIconVirtualDom(fileIcon);
614
- }
615
- return {
616
- type: Div,
617
- className: `${ColoredMaskIcon} ${symbolName}`,
618
- childCount: 0
619
- };
620
- };
621
-
622
- const text = data => {
623
- return {
624
- type: Text,
625
- text: data,
626
- childCount: 0
627
- };
628
- };
629
-
630
- const label1 = {
631
- type: Div,
632
- className: Label,
633
- childCount: 1
634
- };
635
- const completionHighlight = {
636
- type: Span,
637
- className: EditorCompletionItemHighlight,
638
- childCount: 1
639
- };
640
- const getHighlightedLabelDom = (label, highlights) => {
641
- if (highlights.length === 0) {
642
- return [label1, text(label)];
643
- }
644
- const dom = [];
645
- const labelDom = {
646
- type: Div,
647
- className: Label,
648
- childCount: 0
649
- };
650
- dom.push(labelDom);
651
- let position = 0;
652
- for (let i = 0; i < highlights.length; i += 2) {
653
- const highlightStart = highlights[i];
654
- const highlightEnd = highlights[i + 1];
655
- if (position < highlightStart) {
656
- const beforeText = label.slice(position, highlightStart);
657
- labelDom.childCount++;
658
- dom.push(text(beforeText));
659
- }
660
- const highlightText = label.slice(highlightStart, highlightEnd);
661
- labelDom.childCount++;
662
- dom.push(completionHighlight, text(highlightText));
663
- position = highlightEnd;
664
- }
665
- if (position < label.length) {
666
- const afterText = label.slice(position);
667
- labelDom.childCount++;
668
- dom.push(text(afterText));
669
- }
670
- return dom;
671
- };
672
-
673
- const getCompletionItemVirtualDom = visibleItem => {
674
- const {
675
- top,
676
- label,
677
- symbolName,
678
- highlights,
679
- focused,
680
- deprecated,
681
- fileIcon
682
- } = visibleItem;
683
- let className = EditorCompletionItem;
684
- if (focused) {
685
- className += ' ' + EditorCompletionItemFocused;
686
- }
687
- if (deprecated) {
688
- className += ' ' + EditorCompletionItemDeprecated;
689
- }
690
- return [{
691
- type: Div,
692
- role: Option,
693
- className,
694
- top,
695
- childCount: 2
696
- }, getIconDom(fileIcon, symbolName), ...getHighlightedLabelDom(label, highlights)];
697
- };
698
-
699
- const getCompletionItemsVirtualDom = visibleItems => {
700
- if (visibleItems.length === 0) {
701
- return [{
702
- type: Div,
703
- childCount: 1
704
- }, text(noResults())];
705
- }
706
- const root = {
707
- type: Div,
708
- childCount: visibleItems.length
709
- };
710
- const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
711
- return dom;
712
- };
713
-
714
- const Deprecated = 1 << 0;
715
-
716
- const Property = 1;
717
- const Value = 2;
718
- const Function$1 = 3;
719
- const Variable = 4;
720
- const Keyword = 5;
721
- const Folder = 6;
722
- const File = 7;
723
- const Field = 8;
724
-
725
- const SymbolProperty = 'SymbolProperty';
726
- const SymbolValue = 'SymbolValue';
727
- const SymbolFunction = 'SymbolFunction';
728
- const SymbolVariable = 'SymbolVariable';
729
- const SymbolKeyword = 'SymbolKeyword';
730
- const SymbolDefault = 'SymbolDefault';
731
- const SymbolField = 'SymbolField';
732
- const SymbolNone = '';
733
-
734
- const getSymbolName = kind => {
735
- switch (kind) {
736
- case Property:
737
- return SymbolProperty;
738
- case Value:
739
- return SymbolValue;
740
- case Function$1:
741
- return SymbolFunction;
742
- case Variable:
743
- return SymbolVariable;
744
- case Keyword:
745
- return SymbolKeyword;
746
- case Field:
747
- return SymbolField;
748
- case File:
749
- return SymbolNone;
750
- default:
751
- return SymbolDefault;
752
- }
753
- };
754
-
755
- const getHighlights = item => {
756
- const {
757
- matches
758
- } = item;
759
- return matches.slice(1);
760
- };
761
-
762
- // import * as IconTheme from '../IconTheme/IconTheme.ts'
763
-
764
- const getLabel = item => {
765
- return item.label;
766
- };
767
- const getFileIcon = item => {
768
- switch (item.kind) {
769
- case File:
770
- // TODO IconTheme.getFileNameIcon(item.label)
771
- return '';
772
- case Folder:
773
- // TODO IconTheme.getFolderNameIcon(item.label)
774
- return '';
775
- default:
776
- return '';
777
- }
778
- };
779
- const getVisibleIem = (item, itemHeight, leadingWord, i, focusedIndex) => {
780
- return {
781
- label: getLabel(item),
782
- symbolName: getSymbolName(item.kind),
783
- top: i * itemHeight,
784
- highlights: getHighlights(item),
785
- focused: i === focusedIndex,
786
- deprecated: item.flags & Deprecated,
787
- fileIcon: getFileIcon(item)
788
- };
789
- };
790
-
791
- const getVisibleItems = (filteredItems, itemHeight, leadingWord, minLineY, maxLineY, focusedIndex) => {
792
- const visibleItems = [];
793
- for (let i = minLineY; i < maxLineY; i++) {
794
- const filteredItem = filteredItems[i];
795
- visibleItems.push(getVisibleIem(filteredItem, itemHeight, leadingWord, i, focusedIndex));
796
- }
797
- return visibleItems;
798
- };
799
-
800
- /**
801
- *
802
- * @param {number} size
803
- * @param {number} contentSize
804
- * @param {number} minimumSliderSize
805
- * @returns
806
- */
807
- const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
808
- if (size >= contentSize) {
809
- return 0;
810
- }
811
- return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
812
- };
813
- const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
814
- const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
815
- return scrollBarOffset;
816
- };
817
- const getScrollBarY = getScrollBarOffset;
818
- const getScrollBarWidth = (width, longestLineWidth) => {
819
- if (width > longestLineWidth) {
820
- return 0;
821
- }
822
- return width ** 2 / longestLineWidth;
823
- };
824
- const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
825
- const halfScrollBarHeight = scrollBarHeight / 2;
826
- if (relativeY <= halfScrollBarHeight) {
827
- // clicked at top
828
- return {
829
- percent: 0,
830
- handleOffset: relativeY
831
- };
832
- }
833
- if (relativeY <= height - halfScrollBarHeight) {
834
- // clicked in middle
835
- return {
836
- percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight),
837
- handleOffset: halfScrollBarHeight
838
- };
839
- }
840
- // clicked at bottom
841
- return {
842
- percent: 1,
843
- handleOffset: scrollBarHeight - height + relativeY
844
- };
845
- };
846
-
847
- const renderItems = {
848
- isEqual(oldState, newState) {
849
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
850
- },
851
- apply(oldState, newState) {
852
- const visibleItems = getVisibleItems(newState.items, newState.itemHeight, newState.leadingWord, newState.minLineY, newState.maxLineY, newState.focusedIndex);
853
- const dom = getCompletionItemsVirtualDom(visibleItems);
854
- return ['setDom', dom];
855
- }
856
- };
857
- const renderBounds$1 = {
858
- isEqual(oldState, newState) {
859
- return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.x === newState.x && oldState.y === newState.y;
860
- },
861
- apply(oldState, newState) {
862
- const {
863
- x,
864
- y,
865
- width,
866
- height
867
- } = newState;
868
- return [/* method */SetBounds, /* x */x, /* y */y, /* width */width, /* height */height];
869
- }
870
- };
871
- const renderHeight = {
872
- isEqual(oldState, newState) {
873
- return oldState.items.length === newState.items.length;
874
- },
875
- apply(oldState, newState) {
876
- const {
877
- itemHeight
878
- } = newState;
879
- const contentHeight = newState.items.length * itemHeight;
880
- return [/* method */SetContentHeight, /* contentHeight */contentHeight];
881
- }
882
- };
883
- const renderNegativeMargin = {
884
- isEqual(oldState, newState) {
885
- return oldState.deltaY === newState.deltaY;
886
- },
887
- apply(oldState, newState) {
888
- return [/* method */SetNegativeMargin, /* negativeMargin */-newState.deltaY];
889
- }
890
- };
891
- const renderScrollBar = {
892
- isEqual(oldState, newState) {
893
- return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length;
894
- },
895
- apply(oldState, newState) {
896
- const total = newState.items.length;
897
- const contentHeight = total * newState.itemHeight;
898
- const scrollBarHeight = getScrollBarSize(newState.height, contentHeight, newState.minimumSliderSize);
899
- const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
900
- return [/* method */SetScrollBar, /* scrollBarY */scrollBarY, /* scrollBarHeight */scrollBarHeight];
901
- }
902
- };
903
- const render$3 = [renderItems, renderBounds$1, renderHeight, renderNegativeMargin, renderScrollBar];
904
- const renderCompletion = (oldState, newState) => {
905
- const commands = [];
906
- for (const item of render$3) {
907
- if (!item.isEqual(oldState, newState)) {
908
- commands.push(item.apply(oldState, newState));
909
- }
910
- }
911
- return commands;
912
- };
913
-
914
- const EmptyMatches = [];
915
-
916
- const Diagonal = 1;
917
- const Left = 2;
918
-
919
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
920
-
921
- const createTable = size => {
922
- const table = [];
923
- for (let i = 0; i < size; i++) {
924
- const row = new Uint8Array(size);
925
- table.push(row);
926
- }
927
- return table;
928
- };
929
-
930
- const isLowerCase = char => {
931
- return char === char.toLowerCase();
932
- };
933
-
934
- const isUpperCase = char => {
935
- return char === char.toUpperCase();
936
- };
937
-
938
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
939
- const isGap = (columnCharBefore, columnChar) => {
940
- switch (columnCharBefore) {
941
- case Dash:
942
- case Underline:
943
- case EmptyString:
944
- case T:
945
- case Space:
946
- case Dot:
947
- return true;
948
- }
949
- if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
950
- return true;
951
- }
952
- return false;
953
- };
954
-
955
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
956
- const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch) => {
957
- if (rowCharLow !== columnCharLow) {
958
- return -1;
959
- }
960
- const isMatch = rowChar === columnChar;
961
- if (isMatch) {
962
- if (isDiagonalMatch) {
963
- return 8;
964
- }
965
- if (isGap(columnCharBefore, columnChar)) {
966
- return 8;
967
- }
968
- return 5;
969
- }
970
- if (isGap(columnCharBefore, columnChar)) {
971
- return 8;
972
- }
973
- return 5;
974
- };
975
-
976
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
977
-
978
- const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
979
- while (patternPos < patternLen && wordPos < wordLen) {
980
- if (patternLow[patternPos] === wordLow[wordPos]) {
981
- patternPos += 1;
982
- }
983
- wordPos += 1;
984
- }
985
- return patternPos === patternLen; // pattern must be exhausted
986
- };
987
-
988
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
989
- const traceHighlights = (table, arrows, patternLength, wordLength) => {
990
- let row = patternLength;
991
- let column = wordLength;
992
- const matches = [];
993
- while (row >= 1 && column >= 1) {
994
- const arrow = arrows[row][column];
995
- if (arrow === Left) {
996
- column--;
997
- } else if (arrow === Diagonal) {
998
- row--;
999
- column--;
1000
- const start = column + 1;
1001
- while (row >= 1 && column >= 1) {
1002
- const arrow = arrows[row][column];
1003
- if (arrow === Left) {
1004
- break;
1005
- }
1006
- if (arrow === Diagonal) {
1007
- row--;
1008
- column--;
1009
- }
1010
- }
1011
- const end = column;
1012
- matches.unshift(end, start);
1013
- }
1014
- }
1015
- matches.unshift(table[patternLength][wordLength - 1]);
1016
- return matches;
1017
- };
1018
-
1019
- // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
1020
- const gridSize = 128;
1021
- const table = createTable(gridSize);
1022
- const arrows = createTable(gridSize);
1023
- const filterCompletionItem = (pattern, word) => {
1024
- const patternLength = Math.min(pattern.length, gridSize - 1);
1025
- const wordLength = Math.min(word.length, gridSize - 1);
1026
- const patternLower = pattern.toLowerCase();
1027
- const wordLower = word.toLowerCase();
1028
- if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
1029
- return EmptyMatches;
1030
- }
1031
- let strongMatch = false;
1032
- for (let row = 1; row < patternLength + 1; row++) {
1033
- const rowChar = pattern[row - 1];
1034
- const rowCharLow = patternLower[row - 1];
1035
- for (let column = 1; column < wordLength + 1; column++) {
1036
- const columnChar = word[column - 1];
1037
- const columnCharLow = wordLower[column - 1];
1038
- const columnCharBefore = word[column - 2] || '';
1039
- const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
1040
- const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch);
1041
- if (row === 1 && score > 5) {
1042
- strongMatch = true;
1043
- }
1044
- let diagonalScore = score + table[row - 1][column - 1];
1045
- if (isDiagonalMatch && score !== -1) {
1046
- diagonalScore += 2;
1047
- }
1048
- const leftScore = table[row][column - 1];
1049
- if (leftScore > diagonalScore) {
1050
- table[row][column] = leftScore;
1051
- arrows[row][column] = Left;
1052
- } else {
1053
- table[row][column] = diagonalScore;
1054
- arrows[row][column] = Diagonal;
1055
- }
1056
- }
1057
- }
1058
- if (!strongMatch) {
1059
- return EmptyMatches;
1060
- }
1061
- const highlights = traceHighlights(table, arrows, patternLength, wordLength);
1062
- return highlights;
1063
- };
1064
-
1065
- const addEmptyMatch = item => {
1066
- return {
1067
- ...item,
1068
- matches: EmptyMatches
1069
- };
1070
- };
1071
- const filterCompletionItems = (completionItems, word) => {
1072
- if (word === EmptyString) {
1073
- return completionItems.map(addEmptyMatch);
1074
- }
1075
- const filteredCompletions = [];
1076
- const deprecated = [];
1077
- for (const completionItem of completionItems) {
1078
- const {
1079
- label,
1080
- flags
1081
- } = completionItem;
1082
- const result = filterCompletionItem(word, label);
1083
- if (result !== EmptyMatches) {
1084
- if (flags & Deprecated) {
1085
- // TODO avoid mutation
1086
- completionItem.matches = EmptyMatches;
1087
- deprecated.push(completionItem);
1088
- } else {
1089
- // TODO avoid mutation
1090
- completionItem.matches = result;
1091
- filteredCompletions.push(completionItem);
1092
- }
1093
- }
1094
- }
1095
- if (deprecated.length > 0) {
1096
- filteredCompletions.push(...deprecated);
1097
- }
1098
- return filteredCompletions;
1099
- };
1100
-
1101
- const getListHeight = (itemsLength, itemHeight, maxHeight) => {
1102
- number$1(itemsLength);
1103
- number$1(itemHeight);
1104
- number$1(maxHeight);
1105
- if (itemsLength === 0) {
1106
- return itemHeight;
1107
- }
1108
- const totalHeight = itemsLength * itemHeight;
1109
- return Math.min(totalHeight, maxHeight);
1110
- };
1111
-
1112
- const RE_WORD_START$1 = /^[\w\-]+/;
1113
- const RE_WORD_END$1 = /[\w\-]+$/;
1114
- const getWordAt$1 = (line, columnIndex) => {
1115
- const before = line.slice(0, columnIndex);
1116
- const matchBefore = before.match(RE_WORD_END$1);
1117
- const after = line.slice(columnIndex);
1118
- const matchAfter = after.match(RE_WORD_START$1);
1119
- let word = EmptyString;
1120
- if (matchBefore) {
1121
- word += matchBefore[0];
1122
- }
1123
- if (matchAfter) {
1124
- word += matchAfter[0];
1125
- }
1126
- return {
1127
- word
1128
- };
1129
- };
1130
- const getWordBefore$1 = (line, columnIndex) => {
1131
- const before = line.slice(0, columnIndex);
1132
- const matchBefore = before.match(RE_WORD_END$1);
1133
- if (matchBefore) {
1134
- return matchBefore[0];
1135
- }
1136
- return EmptyString;
1137
- };
1138
-
1139
- const getWordAt = (editor, rowIndex, columnIndex) => {
1140
- const {
1141
- lines
1142
- } = editor;
1143
- const line = lines[rowIndex];
1144
- return getWordAt$1(line, columnIndex);
1145
- };
1146
- const getWordBefore = (editor, rowIndex, columnIndex) => {
1147
- const {
1148
- lines
1149
- } = editor;
1150
- const line = lines[rowIndex];
1151
- return getWordBefore$1(line, columnIndex);
1152
- };
1153
-
1154
- const render$2 = (oldState, newState) => {
1155
- const commands = renderCompletion(oldState, newState);
1156
- const wrappedCommands = [];
1157
- const uid = newState.uid;
1158
- for (const command of commands) {
1159
- wrappedCommands.push(['Viewlet.send', uid, ...command]);
1160
- }
1161
- return wrappedCommands;
1162
- };
1163
- const add = widget => {
1164
- const commands = render$2(widget.oldState, widget.newState);
1165
- const id = 'EditorCompletion';
1166
- // TODO how to generate a unique integer id
1167
- // that doesn't collide with ids created in renderer worker?
1168
- const uid = widget.newState.uid;
1169
- const allCommands = [];
1170
- allCommands.push(['Viewlet.create', id, uid]);
1171
- allCommands.push(...commands);
1172
- return allCommands;
1173
- };
1174
- const remove$1 = widget => {
1175
- return [['Viewlet.send', widget.newState.uid, 'dispose']];
1176
- };
1177
- const handleEditorType$1 = (editor, state) => {
1178
- const {
1179
- unfilteredItems,
1180
- itemHeight,
1181
- maxHeight
1182
- } = state;
1183
- const {
1184
- selections
1185
- } = editor;
1186
- const rowIndex = selections[0];
1187
- const columnIndex = selections[1];
1188
- const x$1 = x(editor, rowIndex, columnIndex);
1189
- const y$1 = y(editor, rowIndex);
1190
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1191
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1192
- const newMinLineY = 0;
1193
- const newMaxLineY = Math.min(items.length, 8);
1194
- const height = getListHeight(items.length, itemHeight, maxHeight);
1195
- const finalDeltaY = items.length * itemHeight - height;
1196
- return {
1197
- ...state,
1198
- items,
1199
- x: x$1,
1200
- y: y$1,
1201
- minLineY: newMinLineY,
1202
- maxLineY: newMaxLineY,
1203
- leadingWord: wordAtOffset,
1204
- height,
1205
- finalDeltaY
1206
- };
1207
- };
1208
- const handleEditorDeleteLeft$1 = (editor, state) => {
1209
- const {
1210
- unfilteredItems,
1211
- itemHeight,
1212
- maxHeight
1213
- } = state;
1214
- const {
1215
- selections
1216
- } = editor;
1217
- const rowIndex = selections[0];
1218
- const columnIndex = selections[1];
1219
- const x$1 = x(editor, rowIndex, columnIndex);
1220
- const y$1 = y(editor, rowIndex);
1221
- const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
1222
- if (!wordAtOffset) {
1223
- return undefined;
1224
- }
1225
- const items = filterCompletionItems(unfilteredItems, wordAtOffset);
1226
- const newMaxLineY = Math.min(items.length, 8);
1227
- const height = getListHeight(items.length, itemHeight, maxHeight);
1228
- return {
1229
- ...state,
1230
- items,
1231
- x: x$1,
1232
- y: y$1,
1233
- maxLineY: newMaxLineY,
1234
- leadingWord: wordAtOffset,
1235
- height
1236
- };
1237
- };
1238
-
1239
- const EditorCompletionWidget = {
1240
- __proto__: null,
1241
- add,
1242
- handleEditorDeleteLeft: handleEditorDeleteLeft$1,
1243
- handleEditorType: handleEditorType$1,
1244
- remove: remove$1,
1245
- render: render$2
1246
- };
1247
-
1248
- const Completion = 'completion';
1249
-
1250
- const modules = Object.create(null);
1251
- const register = (id, value) => {
1252
- modules[id] = value;
1253
- };
1254
- const get$7 = id => {
1255
- return modules[id];
1256
- };
1257
-
1258
- const getModule$2 = id => {
1259
- return get$7(id);
1260
- };
1261
-
1262
- const applyWidgetChange = (editor, widget, changes) => {
1263
- const module = getModule$2(widget.id);
1264
- if (changes.length === 1 && changes[0].origin === EditorType && module.handleEditorType) {
1265
- const newState = module.handleEditorType(editor, widget.newState);
1266
- return {
1267
- ...widget,
1268
- newState
1269
- };
1270
- }
1271
- if (changes.length === 1 && changes[0].origin === DeleteLeft && module.handleEditorDeleteLeft) {
1272
- const newState = module.handleEditorDeleteLeft(editor, widget.newState);
1273
- return {
1274
- ...widget,
1275
- newState
1276
- };
1277
- }
1278
- return widget;
1279
- };
1280
-
1281
- const applyWidgetChanges = (editor, changes) => {
1282
- const widgets = editor.widgets || [];
1283
- if (widgets.length === 0) {
1284
- return widgets;
1285
- }
1286
- const newWidgets = [];
1287
- for (const widget of widgets) {
1288
- const newWidget = applyWidgetChange(editor, widget, changes);
1289
- if (newWidget.newState) {
1290
- newWidgets.push(newWidget);
1291
- }
1292
- }
1293
- return newWidgets;
1294
- };
1295
-
1296
- const splitLines$2 = lines => {
1297
- if (!lines) {
1298
- return [];
1299
- }
1300
- return lines.split('\n');
1301
- };
1302
-
1303
- // based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)
1304
-
1305
- // @ts-ignore
1306
- const insertInto = (array, start, newItems) => {
1307
- const originalLength = array.length;
1308
- const newItemsLength = newItems.length;
1309
- array.length = originalLength + newItemsLength;
1310
- // Move the items after the start index, start from the end so that we don't overwrite any value.
1311
- for (let i = originalLength - 1; i >= start; i--) {
1312
- array[i + newItemsLength] = array[i];
1313
- }
1314
- for (let i = 0; i < newItemsLength; i++) {
1315
- array[i + start] = newItems[i];
1316
- }
1317
- };
1318
-
1319
- /**
1320
- * Alternative to the native Array.splice method, it
1321
- * can only support limited number of items due to the maximum call stack size limit.
1322
- */
1323
- // @ts-ignore
1324
- const spliceLargeArray = (array, start, deleteCount, newItems) => {
1325
- const result = array.splice(start, deleteCount);
1326
- insertInto(array, start, newItems);
1327
- return result;
1328
- };
1329
-
1330
- const joinLines$2 = lines => {
1331
- return lines.join('\n');
364
+ const joinLines$2 = lines => {
365
+ return lines.join('\n');
1332
366
  };
1333
367
 
1334
368
  // TODO have function for single edit (most common, avoid one array)
@@ -1521,6 +555,15 @@ const getSelectionPairs = (selections, i) => {
1521
555
  return [first, second, third, fourth, 0];
1522
556
  };
1523
557
 
558
+ const Dash = '-';
559
+ const Dot = '.';
560
+ const EmptyString = '';
561
+ const Space = ' ';
562
+ const Tab = '\t';
563
+ const Underline = '_';
564
+ const DoubleQuote$1 = '"';
565
+ const T = 't';
566
+
1524
567
  const getTabCount = string => {
1525
568
  let count = 0;
1526
569
  for (const element of string) {
@@ -1531,8 +574,74 @@ const getTabCount = string => {
1531
574
  return count;
1532
575
  };
1533
576
 
1534
- // TODO visible selections could also be uint16array
1535
- // [top1, left1, width1, height1, top2, left2, width2, height2...]
577
+ const getFontString = (fontWeight, fontSize, fontFamily) => {
578
+ return `${fontWeight} ${fontSize}px ${fontFamily}`;
579
+ };
580
+
581
+ const getLetterSpacingString = letterSpacing => {
582
+ return `${letterSpacing}px`;
583
+ };
584
+
585
+ const createMeasureContext = () => {
586
+ const canvas = new OffscreenCanvas(0, 0);
587
+ const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
588
+ if (!ctx) {
589
+ throw new Error('Failed to get canvas context 2d');
590
+ }
591
+ return ctx;
592
+ };
593
+
594
+ const state$b = {
595
+ ctx: undefined
596
+ };
597
+ const getOrCreate = createCtx => {
598
+ if (state$b.ctx) {
599
+ return state$b.ctx;
600
+ }
601
+ state$b.ctx = createCtx();
602
+ return state$b.ctx;
603
+ };
604
+
605
+ const getContext = () => {
606
+ const ctx = getOrCreate(createMeasureContext);
607
+ return ctx;
608
+ };
609
+
610
+ const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
611
+ string(text);
612
+ number$1(fontWeight);
613
+ number$1(fontSize);
614
+ string(fontFamily);
615
+ boolean(isMonoSpaceFont);
616
+ number$1(charWidth);
617
+ if (isMonoSpaceFont) {
618
+ return text.length * charWidth;
619
+ }
620
+ if (typeof letterSpacing !== 'number') {
621
+ throw new TypeError('letterSpacing must be of type number');
622
+ }
623
+ const letterSpacingString = getLetterSpacingString(letterSpacing);
624
+ const fontString = getFontString(fontWeight, fontSize, fontFamily);
625
+ const ctx = getContext();
626
+ // @ts-ignore
627
+ ctx.letterSpacing = letterSpacingString;
628
+ // @ts-ignore
629
+ ctx.font = fontString;
630
+ // @ts-ignore
631
+ const metrics = ctx.measureText(text);
632
+ const width = metrics.width;
633
+ return width;
634
+ };
635
+
636
+ const normalizeText = (text, normalize, tabSize) => {
637
+ if (normalize) {
638
+ return text.replaceAll(Tab, Space.repeat(tabSize));
639
+ }
640
+ return text;
641
+ };
642
+ const shouldNormalizeText = text => {
643
+ return text.includes(Tab);
644
+ };
1536
645
 
1537
646
  const getX = (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, letterSpacing, tabSize, halfCursorWidth, width, averageCharWidth, difference = 0) => {
1538
647
  if (!line) {
@@ -1558,6 +667,7 @@ const getX = (line, column, fontWeight, fontSize, fontFamily, isMonospaceFont, l
1558
667
  const partialText = normalizedLine.slice(0, column + tabCount);
1559
668
  return measureTextWidth(partialText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, averageCharWidth) - halfCursorWidth + difference;
1560
669
  };
670
+
1561
671
  const getY = (row, minLineY, rowHeight) => {
1562
672
  return (row - minLineY) * rowHeight;
1563
673
  };
@@ -2067,7 +1177,7 @@ const set$5 = (id, fn) => {
2067
1177
  const get$5 = id => {
2068
1178
  return state$1$1.callbacks[id];
2069
1179
  };
2070
- const remove = id => {
1180
+ const remove$1 = id => {
2071
1181
  delete state$1$1.callbacks[id];
2072
1182
  };
2073
1183
  const state$a = {
@@ -2113,7 +1223,7 @@ const resolve = (id, args) => {
2113
1223
  return;
2114
1224
  }
2115
1225
  fn(args);
2116
- remove(id);
1226
+ remove$1(id);
2117
1227
  };
2118
1228
  const create$2$1 = (method, params) => {
2119
1229
  const {
@@ -2298,7 +1408,7 @@ const getErrorResponse = (message, error, preparePrettyError, logError) => {
2298
1408
  const errorProperty = getErrorProperty(error, prettyError);
2299
1409
  return create$1$1(message, errorProperty);
2300
1410
  };
2301
- const create$4 = (message, result) => {
1411
+ const create$5 = (message, result) => {
2302
1412
  return {
2303
1413
  jsonrpc: Two,
2304
1414
  id: message.id,
@@ -2307,7 +1417,7 @@ const create$4 = (message, result) => {
2307
1417
  };
2308
1418
  const getSuccessResponse = (message, result) => {
2309
1419
  const resultProperty = result ?? null;
2310
- return create$4(message, resultProperty);
1420
+ return create$5(message, resultProperty);
2311
1421
  };
2312
1422
  const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
2313
1423
  try {
@@ -2504,7 +1614,7 @@ const waitForFirstMessage$1 = async port => {
2504
1614
  return event;
2505
1615
  };
2506
1616
 
2507
- const create$3 = async () => {
1617
+ const create$4 = async () => {
2508
1618
  const {
2509
1619
  port1,
2510
1620
  port2
@@ -2549,7 +1659,7 @@ const wrap$3 = port => {
2549
1659
 
2550
1660
  const IpcParentWithExtensionHostWorker = {
2551
1661
  __proto__: null,
2552
- create: create$3,
1662
+ create: create$4,
2553
1663
  wrap: wrap$3
2554
1664
  };
2555
1665
 
@@ -2557,7 +1667,7 @@ const sendMessagePortToSyntaxHighlightingWorker = async port => {
2557
1667
  await invokeAndTransfer([port], 'SendMessagePortToSyntaxHighlightingWorker.sendMessagePortToSyntaxHighlightingWorker', port, 'HandleMessagePort.handleMessagePort');
2558
1668
  };
2559
1669
 
2560
- const create$2 = async () => {
1670
+ const create$3 = async () => {
2561
1671
  const {
2562
1672
  port1,
2563
1673
  port2
@@ -2602,7 +1712,7 @@ const wrap$1 = port => {
2602
1712
 
2603
1713
  const IpcParentWithSyntaxHighlightingWorker = {
2604
1714
  __proto__: null,
2605
- create: create$2,
1715
+ create: create$3,
2606
1716
  wrap: wrap$1
2607
1717
  };
2608
1718
 
@@ -2610,7 +1720,7 @@ const sendMessagePortToRendererProcess = async port => {
2610
1720
  await invokeAndTransfer([port], 'SendMessagePortToRendererProcess.sendMessagePortToRendererProcess', port, 'HandleMessagePort.handleMessagePort');
2611
1721
  };
2612
1722
 
2613
- const create$1 = async () => {
1723
+ const create$2 = async () => {
2614
1724
  const {
2615
1725
  port1,
2616
1726
  port2
@@ -2655,7 +1765,7 @@ const wrap = port => {
2655
1765
 
2656
1766
  const IpcParentWithRendererProcess = {
2657
1767
  __proto__: null,
2658
- create: create$1,
1768
+ create: create$2,
2659
1769
  wrap
2660
1770
  };
2661
1771
 
@@ -2672,7 +1782,7 @@ const getModule$1 = method => {
2672
1782
  }
2673
1783
  };
2674
1784
 
2675
- const create = async ({
1785
+ const create$1 = async ({
2676
1786
  method,
2677
1787
  ...options
2678
1788
  }) => {
@@ -2690,7 +1800,7 @@ const create = async ({
2690
1800
  const createRpc = method => {
2691
1801
  let _ipc;
2692
1802
  const listen = async () => {
2693
- const ipc = await create({
1803
+ const ipc = await create$1({
2694
1804
  method
2695
1805
  });
2696
1806
  handleIpc(ipc);
@@ -2860,98 +1970,272 @@ const getNewSelections$d = selections => {
2860
1970
  };
2861
1971
 
2862
1972
  // @ts-ignore
2863
- const addCursorAbove = editor => {
2864
- const {
2865
- selections
2866
- } = editor;
2867
- const newSelections = getNewSelections$d(selections);
2868
- return {
2869
- ...editor,
2870
- selections: newSelections
2871
- };
1973
+ const addCursorAbove = editor => {
1974
+ const {
1975
+ selections
1976
+ } = editor;
1977
+ const newSelections = getNewSelections$d(selections);
1978
+ return {
1979
+ ...editor,
1980
+ selections: newSelections
1981
+ };
1982
+ };
1983
+
1984
+ // @ts-ignore
1985
+ const getNewSelections$c = (selections, linesLength) => {
1986
+ const newSelections = [];
1987
+ for (let i = 0; i < selections.length; i += 4) {
1988
+ const startRowIndex = selections[i];
1989
+ const startColumnIndex = selections[i + 1];
1990
+ const endRowIndex = selections[i + 2];
1991
+ const endColumnIndex = selections[i + 3];
1992
+ newSelections.push(startRowIndex, startColumnIndex, endRowIndex, endColumnIndex);
1993
+ if (i === selections.length - 4 && endRowIndex < linesLength) {
1994
+ newSelections.push(endRowIndex + 1, endColumnIndex, endRowIndex + 1, endColumnIndex);
1995
+ }
1996
+ }
1997
+ return new Uint32Array(newSelections);
1998
+ };
1999
+
2000
+ // @ts-ignore
2001
+ const addCursorBelow = editor => {
2002
+ const {
2003
+ selections,
2004
+ lines
2005
+ } = editor;
2006
+ const newSelections = getNewSelections$c(selections, lines.length);
2007
+ return {
2008
+ ...editor,
2009
+ selections: newSelections
2010
+ };
2011
+ };
2012
+
2013
+ const applyEdit = async (editor, changes) => {
2014
+ object(editor);
2015
+ array(changes);
2016
+ return scheduleDocumentAndCursorsSelections(editor, changes);
2017
+ };
2018
+
2019
+ const handleBlur = editor => {
2020
+ // for (const listener of state.blurListeners) {
2021
+ // listener(editor)
2022
+ // }
2023
+ // TODO save on blur
2024
+ // Command.execute(/* Main.save */ 89)
2025
+ const newEditor = {
2026
+ ...editor,
2027
+ focused: false
2028
+ };
2029
+ return newEditor;
2030
+ };
2031
+
2032
+ const replaceRange = (editor, ranges, replacement, origin) => {
2033
+ const changes = [];
2034
+ const rangesLength = ranges.length;
2035
+ for (let i = 0; i < rangesLength; i += 4) {
2036
+ const [selectionStartRow, selectionStartColumn, selectionEndRow, selectionEndColumn] = getSelectionPairs(ranges, i);
2037
+ const start = {
2038
+ rowIndex: selectionStartRow,
2039
+ columnIndex: selectionStartColumn
2040
+ };
2041
+ const end = {
2042
+ rowIndex: selectionEndRow,
2043
+ columnIndex: selectionEndColumn
2044
+ };
2045
+ const selection = {
2046
+ start,
2047
+ end
2048
+ };
2049
+ changes.push({
2050
+ start: start,
2051
+ end: end,
2052
+ inserted: replacement,
2053
+ deleted: getSelectionText(editor, selection),
2054
+ origin
2055
+ });
2056
+ }
2057
+ return changes;
2058
+ };
2059
+
2060
+ const editorReplaceSelections = (editor, replacement, origin) => {
2061
+ const {
2062
+ selections
2063
+ } = editor;
2064
+ return replaceRange(editor, selections, replacement, origin);
2065
+ };
2066
+
2067
+ const getAccurateColumnIndexAscii = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth) => {
2068
+ for (let i = guess; i < line.length; i++) {
2069
+ const width = measureTextWidth(line.slice(0, i), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2070
+ if (eventX - width < averageCharWidth / 2) {
2071
+ return i;
2072
+ }
2073
+ }
2074
+ return line.length;
2075
+ };
2076
+
2077
+ const supported = () => {
2078
+ return 'Segmenter' in Intl;
2079
+ };
2080
+ const create = () => {
2081
+ // @ts-ignore
2082
+ const segmenter = new Intl.Segmenter();
2083
+ return {
2084
+ at(line, index) {
2085
+ const segments = segmenter.segment(line);
2086
+ return segments.containing(index);
2087
+ },
2088
+ visualIndex(line, index) {
2089
+ const segments = segmenter.segment(line);
2090
+ let currentVisualIndex = 0;
2091
+ for (const segment of segments) {
2092
+ if (segment.index >= index) {
2093
+ return currentVisualIndex;
2094
+ }
2095
+ currentVisualIndex++;
2096
+ }
2097
+ return currentVisualIndex;
2098
+ },
2099
+ modelIndex(line, visualIndex) {
2100
+ const segments = segmenter.segment(line);
2101
+ let currentVisualIndex = 0;
2102
+ for (const segment of segments) {
2103
+ if (currentVisualIndex >= visualIndex) {
2104
+ return segment.index;
2105
+ }
2106
+ currentVisualIndex++;
2107
+ }
2108
+ return line.length;
2109
+ },
2110
+ getSegments(line) {
2111
+ return segmenter.segment(line);
2112
+ }
2113
+ };
2114
+ };
2115
+
2116
+ // @ts-ignore
2117
+ const getAccurateColumnIndexUnicode = (line, guess, averageCharWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing) => {
2118
+ const segmenter = create();
2119
+ const segments = segmenter.getSegments(line);
2120
+ const isMonospaceFont = false;
2121
+ const charWidth = 0;
2122
+ for (const segment of segments) {
2123
+ const width = measureTextWidth(line.slice(0, segment.index), fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2124
+ if (eventX - width < averageCharWidth) {
2125
+ return segment.index;
2126
+ }
2127
+ }
2128
+ return line.length;
2129
+ };
2130
+
2131
+ const RE_ASCII = /^[\p{ASCII}]*$/u;
2132
+ const isAscii = line => {
2133
+ return RE_ASCII.test(line);
2134
+ };
2135
+
2136
+ // @ts-ignore
2137
+ const guessOffset = (eventX, averageCharWidth) => {
2138
+ const guess = Math.round(eventX / averageCharWidth);
2139
+ return guess;
2140
+ };
2141
+
2142
+ // @ts-ignore
2143
+ const normalizeGuess = (line, guess, tabSize) => {
2144
+ let normalizedGuess = guess;
2145
+ for (let i = 0; i < guess; i++) {
2146
+ if (line[i] === Tab) {
2147
+ normalizedGuess -= tabSize - 1;
2148
+ }
2149
+ }
2150
+ return normalizedGuess;
2872
2151
  };
2873
2152
 
2874
2153
  // @ts-ignore
2875
- const getNewSelections$c = (selections, linesLength) => {
2876
- const newSelections = [];
2877
- for (let i = 0; i < selections.length; i += 4) {
2878
- const startRowIndex = selections[i];
2879
- const startColumnIndex = selections[i + 1];
2880
- const endRowIndex = selections[i + 2];
2881
- const endColumnIndex = selections[i + 3];
2882
- newSelections.push(startRowIndex, startColumnIndex, endRowIndex, endColumnIndex);
2883
- if (i === selections.length - 4 && endRowIndex < linesLength) {
2884
- newSelections.push(endRowIndex + 1, endColumnIndex, endRowIndex + 1, endColumnIndex);
2154
+ const getAccurateColumnIndex = (line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX) => {
2155
+ string(line);
2156
+ number$1(fontWeight);
2157
+ number$1(fontSize);
2158
+ string(fontFamily);
2159
+ number$1(letterSpacing);
2160
+ boolean(isMonospaceFont);
2161
+ number$1(charWidth);
2162
+ number$1(tabSize);
2163
+ number$1(eventX);
2164
+ // Assert.greaterZero(charWidth)
2165
+ const guess = guessOffset(eventX, charWidth);
2166
+ const normalize = shouldNormalizeText(line);
2167
+ const normalizedGuess = normalizeGuess(line, guess, tabSize);
2168
+ const text = line.slice(0, normalizedGuess);
2169
+ const normalizedText = normalizeText(text, normalize, tabSize);
2170
+ const actual = measureTextWidth(normalizedText, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2171
+ const isAscii$1 = isAscii(line);
2172
+ if (isAscii$1) {
2173
+ if (Math.abs(eventX - actual) < charWidth / 2) {
2174
+ return normalizedGuess;
2885
2175
  }
2176
+ return getAccurateColumnIndexAscii(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2886
2177
  }
2887
- return new Uint32Array(newSelections);
2178
+ return getAccurateColumnIndexUnicode(line, normalizedGuess, charWidth, eventX, fontWeight, fontSize, fontFamily, letterSpacing);
2888
2179
  };
2889
2180
 
2890
- // @ts-ignore
2891
- const addCursorBelow = editor => {
2181
+ const at = (editor, eventX, eventY) => {
2182
+ object(editor);
2183
+ number$1(eventX);
2184
+ number$1(eventY);
2892
2185
  const {
2893
- selections,
2894
- lines
2186
+ y,
2187
+ deltaY,
2188
+ rowHeight,
2189
+ fontSize,
2190
+ fontWeight,
2191
+ fontFamily,
2192
+ letterSpacing,
2193
+ lines,
2194
+ tabSize,
2195
+ differences,
2196
+ isMonospaceFont,
2197
+ charWidth
2895
2198
  } = editor;
2896
- const newSelections = getNewSelections$c(selections, lines.length);
2199
+ const rowIndex = Math.floor((eventY - y + deltaY) / rowHeight);
2200
+ if (rowIndex < 0) {
2201
+ return {
2202
+ rowIndex: 0,
2203
+ columnIndex: 0
2204
+ };
2205
+ }
2206
+ const clampedRowIndex = clamp(rowIndex, 0, lines.length - 1);
2207
+ const line = lines[clampedRowIndex];
2208
+ const columnIndex = getAccurateColumnIndex(line, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth, tabSize, eventX);
2897
2209
  return {
2898
- ...editor,
2899
- selections: newSelections
2900
- };
2901
- };
2902
-
2903
- const applyEdit = async (editor, changes) => {
2904
- object(editor);
2905
- array(changes);
2906
- return scheduleDocumentAndCursorsSelections(editor, changes);
2907
- };
2908
-
2909
- const handleBlur = editor => {
2910
- // for (const listener of state.blurListeners) {
2911
- // listener(editor)
2912
- // }
2913
- // TODO save on blur
2914
- // Command.execute(/* Main.save */ 89)
2915
- const newEditor = {
2916
- ...editor,
2917
- focused: false
2210
+ rowIndex: clampedRowIndex,
2211
+ columnIndex
2918
2212
  };
2919
- return newEditor;
2920
2213
  };
2921
2214
 
2922
- const replaceRange = (editor, ranges, replacement, origin) => {
2923
- const changes = [];
2924
- const rangesLength = ranges.length;
2925
- for (let i = 0; i < rangesLength; i += 4) {
2926
- const [selectionStartRow, selectionStartColumn, selectionEndRow, selectionEndColumn] = getSelectionPairs(ranges, i);
2927
- const start = {
2928
- rowIndex: selectionStartRow,
2929
- columnIndex: selectionStartColumn
2930
- };
2931
- const end = {
2932
- rowIndex: selectionEndRow,
2933
- columnIndex: selectionEndColumn
2934
- };
2935
- const selection = {
2936
- start,
2937
- end
2938
- };
2939
- changes.push({
2940
- start: start,
2941
- end: end,
2942
- inserted: replacement,
2943
- deleted: getSelectionText(editor, selection),
2944
- origin
2945
- });
2946
- }
2947
- return changes;
2215
+ /**
2216
+ * @deprecated this doesn't work for variable width characters (unicode/emoji).
2217
+ * Use position computation in renderer process instead
2218
+ *
2219
+ * @param {object} editor
2220
+ * @param {number} rowIndex
2221
+ * @param {number} columnIndex
2222
+ * @returns
2223
+ */
2224
+ const x = (editor, rowIndex, columnIndex) => {
2225
+ const {
2226
+ columnWidth,
2227
+ x
2228
+ } = editor;
2229
+ const offsetX = columnIndex * columnWidth + x;
2230
+ return offsetX;
2948
2231
  };
2949
-
2950
- const editorReplaceSelections = (editor, replacement, origin) => {
2232
+ const y = (editor, rowIndex) => {
2951
2233
  const {
2952
- selections
2234
+ rowHeight,
2235
+ y
2953
2236
  } = editor;
2954
- return replaceRange(editor, selections, replacement, origin);
2237
+ const offsetY = (rowIndex + 1) * rowHeight + y;
2238
+ return offsetY;
2955
2239
  };
2956
2240
 
2957
2241
  const state$7 = {
@@ -3060,6 +2344,8 @@ const cancelSelection = editor => {
3060
2344
  return scheduleSelections(editor, newSelections);
3061
2345
  };
3062
2346
 
2347
+ const Completion = 'completion';
2348
+
3063
2349
  const isCompletionWidget = widget => {
3064
2350
  return widget.id === Completion;
3065
2351
  };
@@ -3256,36 +2542,30 @@ const copy = async editor => {
3256
2542
  // TODO handle multiline selection
3257
2543
  // TODO handle multiple cursors
3258
2544
 
3259
- // @ts-ignore
3260
2545
  const copyLineDown = editor => {
3261
2546
  const {
3262
- selections,
3263
- primarySelectionIndex
2547
+ selections
3264
2548
  } = editor;
3265
- const rowIndex = selections[primarySelectionIndex];
2549
+ const rowIndex = selections[0];
3266
2550
  number$1(rowIndex);
3267
2551
  const position = {
3268
2552
  rowIndex,
3269
2553
  columnIndex: 0
3270
2554
  };
3271
2555
  const changes = [{
3272
- inserted: [getLine(editor, rowIndex), ''],
3273
- deleted: [''],
3274
2556
  start: position,
3275
- end: position
2557
+ end: position,
2558
+ inserted: [getLine(editor, rowIndex), ''],
2559
+ deleted: ['']
3276
2560
  }];
3277
2561
  return scheduleDocumentAndCursorsSelections(editor, changes);
3278
2562
  };
3279
2563
 
3280
- // @ts-ignore
3281
-
3282
- // @ts-ignore
3283
2564
  const copyLineUp = editor => {
3284
2565
  const {
3285
- selections,
3286
- primarySelectionIndex
2566
+ selections
3287
2567
  } = editor;
3288
- const rowIndex = selections[primarySelectionIndex];
2568
+ const rowIndex = selections[0];
3289
2569
  const position = {
3290
2570
  rowIndex: rowIndex,
3291
2571
  columnIndex: 0
@@ -3383,35 +2663,28 @@ const editorCursorHorizontalLeft = (editor, getDelta) => {
3383
2663
  return scheduleSelections(editor, newSelections);
3384
2664
  };
3385
2665
 
3386
- // @ts-ignore
3387
2666
  const characterLeft = (line, columnIndex) => {
3388
2667
  if (!supported()) {
3389
2668
  return 1;
3390
2669
  }
3391
- const segmenter = create$5();
2670
+ const segmenter = create();
3392
2671
  const last = segmenter.at(line, columnIndex - 1);
3393
2672
  return columnIndex - last.index;
3394
2673
  };
3395
2674
  const twoCharactersLeft = () => {
3396
2675
  return 2;
3397
2676
  };
3398
-
3399
- // @ts-ignore
3400
2677
  const characterRight = (line, columnIndex) => {
3401
2678
  if (!supported()) {
3402
2679
  return 1;
3403
2680
  }
3404
- const segmenter = create$5();
2681
+ const segmenter = create();
3405
2682
  const next = segmenter.at(line, columnIndex);
3406
2683
  return next.segment.length;
3407
2684
  };
3408
-
3409
- // @ts-ignore
3410
2685
  const isWhitespace = char => {
3411
2686
  return char === Space || char === Tab;
3412
2687
  };
3413
-
3414
- // @ts-ignore
3415
2688
  const lineCharacterStart = (line, columnIndex) => {
3416
2689
  if (line.length === 0) {
3417
2690
  return 0;
@@ -3423,13 +2696,9 @@ const lineCharacterStart = (line, columnIndex) => {
3423
2696
  }
3424
2697
  return columnIndex;
3425
2698
  };
3426
-
3427
- // @ts-ignore
3428
2699
  const lineEnd = (line, columnIndex) => {
3429
2700
  return line.length - columnIndex;
3430
2701
  };
3431
-
3432
- // @ts-ignore
3433
2702
  const tryRegexArray = (partialLine, regexArray) => {
3434
2703
  for (const regex of regexArray) {
3435
2704
  const match = partialLine.match(regex);
@@ -3454,8 +2723,6 @@ const wordLeft = (line, columnIndex) => {
3454
2723
  const RE_WORD_RIGHT_1 = /^\s*[\u00C0-\u017F\w]+/i;
3455
2724
  const RE_WORD_RIGHT_2 = /^[^a-zA-Z\d]+\w*/;
3456
2725
  const RE_WORD_RIGHT = [RE_WORD_RIGHT_1, RE_WORD_RIGHT_2];
3457
-
3458
- // @ts-ignore
3459
2726
  const wordRight = (line, columnIndex) => {
3460
2727
  const partialLine = line.slice(columnIndex);
3461
2728
  return tryRegexArray(partialLine, RE_WORD_RIGHT);
@@ -3470,8 +2737,6 @@ const RE_PARTIAL_WORD_LEFT_7 = /(?<![A-Z])[A-Z]_+\s*$/;
3470
2737
  const RE_PARTIAL_WORD_LEFT_8 = /[a-z]+\s*$/;
3471
2738
  const RE_PARTIAL_WORD_LEFT_9 = /[^a-zA-Z\d\s]+\s*$/;
3472
2739
  const RE_PARTIAL_WORD_LEFT = [RE_PARTIAL_WORD_LEFT_1, RE_PARTIAL_WORD_LEFT_2, RE_PARTIAL_WORD_LEFT_3, RE_PARTIAL_WORD_LEFT_4, RE_PARTIAL_WORD_LEFT_5, RE_PARTIAL_WORD_LEFT_6, RE_PARTIAL_WORD_LEFT_7, RE_PARTIAL_WORD_LEFT_8, RE_PARTIAL_WORD_LEFT_9];
3473
-
3474
- // @ts-ignore
3475
2740
  const wordPartLeft = (line, columnIndex) => {
3476
2741
  const partialLine = line.slice(0, columnIndex);
3477
2742
  return tryRegexArray(partialLine, RE_PARTIAL_WORD_LEFT);
@@ -3490,8 +2755,6 @@ const ARRAY_PARTIAL_WORD_RIGHT_1 = [RE_PARTIAL_WORD_RIGHT_1,
3490
2755
  RE_PARTIAL_WORD_RIGHT_3, RE_PARTIAL_WORD_RIGHT_4, RE_PARTIAL_WORD_RIGHT_5, RE_PARTIAL_WORD_RIGHT_6];
3491
2756
  const ARRAY_PARTIAL_WORD_RIGHT_2 = [RE_PARTIAL_WORD_RIGHT_1, RE_PARTIAL_WORD_RIGHT_2, RE_PARTIAL_WORD_RIGHT_3, RE_PARTIAL_WORD_RIGHT_4, RE_PARTIAL_WORD_RIGHT_5, RE_PARTIAL_WORD_RIGHT_6];
3492
2757
  const RE_UPPERCASE = /[A-Z]/;
3493
-
3494
- // @ts-ignore
3495
2758
  const wordPartRight = (line, columnIndex) => {
3496
2759
  const partialLine = line.slice(columnIndex);
3497
2760
  // line[columnIndex]//?
@@ -3506,7 +2769,6 @@ const cursorCharacterLeft = editor => {
3506
2769
  return editorCursorHorizontalLeft(editor, characterLeft);
3507
2770
  };
3508
2771
 
3509
- // @ts-ignore
3510
2772
  const editorGetPositionRight = (position, lines, getDelta) => {
3511
2773
  const rowIndex = position.rowIndex;
3512
2774
  const columnIndex = position.columnIndex;
@@ -3525,8 +2787,6 @@ const editorGetPositionRight = (position, lines, getDelta) => {
3525
2787
  columnIndex: columnIndex + delta
3526
2788
  };
3527
2789
  };
3528
-
3529
- // @ts-ignore
3530
2790
  const moveToPositionRight = (selections, i, rowIndex, columnIndex, lines, getDelta) => {
3531
2791
  if (rowIndex >= lines.length) {
3532
2792
  return;
@@ -3542,9 +2802,6 @@ const moveToPositionRight = (selections, i, rowIndex, columnIndex, lines, getDel
3542
2802
  }
3543
2803
  };
3544
2804
 
3545
- // @ts-ignore
3546
-
3547
- // @ts-ignore
3548
2805
  const getNewSelections$a = (selections, lines, getDelta) => {
3549
2806
  const newSelections = new Uint32Array(selections.length);
3550
2807
  for (let i = 0; i < selections.length; i += 4) {
@@ -3558,8 +2815,6 @@ const getNewSelections$a = (selections, lines, getDelta) => {
3558
2815
  }
3559
2816
  return newSelections;
3560
2817
  };
3561
-
3562
- // @ts-ignore
3563
2818
  const editorCursorHorizontalRight = (editor, getDelta) => {
3564
2819
  const {
3565
2820
  lines,
@@ -3616,16 +2871,9 @@ const getNewSelections$8 = selections => {
3616
2871
  return map(selections, moveSelectionWithoutIntlSegmenter);
3617
2872
  };
3618
2873
  const cursorVertical = (editor, getPosition, getEdgePosition, delta) => {
3619
- // if (TextSegmenter.supported()) {
3620
- // return editorCursorsVerticalWithIntlSegmenter(
3621
- // editor,
3622
- // getPosition,
3623
- // getEdgePosition,
3624
- // delta
3625
- // )
3626
- // }
3627
- const selections = editor.selections;
3628
- // @ts-ignore
2874
+ const {
2875
+ selections
2876
+ } = editor;
3629
2877
  const newSelections = getNewSelections$8(selections);
3630
2878
  return scheduleSelections(editor, newSelections);
3631
2879
  };
@@ -3652,7 +2900,6 @@ const cursorWordPartRight = editor => {
3652
2900
  return editorCursorHorizontalRight(editor, wordPartRight);
3653
2901
  };
3654
2902
 
3655
- // @ts-ignore
3656
2903
  const cursorWordRight = editor => {
3657
2904
  return editorCursorHorizontalRight(editor, wordRight);
3658
2905
  };
@@ -3918,32 +3165,74 @@ const applyDocumentEdits = (editor, edits) => {
3918
3165
  if (edits.length === 0) {
3919
3166
  return editor;
3920
3167
  }
3921
- const documentEdits = getDocumentEdits(editor, edits);
3922
- return scheduleDocumentAndCursorsSelections(editor, documentEdits);
3168
+ const documentEdits = getDocumentEdits(editor, edits);
3169
+ return scheduleDocumentAndCursorsSelections(editor, documentEdits);
3170
+ };
3171
+
3172
+ const expectedErrorMessage = 'Failed to execute formatting provider: FormattingError:';
3173
+ const isFormattingError = error => {
3174
+ return error && error instanceof Error && error.message.startsWith(expectedErrorMessage);
3175
+ };
3176
+
3177
+ // TODO also format with cursor
3178
+ const format = async editor => {
3179
+ try {
3180
+ const edits = await format$1(editor);
3181
+ return applyDocumentEdits(editor, edits);
3182
+ } catch (error) {
3183
+ if (isFormattingError(error)) {
3184
+ console.error('Formatting Error:',
3185
+ // @ts-ignore
3186
+ error.message.slice(expectedErrorMessage.length));
3187
+ return editor;
3188
+ }
3189
+ console.error(error);
3190
+ const displayErrorMessage = `${error}`;
3191
+ await editorShowMessage(editor, 0, 0, displayErrorMessage, true);
3192
+ return editor;
3193
+ }
3194
+ };
3195
+
3196
+ const RE_WORD_START$1 = /^[\w\-]+/;
3197
+ const RE_WORD_END$1 = /[\w\-]+$/;
3198
+ const getWordAt$1 = (line, columnIndex) => {
3199
+ const before = line.slice(0, columnIndex);
3200
+ const matchBefore = before.match(RE_WORD_END$1);
3201
+ const after = line.slice(columnIndex);
3202
+ const matchAfter = after.match(RE_WORD_START$1);
3203
+ let word = EmptyString;
3204
+ if (matchBefore) {
3205
+ word += matchBefore[0];
3206
+ }
3207
+ if (matchAfter) {
3208
+ word += matchAfter[0];
3209
+ }
3210
+ return {
3211
+ word
3212
+ };
3213
+ };
3214
+ const getWordBefore$1 = (line, columnIndex) => {
3215
+ const before = line.slice(0, columnIndex);
3216
+ const matchBefore = before.match(RE_WORD_END$1);
3217
+ if (matchBefore) {
3218
+ return matchBefore[0];
3219
+ }
3220
+ return EmptyString;
3923
3221
  };
3924
3222
 
3925
- const expectedErrorMessage = 'Failed to execute formatting provider: FormattingError:';
3926
- const isFormattingError = error => {
3927
- return error && error instanceof Error && error.message.startsWith(expectedErrorMessage);
3223
+ const getWordAt = (editor, rowIndex, columnIndex) => {
3224
+ const {
3225
+ lines
3226
+ } = editor;
3227
+ const line = lines[rowIndex];
3228
+ return getWordAt$1(line, columnIndex);
3928
3229
  };
3929
-
3930
- // TODO also format with cursor
3931
- const format = async editor => {
3932
- try {
3933
- const edits = await format$1(editor);
3934
- return applyDocumentEdits(editor, edits);
3935
- } catch (error) {
3936
- if (isFormattingError(error)) {
3937
- console.error('Formatting Error:',
3938
- // @ts-ignore
3939
- error.message.slice(expectedErrorMessage.length));
3940
- return editor;
3941
- }
3942
- console.error(error);
3943
- const displayErrorMessage = `${error}`;
3944
- await editorShowMessage(editor, 0, 0, displayErrorMessage, true);
3945
- return editor;
3946
- }
3230
+ const getWordBefore = (editor, rowIndex, columnIndex) => {
3231
+ const {
3232
+ lines
3233
+ } = editor;
3234
+ const line = lines[rowIndex];
3235
+ return getWordBefore$1(line, columnIndex);
3947
3236
  };
3948
3237
 
3949
3238
  // @ts-ignore
@@ -3952,6 +3241,46 @@ const getDefinition = async (editor, offset) => {
3952
3241
  return definition;
3953
3242
  };
3954
3243
 
3244
+ const emptyObject = {};
3245
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
3246
+ const i18nString = (key, placeholders = emptyObject) => {
3247
+ if (placeholders === emptyObject) {
3248
+ return key;
3249
+ }
3250
+ const replacer = (match, rest) => {
3251
+ return placeholders[rest];
3252
+ };
3253
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
3254
+ };
3255
+
3256
+ const UiStrings = {
3257
+ GoToDefinition: 'Go to Definition',
3258
+ NoDefinitionFound: 'No definition found',
3259
+ NoDefinitionFoundFor: "No definition found for '{PH1}'",
3260
+ NoTypeDefinitionFound: 'No type definition found',
3261
+ NoTypeDefinitionFoundFor: "No type definition found for '{PH1}'",
3262
+ NoResults: 'No Results'
3263
+ };
3264
+ const noDefinitionFound = () => {
3265
+ return i18nString(UiStrings.NoDefinitionFound);
3266
+ };
3267
+ const noDefinitionFoundFor = word => {
3268
+ return i18nString(UiStrings.NoDefinitionFoundFor, {
3269
+ PH1: word
3270
+ });
3271
+ };
3272
+ const noTypeDefinitionFoundFor = word => {
3273
+ return i18nString(UiStrings.NoTypeDefinitionFoundFor, {
3274
+ PH1: word
3275
+ });
3276
+ };
3277
+ const noTypeDefinitionFound = () => {
3278
+ return i18nString(UiStrings.NoTypeDefinitionFound);
3279
+ };
3280
+ const noResults = () => {
3281
+ return i18nString(UiStrings.NoResults);
3282
+ };
3283
+
3955
3284
  // @ts-ignore
3956
3285
  const goTo = async ({
3957
3286
  editor,
@@ -5114,81 +4443,270 @@ const activateByEvent = async event => {
5114
4443
  await invoke$3('ExtensionHostManagement.activateByEvent', event);
5115
4444
  };
5116
4445
 
5117
- const execute = async ({
5118
- editor,
5119
- args,
5120
- event,
5121
- method,
5122
- noProviderFoundMessage,
5123
- noProviderFoundResult = undefined
5124
- }) => {
5125
- const fullEvent = `${event}:${editor.languageId}`;
5126
- await activateByEvent(fullEvent);
5127
- const result = await invoke$2(method, editor.uid, ...args);
5128
- return result;
4446
+ const execute = async ({
4447
+ editor,
4448
+ args,
4449
+ event,
4450
+ method,
4451
+ noProviderFoundMessage,
4452
+ noProviderFoundResult = undefined
4453
+ }) => {
4454
+ const fullEvent = `${event}:${editor.languageId}`;
4455
+ await activateByEvent(fullEvent);
4456
+ const result = await invoke$2(method, editor.uid, ...args);
4457
+ return result;
4458
+ };
4459
+
4460
+ const combineResults = results => {
4461
+ return results[0] ?? [];
4462
+ };
4463
+ const executeCompletionProvider = (editor, offset) => {
4464
+ return execute({
4465
+ editor,
4466
+ event: OnCompletion,
4467
+ method: CompletionExecute,
4468
+ args: [offset],
4469
+ noProviderFoundMessage: 'no completion provider found',
4470
+ noProviderFoundResult: [],
4471
+ combineResults
4472
+ });
4473
+ };
4474
+ const combineResultsResolve = items => {
4475
+ return items[0] ?? undefined;
4476
+ };
4477
+ const executeResolveCompletionItem = (editor, offset, name, completionItem) => {
4478
+ return execute({
4479
+ editor,
4480
+ event: OnCompletion,
4481
+ method: CompletionResolveExecute,
4482
+ args: [offset, name, completionItem],
4483
+ noProviderFoundMessage: 'no completion provider found',
4484
+ noProviderFoundResult: [],
4485
+ combineResults: combineResultsResolve
4486
+ });
4487
+ };
4488
+
4489
+ // TODO possible to do this with events/state machine instead of promises -> enables canceling operations / concurrent calls
4490
+ const getCompletions = async editor => {
4491
+ const {
4492
+ selections
4493
+ } = editor;
4494
+ const rowIndex = selections[0];
4495
+ const columnIndex = selections[1];
4496
+ // Editor.sync(editor)
4497
+ const offset = await offsetAt(editor, rowIndex, columnIndex);
4498
+ const completions = await executeCompletionProvider(editor, offset);
4499
+ return completions;
4500
+ };
4501
+
4502
+ // TODO don't send unnecessary parts of completion item like matches
4503
+ const resolveCompletion = async (editor, name, completionItem) => {
4504
+ try {
4505
+ object(editor);
4506
+ string(name);
4507
+ object(completionItem);
4508
+ const rowIndex = editor.selections[0];
4509
+ const columnIndex = editor.selections[1];
4510
+ const offset = await offsetAt(editor, rowIndex, columnIndex);
4511
+ // @ts-ignore
4512
+ const resolvedCompletionItem = await executeResolveCompletionItem(editor, offset, name, completionItem);
4513
+ return resolvedCompletionItem;
4514
+ } catch {
4515
+ return undefined;
4516
+ }
4517
+ };
4518
+
4519
+ const None$1 = 1;
4520
+
4521
+ const EmptyMatches = [];
4522
+
4523
+ const Diagonal = 1;
4524
+ const Left = 2;
4525
+
4526
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4527
+
4528
+ const createTable = size => {
4529
+ const table = [];
4530
+ for (let i = 0; i < size; i++) {
4531
+ const row = new Uint8Array(size);
4532
+ table.push(row);
4533
+ }
4534
+ return table;
4535
+ };
4536
+
4537
+ const isLowerCase = char => {
4538
+ return char === char.toLowerCase();
4539
+ };
4540
+
4541
+ const isUpperCase = char => {
4542
+ return char === char.toUpperCase();
4543
+ };
4544
+
4545
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4546
+ const isGap = (columnCharBefore, columnChar) => {
4547
+ switch (columnCharBefore) {
4548
+ case Dash:
4549
+ case Underline:
4550
+ case EmptyString:
4551
+ case T:
4552
+ case Space:
4553
+ case Dot:
4554
+ return true;
4555
+ }
4556
+ if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
4557
+ return true;
4558
+ }
4559
+ return false;
4560
+ };
4561
+
4562
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4563
+ const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch) => {
4564
+ if (rowCharLow !== columnCharLow) {
4565
+ return -1;
4566
+ }
4567
+ const isMatch = rowChar === columnChar;
4568
+ if (isMatch) {
4569
+ if (isDiagonalMatch) {
4570
+ return 8;
4571
+ }
4572
+ if (isGap(columnCharBefore, columnChar)) {
4573
+ return 8;
4574
+ }
4575
+ return 5;
4576
+ }
4577
+ if (isGap(columnCharBefore, columnChar)) {
4578
+ return 8;
4579
+ }
4580
+ return 5;
4581
+ };
4582
+
4583
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4584
+
4585
+ const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
4586
+ while (patternPos < patternLen && wordPos < wordLen) {
4587
+ if (patternLow[patternPos] === wordLow[wordPos]) {
4588
+ patternPos += 1;
4589
+ }
4590
+ wordPos += 1;
4591
+ }
4592
+ return patternPos === patternLen; // pattern must be exhausted
4593
+ };
4594
+
4595
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4596
+ const traceHighlights = (table, arrows, patternLength, wordLength) => {
4597
+ let row = patternLength;
4598
+ let column = wordLength;
4599
+ const matches = [];
4600
+ while (row >= 1 && column >= 1) {
4601
+ const arrow = arrows[row][column];
4602
+ if (arrow === Left) {
4603
+ column--;
4604
+ } else if (arrow === Diagonal) {
4605
+ row--;
4606
+ column--;
4607
+ const start = column + 1;
4608
+ while (row >= 1 && column >= 1) {
4609
+ const arrow = arrows[row][column];
4610
+ if (arrow === Left) {
4611
+ break;
4612
+ }
4613
+ if (arrow === Diagonal) {
4614
+ row--;
4615
+ column--;
4616
+ }
4617
+ }
4618
+ const end = column;
4619
+ matches.unshift(end, start);
4620
+ }
4621
+ }
4622
+ matches.unshift(table[patternLength][wordLength - 1]);
4623
+ return matches;
4624
+ };
4625
+
4626
+ // based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
4627
+ const gridSize = 128;
4628
+ const table = createTable(gridSize);
4629
+ const arrows = createTable(gridSize);
4630
+ const filterCompletionItem = (pattern, word) => {
4631
+ const patternLength = Math.min(pattern.length, gridSize - 1);
4632
+ const wordLength = Math.min(word.length, gridSize - 1);
4633
+ const patternLower = pattern.toLowerCase();
4634
+ const wordLower = word.toLowerCase();
4635
+ if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
4636
+ return EmptyMatches;
4637
+ }
4638
+ let strongMatch = false;
4639
+ for (let row = 1; row < patternLength + 1; row++) {
4640
+ const rowChar = pattern[row - 1];
4641
+ const rowCharLow = patternLower[row - 1];
4642
+ for (let column = 1; column < wordLength + 1; column++) {
4643
+ const columnChar = word[column - 1];
4644
+ const columnCharLow = wordLower[column - 1];
4645
+ const columnCharBefore = word[column - 2] || '';
4646
+ const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
4647
+ const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, column, wordLength, isDiagonalMatch);
4648
+ if (row === 1 && score > 5) {
4649
+ strongMatch = true;
4650
+ }
4651
+ let diagonalScore = score + table[row - 1][column - 1];
4652
+ if (isDiagonalMatch && score !== -1) {
4653
+ diagonalScore += 2;
4654
+ }
4655
+ const leftScore = table[row][column - 1];
4656
+ if (leftScore > diagonalScore) {
4657
+ table[row][column] = leftScore;
4658
+ arrows[row][column] = Left;
4659
+ } else {
4660
+ table[row][column] = diagonalScore;
4661
+ arrows[row][column] = Diagonal;
4662
+ }
4663
+ }
4664
+ }
4665
+ if (!strongMatch) {
4666
+ return EmptyMatches;
4667
+ }
4668
+ const highlights = traceHighlights(table, arrows, patternLength, wordLength);
4669
+ return highlights;
5129
4670
  };
5130
4671
 
5131
- const combineResults = results => {
5132
- return results[0] ?? [];
5133
- };
5134
- const executeCompletionProvider = (editor, offset) => {
5135
- return execute({
5136
- editor,
5137
- event: OnCompletion,
5138
- method: CompletionExecute,
5139
- args: [offset],
5140
- noProviderFoundMessage: 'no completion provider found',
5141
- noProviderFoundResult: [],
5142
- combineResults
5143
- });
5144
- };
5145
- const combineResultsResolve = items => {
5146
- return items[0] ?? undefined;
5147
- };
5148
- const executeResolveCompletionItem = (editor, offset, name, completionItem) => {
5149
- return execute({
5150
- editor,
5151
- event: OnCompletion,
5152
- method: CompletionResolveExecute,
5153
- args: [offset, name, completionItem],
5154
- noProviderFoundMessage: 'no completion provider found',
5155
- noProviderFoundResult: [],
5156
- combineResults: combineResultsResolve
5157
- });
5158
- };
4672
+ const Deprecated = 1 << 0;
5159
4673
 
5160
- // TODO possible to do this with events/state machine instead of promises -> enables canceling operations / concurrent calls
5161
- const getCompletions = async editor => {
5162
- const {
5163
- selections
5164
- } = editor;
5165
- const rowIndex = selections[0];
5166
- const columnIndex = selections[1];
5167
- // Editor.sync(editor)
5168
- const offset = await offsetAt(editor, rowIndex, columnIndex);
5169
- const completions = await executeCompletionProvider(editor, offset);
5170
- return completions;
4674
+ const addEmptyMatch = item => {
4675
+ return {
4676
+ ...item,
4677
+ matches: EmptyMatches
4678
+ };
5171
4679
  };
5172
-
5173
- // TODO don't send unnecessary parts of completion item like matches
5174
- const resolveCompletion = async (editor, name, completionItem) => {
5175
- try {
5176
- object(editor);
5177
- string(name);
5178
- object(completionItem);
5179
- const rowIndex = editor.selections[0];
5180
- const columnIndex = editor.selections[1];
5181
- const offset = await offsetAt(editor, rowIndex, columnIndex);
5182
- // @ts-ignore
5183
- const resolvedCompletionItem = await executeResolveCompletionItem(editor, offset, name, completionItem);
5184
- return resolvedCompletionItem;
5185
- } catch {
5186
- return undefined;
4680
+ const filterCompletionItems = (completionItems, word) => {
4681
+ if (word === EmptyString) {
4682
+ return completionItems.map(addEmptyMatch);
4683
+ }
4684
+ const filteredCompletions = [];
4685
+ const deprecated = [];
4686
+ for (const completionItem of completionItems) {
4687
+ const {
4688
+ label,
4689
+ flags
4690
+ } = completionItem;
4691
+ const result = filterCompletionItem(word, label);
4692
+ if (result !== EmptyMatches) {
4693
+ if (flags & Deprecated) {
4694
+ // TODO avoid mutation
4695
+ completionItem.matches = EmptyMatches;
4696
+ deprecated.push(completionItem);
4697
+ } else {
4698
+ // TODO avoid mutation
4699
+ completionItem.matches = result;
4700
+ filteredCompletions.push(completionItem);
4701
+ }
4702
+ }
4703
+ }
4704
+ if (deprecated.length > 0) {
4705
+ filteredCompletions.push(...deprecated);
5187
4706
  }
4707
+ return filteredCompletions;
5188
4708
  };
5189
4709
 
5190
- const None = 1;
5191
-
5192
4710
  const getEditor = editorUid => {
5193
4711
  const instance = get$6(editorUid);
5194
4712
  if (!instance) {
@@ -5206,6 +4724,17 @@ const getFinalDeltaY = (height, itemHeight, itemsLength) => {
5206
4724
  return finalDeltaY;
5207
4725
  };
5208
4726
 
4727
+ const getListHeight = (itemsLength, itemHeight, maxHeight) => {
4728
+ number$1(itemsLength);
4729
+ number$1(itemHeight);
4730
+ number$1(maxHeight);
4731
+ if (itemsLength === 0) {
4732
+ return itemHeight;
4733
+ }
4734
+ const totalHeight = itemsLength * itemHeight;
4735
+ return Math.min(totalHeight, maxHeight);
4736
+ };
4737
+
5209
4738
  const RE_WORD = /[\w\-]+$/;
5210
4739
  const getWordAtOffset = editor => {
5211
4740
  const {
@@ -5222,7 +4751,7 @@ const getWordAtOffset = editor => {
5222
4751
  }
5223
4752
  return '';
5224
4753
  };
5225
- const handleEditorType = (editorUid, state, text) => {
4754
+ const handleEditorType$1 = (editorUid, state, text) => {
5226
4755
  const editor = getEditor(editorUid);
5227
4756
  const {
5228
4757
  unfilteredItems,
@@ -5252,7 +4781,7 @@ const handleEditorType = (editorUid, state, text) => {
5252
4781
  finalDeltaY
5253
4782
  };
5254
4783
  };
5255
- const handleEditorDeleteLeft = (editorUid, state) => {
4784
+ const handleEditorDeleteLeft$1 = (editorUid, state) => {
5256
4785
  const editor = getEditor(editorUid);
5257
4786
  const {
5258
4787
  unfilteredItems,
@@ -5266,7 +4795,7 @@ const handleEditorDeleteLeft = (editorUid, state) => {
5266
4795
  const y$1 = y(editor, rowIndex);
5267
4796
  const wordAtOffset = getWordAtOffset(editor);
5268
4797
  if (!wordAtOffset) {
5269
- editor.completionState = None;
4798
+ editor.completionState = None$1;
5270
4799
  return {
5271
4800
  ...state,
5272
4801
  disposed: true
@@ -5292,7 +4821,7 @@ const dispose = state => {
5292
4821
  };
5293
4822
  };
5294
4823
  const disposeWithEditor = (state, editor) => {
5295
- editor.completionState = None;
4824
+ editor.completionState = None$1;
5296
4825
  editor.completionUid = 0;
5297
4826
  // Focus.removeAdditionalFocus(FocusKey.EditorCompletion)
5298
4827
  return dispose(state);
@@ -5634,27 +5163,39 @@ const editorSelectAllLeft = editor => {
5634
5163
  editorSelectHorizontalLeft(editor, lineCharacterStart);
5635
5164
  };
5636
5165
 
5637
- // @ts-ignore
5638
-
5639
- // TODO handle virtual space
5166
+ const RE_ALPHA_NUMERIC = /[a-zA-Z\d]/;
5167
+ const isAlphaNumeric = char => {
5168
+ return RE_ALPHA_NUMERIC.test(char);
5169
+ };
5640
5170
 
5641
- // @ts-ignore
5642
- const getNewSelectionsSingleLineWord = (lines, word) => {
5643
- if (word.length === 0) {
5644
- throw new Error('word length must be greater than zero');
5171
+ const getWordStartIndex = (line, index) => {
5172
+ for (let i = index - 1; i >= 0; i--) {
5173
+ if (!isAlphaNumeric(line[i])) {
5174
+ return i + 1;
5175
+ }
5645
5176
  }
5646
- const newSelections = [];
5647
- for (let i = 0; i < lines.length; i++) {
5648
- const line = lines[i];
5649
- let columnIndex = -word.length;
5650
- while ((columnIndex = line.indexOf(word, columnIndex + word.length)) !== -1) {
5651
- newSelections.push(i, columnIndex, i, columnIndex + word.length);
5177
+ return 0;
5178
+ };
5179
+ const getWordEndIndex = (line, index) => {
5180
+ for (let i = index; i < line.length; i++) {
5181
+ if (!isAlphaNumeric(line[i])) {
5182
+ return i;
5652
5183
  }
5653
5184
  }
5654
- return new Uint32Array(newSelections);
5185
+ return line.length - 1;
5186
+ };
5187
+ const getWordMatchAtPosition = (lines, rowIndex, columnIndex) => {
5188
+ const line = lines[rowIndex];
5189
+ const start = getWordStartIndex(line, columnIndex);
5190
+ const end = getWordEndIndex(line, columnIndex);
5191
+ const word = line.slice(start, end);
5192
+ return {
5193
+ start,
5194
+ end,
5195
+ word
5196
+ };
5655
5197
  };
5656
5198
 
5657
- // @ts-ignore
5658
5199
  const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5659
5200
  let j = 0;
5660
5201
  if (!lines[rowIndex + j].endsWith(wordParts[j])) {
@@ -5665,21 +5206,36 @@ const isMultiLineMatch = (lines, rowIndex, wordParts) => {
5665
5206
  return false;
5666
5207
  }
5667
5208
  }
5668
- // j--
5669
5209
  if (!lines[rowIndex + j].startsWith(wordParts[j])) {
5670
-
5671
5210
  return false;
5672
5211
  }
5673
5212
  return true;
5674
5213
  };
5675
5214
 
5215
+ // TODO handle virtual space
5216
+
5217
+ const getNewSelectionsSingleLineWord = (lines, word) => {
5218
+ if (word.length === 0) {
5219
+ throw new Error('word length must be greater than zero');
5220
+ }
5221
+ const newSelections = [];
5222
+ for (let i = 0; i < lines.length; i++) {
5223
+ const line = lines[i];
5224
+ let columnIndex = -word.length;
5225
+ while ((columnIndex = line.indexOf(word, columnIndex + word.length)) !== -1) {
5226
+ newSelections.push(i, columnIndex, i, columnIndex + word.length);
5227
+ }
5228
+ }
5229
+ return new Uint32Array(newSelections);
5230
+ };
5231
+
5676
5232
  // TODO this is very expensive, there might be a better algorithm for this
5677
5233
  // TODO if this matches the given selections, don't schedule selections/rerender
5678
- // @ts-ignore
5679
5234
  const getAllOccurrencesMultiLine = (lines, wordParts) => {
5680
5235
  const newSelections = [];
5681
5236
  for (let rowIndex = 0; rowIndex < lines.length - wordParts.length + 1; rowIndex) {
5682
5237
  if (isMultiLineMatch(lines, rowIndex, wordParts)) {
5238
+ // @ts-ignore
5683
5239
  newSelections.push(rowIndex, lines[rowIndex].length - wordParts[0].length, rowIndex + wordParts.length - 1, wordParts.at(-1).length);
5684
5240
  rowIndex += wordParts.length - 1;
5685
5241
  } else {
@@ -5688,49 +5244,6 @@ const getAllOccurrencesMultiLine = (lines, wordParts) => {
5688
5244
  }
5689
5245
  return new Uint32Array(newSelections);
5690
5246
  };
5691
-
5692
- // TODO duplicate code with EditorSelectNextOccurrence
5693
- const RE_ALPHA_NUMERIC$1 = /[a-zA-Z\d]/;
5694
-
5695
- // @ts-ignore
5696
- const isAlphaNumeric$1 = char => {
5697
- return RE_ALPHA_NUMERIC$1.test(char);
5698
- };
5699
-
5700
- // @ts-ignore
5701
- const getWordStartIndex$1 = (line, index) => {
5702
- for (let i = index - 1; i >= 0; i--) {
5703
- if (!isAlphaNumeric$1(line[i])) {
5704
- return i + 1;
5705
- }
5706
- }
5707
- return 0;
5708
- };
5709
-
5710
- // @ts-ignore
5711
- const getWordEndIndex$1 = (line, index) => {
5712
- for (let i = index; i < line.length; i++) {
5713
- if (!isAlphaNumeric$1(line[i])) {
5714
- return i;
5715
- }
5716
- }
5717
- return line.length - 1;
5718
- };
5719
-
5720
- // @ts-ignore
5721
- const getWordMatchAtPosition$1 = (lines, rowIndex, columnIndex) => {
5722
- const line = lines[rowIndex];
5723
- const start = getWordStartIndex$1(line, columnIndex);
5724
- const end = getWordEndIndex$1(line, columnIndex);
5725
- const word = line.slice(start, end);
5726
- return {
5727
- start,
5728
- end,
5729
- word
5730
- };
5731
- };
5732
-
5733
- // @ts-ignore
5734
5247
  const getNewSelections$3 = (lines, selections) => {
5735
5248
  if (selections.length < 4) {
5736
5249
  throw new Error('selections must have at least one entry');
@@ -5742,7 +5255,7 @@ const getNewSelections$3 = (lines, selections) => {
5742
5255
  const endColumnIndex = selections[firstSelectionIndex + 3];
5743
5256
  if (startRowIndex === endRowIndex) {
5744
5257
  if (startColumnIndex === endColumnIndex) {
5745
- const wordMatch = getWordMatchAtPosition$1(lines, endRowIndex, endColumnIndex);
5258
+ const wordMatch = getWordMatchAtPosition(lines, endRowIndex, endColumnIndex);
5746
5259
  if (wordMatch.start === wordMatch.end) {
5747
5260
  return selections;
5748
5261
  }
@@ -5763,12 +5276,12 @@ const getNewSelections$3 = (lines, selections) => {
5763
5276
  const newSelections = getAllOccurrencesMultiLine(lines, wordParts);
5764
5277
  return newSelections;
5765
5278
  };
5766
-
5767
- // @ts-ignore
5768
5279
  const selectAllOccurrences = editor => {
5769
5280
  // when there are no selections -> first selection is word -> find all selection that include word
5770
- const lines = editor.lines;
5771
- const selections = editor.selections;
5281
+ const {
5282
+ lines,
5283
+ selections
5284
+ } = editor;
5772
5285
  const newSelections = getNewSelections$3(lines, selections);
5773
5286
  return scheduleSelections(editor, newSelections);
5774
5287
  };
@@ -5852,11 +5365,11 @@ const getNewSelections$1 = (lines, selections) => {
5852
5365
  let startColumnIndex = selectionStartColumn;
5853
5366
  let endColumnIndex = selectionEndColumn;
5854
5367
  const line = lines[rowIndex];
5855
- while (startColumnIndex > 0 && line[startColumnIndex] !== '"') {
5368
+ while (startColumnIndex > 0 && line[startColumnIndex] !== DoubleQuote$1) {
5856
5369
  startColumnIndex--;
5857
5370
  }
5858
5371
  startColumnIndex++;
5859
- while (endColumnIndex < line.length && line[endColumnIndex] !== '"') {
5372
+ while (endColumnIndex < line.length && line[endColumnIndex] !== DoubleQuote$1) {
5860
5373
  endColumnIndex++;
5861
5374
  }
5862
5375
  newSelections[i] = rowIndex;
@@ -5879,9 +5392,26 @@ const selectInsideString = editor => {
5879
5392
  return scheduleSelections(editor, newSelections);
5880
5393
  };
5881
5394
 
5882
- // @ts-ignore
5395
+ // TODO handle virtual space
5396
+
5397
+ // TODO editors behave differently when selecting next occurrence, for example:
5398
+
5399
+ // aaa
5400
+ // bbb 1
5401
+ // ccc
5402
+ // bbb 2
5403
+ // bbb 3
5404
+ // aaa
5405
+ // bbb 4
5406
+ // ccc
5407
+
5408
+ // when clicking first at position 4 and then position 2,
5409
+ // - vscode selects next position 3 and refuses to select position 1
5410
+ // - atom also selects next position 3 and refuses to select position 1
5411
+ // - WebStorm also selects next position 3 and refuses to select position 1
5412
+ // - brackets (codemirror) selects position 3 and then selects position 1
5413
+ // - sublime selects next position 1, then next position 3
5883
5414
 
5884
- // @ts-ignore
5885
5415
  const getSelectionEditsSingleLineWord = (lines, selections) => {
5886
5416
  const lastSelectionIndex = selections.length - 4;
5887
5417
  const rowIndex = selections[lastSelectionIndex];
@@ -5960,48 +5490,6 @@ const getSelectionEditsSingleLineWord = (lines, selections) => {
5960
5490
  }
5961
5491
  return undefined;
5962
5492
  };
5963
- const RE_ALPHA_NUMERIC = /[a-zA-Z\d]/;
5964
-
5965
- // @ts-ignore
5966
- const isAlphaNumeric = char => {
5967
- return RE_ALPHA_NUMERIC.test(char);
5968
- };
5969
-
5970
- // @ts-ignore
5971
- const getWordStartIndex = (line, index) => {
5972
- for (let i = index - 1; i >= 0; i--) {
5973
- if (!isAlphaNumeric(line[i])) {
5974
- return i + 1;
5975
- }
5976
- }
5977
- return 0;
5978
- };
5979
-
5980
- // @ts-ignore
5981
- const getWordEndIndex = (line, index) => {
5982
- for (let i = index; i < line.length; i++) {
5983
- if (!isAlphaNumeric(line[i])) {
5984
- return i;
5985
- }
5986
- }
5987
- return line.length - 1;
5988
- };
5989
-
5990
- // @ts-ignore
5991
- const getWordMatchAtPosition = (lines, rowIndex, columnIndex) => {
5992
- const line = lines[rowIndex];
5993
- const index = columnIndex;
5994
- const start = getWordStartIndex(line, index);
5995
- const end = getWordEndIndex(line, index);
5996
- const word = line.slice(start, end);
5997
- return {
5998
- start,
5999
- end,
6000
- word
6001
- };
6002
- };
6003
-
6004
- // @ts-ignore
6005
5493
  const getSelectNextOccurrenceResult = editor => {
6006
5494
  const lines = editor.lines;
6007
5495
  const selections = editor.selections;
@@ -6033,13 +5521,31 @@ const getSelectNextOccurrenceResult = editor => {
6033
5521
  return undefined;
6034
5522
  };
6035
5523
 
6036
- // @ts-ignore
5524
+ // TODO handle virtual space
5525
+
5526
+ // TODO editors behave differently when selecting next occurrence, for example:
5527
+
5528
+ // aaa
5529
+ // bbb 1
5530
+ // ccc
5531
+ // bbb 2
5532
+ // bbb 3
5533
+ // aaa
5534
+ // bbb 4
5535
+ // ccc
5536
+
5537
+ // when clicking first at position 4 and then position 2,
5538
+ // - vscode selects next position 3 and refuses to select position 1
5539
+ // - atom also selects next position 3 and refuses to select position 1
5540
+ // - WebStorm also selects next position 3 and refuses to select position 1
5541
+ // - brackets (codemirror) selects position 3 and then selects position 1
5542
+ // - sublime selects next position 1, then next position 3
5543
+
6037
5544
  const isRangeInViewPort = (minLineY, maxLineY, startRowIndex, endRowIndex) => {
6038
5545
  return startRowIndex >= minLineY && endRowIndex <= maxLineY;
6039
5546
  };
6040
5547
 
6041
5548
  // TODO handle virtual space
6042
- // @ts-ignore
6043
5549
  const selectNextOccurrence = editor => {
6044
5550
  const result = getSelectNextOccurrenceResult(editor);
6045
5551
  if (!result) {
@@ -6054,7 +5560,6 @@ const selectNextOccurrence = editor => {
6054
5560
  }
6055
5561
  // TODO what is this magic number 5?
6056
5562
  // const deltaY = (revealRangeStartRowIndex - 5) * editor.rowHeight
6057
- // @ts-ignore
6058
5563
  return scheduleDocumentAndCursorsSelections(editor, [], selectionEdits);
6059
5564
  };
6060
5565
 
@@ -6973,7 +6478,6 @@ const typeWithAutoClosing = async (editor, text) => {
6973
6478
  // EditorCommandCompletion.openFromType(editor, text)
6974
6479
  };
6975
6480
 
6976
- // @ts-ignore
6977
6481
  const inverseChange = edit => {
6978
6482
  const endColumnIndex = edit.end.columnIndex - edit.deleted[0].length + edit.inserted[0].length;
6979
6483
  return {
@@ -6987,7 +6491,6 @@ const inverseChange = edit => {
6987
6491
  };
6988
6492
  };
6989
6493
 
6990
- // @ts-ignore
6991
6494
  const undo = state => {
6992
6495
  const {
6993
6496
  undoStack
@@ -7203,7 +6706,7 @@ const select = async (editor, completionItem) => {
7203
6706
  ();
7204
6707
  if (index !== -1) {
7205
6708
  editor.widgets.splice(index, 1);
7206
- editor.completionState = None;
6709
+ editor.completionState = None$1;
7207
6710
  editor.completionUid = 0;
7208
6711
  }
7209
6712
  // TODO dispose completion widget
@@ -7266,7 +6769,7 @@ const getHover = async (editor, offset) => {
7266
6769
 
7267
6770
  let _ipc;
7268
6771
  const listen$5 = async () => {
7269
- const ipc = await create({
6772
+ const ipc = await create$1({
7270
6773
  method: RendererProcess
7271
6774
  });
7272
6775
  handleIpc(ipc);
@@ -7495,6 +6998,14 @@ const handleSashPointerUp = (state, eventX, eventY) => {
7495
6998
  return state;
7496
6999
  };
7497
7000
 
7001
+ const text = data => {
7002
+ return {
7003
+ type: Text,
7004
+ text: data,
7005
+ childCount: 0
7006
+ };
7007
+ };
7008
+
7498
7009
  const getLineInfoVirtualDom = lineInfo => {
7499
7010
  const dom = [{
7500
7011
  type: Div,
@@ -7582,7 +7093,7 @@ const renderHoverDom = {
7582
7093
  return [/* method */'Viewlet.setDom2', dom];
7583
7094
  }
7584
7095
  };
7585
- const renderBounds = {
7096
+ const renderBounds$1 = {
7586
7097
  isEqual(oldState, newState) {
7587
7098
  return oldState.x === newState.x && oldState.y === newState.y && oldState.resizedWidth === newState.resizedWidth;
7588
7099
  },
@@ -7600,10 +7111,10 @@ const renderBounds = {
7600
7111
  return [SetBounds, x, y, resizedWidth, height];
7601
7112
  }
7602
7113
  };
7603
- const render$1 = [renderHoverDom, renderBounds];
7114
+ const render$3 = [renderHoverDom, renderBounds$1];
7604
7115
  const renderHover = async (oldState, newState) => {
7605
7116
  const commands = [];
7606
- for (const item of render$1) {
7117
+ for (const item of render$3) {
7607
7118
  if (!item.isEqual(oldState, newState)) {
7608
7119
  commands.push(item.apply(oldState, newState));
7609
7120
  }
@@ -7909,7 +7420,7 @@ const moveLineUp = editor => {
7909
7420
  };
7910
7421
 
7911
7422
  const Link$1 = 'Link';
7912
- const Function = 'Function';
7423
+ const Function$1 = 'Function';
7913
7424
  const Parameter = 'Parameter';
7914
7425
  const Type = 'Type';
7915
7426
  const VariableName = 'VariableName';
@@ -7968,7 +7479,7 @@ const getDecorationClassName = type => {
7968
7479
  case Ts3073:
7969
7480
  case Ts3077:
7970
7481
  case Ts3088:
7971
- return Function;
7482
+ return Function$1;
7972
7483
  case Ts1792:
7973
7484
  case Ts1793:
7974
7485
  return Parameter;
@@ -8004,521 +7515,872 @@ const getDecorationClassName = type => {
8004
7515
  return `Unknown-${type}`;
8005
7516
  }
8006
7517
  };
8007
-
8008
- const getTokensViewportEmbedded = (langageId, lines, lineCache, linesWithEmbed) => {
8009
- const tokenizersToLoad = [];
8010
- const embeddedResults = [];
8011
- let topContext;
8012
- for (const index of linesWithEmbed) {
8013
- const result = lineCache[index + 1];
8014
- const line = lines[index];
8015
- if (result.embeddedLanguage) {
8016
- const {
8017
- embeddedLanguage,
8018
- embeddedLanguageStart,
8019
- embeddedLanguageEnd
8020
- } = result;
8021
- const embeddedTokenizer = getTokenizer(embeddedLanguage);
8022
- if (embeddedLanguageStart !== line.length && embeddedTokenizer && embeddedTokenizer !== TokenizePlainText) {
8023
- const isFull = embeddedLanguageStart === 0 && embeddedLanguageEnd === line.length;
8024
- const partialLine = line.slice(embeddedLanguageStart, embeddedLanguageEnd);
8025
- const embedResult = safeTokenizeLine(langageId, embeddedTokenizer.tokenizeLine, partialLine, topContext || getInitialLineState(embeddedTokenizer.initialLineState), embeddedTokenizer.hasArrayReturn);
8026
- topContext = embedResult;
8027
- result.embeddedResultIndex = embeddedResults.length;
8028
- embeddedResults.push({
8029
- result: embedResult,
8030
- TokenMap: embeddedTokenizer.TokenMap,
8031
- isFull
8032
- });
8033
- } else if (line.length === 0) {
8034
- const embedResult = {
8035
- tokens: []
8036
- };
8037
- result.embeddedResultIndex = embeddedResults.length;
8038
- embeddedResults.push({
8039
- result: embedResult,
8040
- isFull: true,
8041
- TokenMap: []
8042
- });
8043
- } else {
8044
- tokenizersToLoad.push(embeddedLanguage);
8045
- embeddedResults.push({
8046
- result: {},
8047
- isFull: false,
8048
- TokenMap: []
8049
- });
8050
- topContext = undefined;
8051
- }
8052
- } else {
8053
- topContext = undefined;
7518
+
7519
+ const getTokensViewportEmbedded = (langageId, lines, lineCache, linesWithEmbed) => {
7520
+ const tokenizersToLoad = [];
7521
+ const embeddedResults = [];
7522
+ let topContext;
7523
+ for (const index of linesWithEmbed) {
7524
+ const result = lineCache[index + 1];
7525
+ const line = lines[index];
7526
+ if (result.embeddedLanguage) {
7527
+ const {
7528
+ embeddedLanguage,
7529
+ embeddedLanguageStart,
7530
+ embeddedLanguageEnd
7531
+ } = result;
7532
+ const embeddedTokenizer = getTokenizer(embeddedLanguage);
7533
+ if (embeddedLanguageStart !== line.length && embeddedTokenizer && embeddedTokenizer !== TokenizePlainText) {
7534
+ const isFull = embeddedLanguageStart === 0 && embeddedLanguageEnd === line.length;
7535
+ const partialLine = line.slice(embeddedLanguageStart, embeddedLanguageEnd);
7536
+ const embedResult = safeTokenizeLine(langageId, embeddedTokenizer.tokenizeLine, partialLine, topContext || getInitialLineState(embeddedTokenizer.initialLineState), embeddedTokenizer.hasArrayReturn);
7537
+ topContext = embedResult;
7538
+ result.embeddedResultIndex = embeddedResults.length;
7539
+ embeddedResults.push({
7540
+ result: embedResult,
7541
+ TokenMap: embeddedTokenizer.TokenMap,
7542
+ isFull
7543
+ });
7544
+ } else if (line.length === 0) {
7545
+ const embedResult = {
7546
+ tokens: []
7547
+ };
7548
+ result.embeddedResultIndex = embeddedResults.length;
7549
+ embeddedResults.push({
7550
+ result: embedResult,
7551
+ isFull: true,
7552
+ TokenMap: []
7553
+ });
7554
+ } else {
7555
+ tokenizersToLoad.push(embeddedLanguage);
7556
+ embeddedResults.push({
7557
+ result: {},
7558
+ isFull: false,
7559
+ TokenMap: []
7560
+ });
7561
+ topContext = undefined;
7562
+ }
7563
+ } else {
7564
+ topContext = undefined;
7565
+ }
7566
+ }
7567
+ return {
7568
+ tokenizersToLoad,
7569
+ embeddedResults
7570
+ };
7571
+ };
7572
+ const getTokenizeEndIndex = (invalidStartIndex, endLineIndex, tokenizeStartIndex) => {
7573
+ return invalidStartIndex < endLineIndex ? endLineIndex : tokenizeStartIndex;
7574
+ };
7575
+
7576
+ // TODO only send changed lines to renderer process instead of all lines in viewport
7577
+ const getTokensViewport = (editor, startLineIndex, endLineIndex) => {
7578
+ const {
7579
+ invalidStartIndex,
7580
+ lineCache,
7581
+ tokenizerId,
7582
+ lines,
7583
+ languageId
7584
+ } = editor;
7585
+ const tokenizer = get(tokenizerId);
7586
+ const {
7587
+ hasArrayReturn,
7588
+ tokenizeLine,
7589
+ initialLineState
7590
+ } = tokenizer;
7591
+ const tokenizeStartIndex = invalidStartIndex;
7592
+ const tokenizeEndIndex = getTokenizeEndIndex(invalidStartIndex, endLineIndex, tokenizeStartIndex);
7593
+ const tokenizersToLoad = [];
7594
+ const embeddedResults = [];
7595
+ const linesWithEmbed = [];
7596
+ for (let i = tokenizeStartIndex; i < tokenizeEndIndex; i++) {
7597
+ const lineState = i === 0 ? getInitialLineState(initialLineState) : lineCache[i];
7598
+ const line = lines[i];
7599
+ const result = safeTokenizeLine(languageId, tokenizeLine, line, lineState, hasArrayReturn);
7600
+ // TODO if lineCacheEnd matches the one before, skip tokenizing lines after
7601
+ lineCache[i + 1] = result;
7602
+ if (result.embeddedLanguage) {
7603
+ result.embeddedResultIndex = linesWithEmbed.length;
7604
+ linesWithEmbed.push(i);
7605
+ }
7606
+ }
7607
+ const visibleLines = lineCache.slice(startLineIndex + 1, endLineIndex + 1);
7608
+ if (linesWithEmbed.length > 0) {
7609
+ const {
7610
+ tokenizersToLoad,
7611
+ embeddedResults
7612
+ } = getTokensViewportEmbedded(languageId, lines, lineCache, linesWithEmbed);
7613
+ // TODO support lineCache with embedded content
7614
+ editor.invalidStartIndex = 0;
7615
+ return {
7616
+ tokens: visibleLines,
7617
+ tokenizersToLoad,
7618
+ embeddedResults
7619
+ };
7620
+ }
7621
+ editor.invalidStartIndex = Math.max(invalidStartIndex, tokenizeEndIndex);
7622
+ return {
7623
+ tokens: visibleLines,
7624
+ tokenizersToLoad,
7625
+ embeddedResults
7626
+ };
7627
+ };
7628
+
7629
+ const sentLines = Object.create(null);
7630
+
7631
+ // TODO only send changed lines to renderer process instead of all lines in viewport
7632
+ const getTokensViewport2 = (editor, startLineIndex, endLineIndex, syncIncremental) => {
7633
+ if (getEnabled$1()) {
7634
+ if (syncIncremental) {
7635
+ const {
7636
+ invalidStartIndex,
7637
+ lines,
7638
+ languageId,
7639
+ id
7640
+ } = editor;
7641
+ let hasLinesToSend = true;
7642
+ let linesToSend = lines;
7643
+ if (sentLines[id] === lines) {
7644
+ hasLinesToSend = false;
7645
+ linesToSend = [];
7646
+ } else {
7647
+ sentLines[id] = lines;
7648
+ }
7649
+ const slimEditor = {
7650
+ languageId,
7651
+ invalidStartIndex
7652
+ };
7653
+ return invoke$1('GetTokensViewport.getTokensViewport', slimEditor, startLineIndex, endLineIndex, hasLinesToSend, id, linesToSend);
7654
+ }
7655
+ // TODO only send needed lines of text
7656
+ return invoke$1('GetTokensViewport.getTokensViewport', editor, startLineIndex, endLineIndex, true, editor.id, editor.lines);
7657
+ }
7658
+ return getTokensViewport(editor, startLineIndex, endLineIndex);
7659
+ };
7660
+
7661
+ const loadTokenizers = async languageIds => {
7662
+ for (const languageId of languageIds) {
7663
+ // @ts-ignore
7664
+ await loadTokenizer(languageId);
7665
+ }
7666
+ };
7667
+
7668
+ // const getTokensIncremental = (editor, min, max) => {
7669
+ // const currentLength = editor.lineStateCache.length
7670
+ // const tokens = []
7671
+ // const lines = editor.lines
7672
+ // let lineState = editor.tokenizer.initialLineState
7673
+ // for (let i = currentLength; i < max; i++) {
7674
+ // const line = lines[i]
7675
+ // try {
7676
+ // lineState = editor.tokenizer.tokenizeLine(line, lineState)
7677
+ // if (!lineState || !lineState.tokens || !lineState.state) {
7678
+ // throw new Error('invalid tokenization result')
7679
+ // }
7680
+ // } catch (error) {
7681
+ // tokens.push([{ length: line.length, type: 0 }])
7682
+ // console.error(error)
7683
+ // // renderWithoutSyntaxHighlighting(state, firstRow, lastRow)
7684
+ // continue
7685
+ // }
7686
+ // const newTokens = lineState.tokens
7687
+ // tokens.push(newTokens)
7688
+ // }
7689
+ // return tokens
7690
+ // }
7691
+
7692
+ // const getLineInfosIncremental = (editor, tokens, minLineY, maxLineY) => {
7693
+ // const result = []
7694
+ // const lines = editor.lines
7695
+ // const TokenMap = editor.tokenizer.TokenMap
7696
+ // for (let i = minLineY; i < maxLineY; i++) {
7697
+ // result.push(getLineInfo(lines[i], tokens[i], TokenMap))
7698
+ // }
7699
+ // return result
7700
+ // }
7701
+
7702
+ const getStartDefaults = (tokens, minOffset) => {
7703
+ let start = 0;
7704
+ let end = 0;
7705
+ let startIndex = 0;
7706
+ const tokensLength = tokens.length;
7707
+ for (let i = 0; i < tokensLength; i += 2) {
7708
+ const tokenLength = tokens[i + 1];
7709
+ end += tokenLength;
7710
+ start = end;
7711
+ if (start >= minOffset) {
7712
+ start -= tokenLength;
7713
+ end -= tokenLength;
7714
+ startIndex = i;
7715
+ break;
7716
+ }
7717
+ }
7718
+ return {
7719
+ start,
7720
+ end,
7721
+ startIndex
7722
+ };
7723
+ };
7724
+ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
7725
+ const lineInfo = [];
7726
+ const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
7727
+ const embeddedTokens = embeddedResult.result.tokens;
7728
+ const embeddedTokenMap = embeddedResult.TokenMap;
7729
+ const tokensLength = embeddedTokens.length;
7730
+ let {
7731
+ startIndex,
7732
+ start,
7733
+ end
7734
+ } = getStartDefaults(embeddedTokens, minOffset);
7735
+ const difference = getDifference(start, averageCharWidth, deltaX);
7736
+ for (let i = startIndex; i < tokensLength; i += 2) {
7737
+ const tokenType = embeddedTokens[i];
7738
+ const tokenLength = embeddedTokens[i + 1];
7739
+ end += tokenLength;
7740
+ const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
7741
+ const text = line.slice(start, end);
7742
+ const normalizedText = normalizeText(text, normalize, tabSize);
7743
+ lineInfo.push(normalizedText, className);
7744
+ start = end;
7745
+ if (end >= maxOffset) {
7746
+ break;
8054
7747
  }
8055
7748
  }
8056
7749
  return {
8057
- tokenizersToLoad,
8058
- embeddedResults
7750
+ lineInfo,
7751
+ difference
8059
7752
  };
8060
7753
  };
8061
- const getTokenizeEndIndex = (invalidStartIndex, endLineIndex, tokenizeStartIndex) => {
8062
- return invalidStartIndex < endLineIndex ? endLineIndex : tokenizeStartIndex;
7754
+ const getOffsets = (deltaX, width, averageCharWidth) => {
7755
+ // TODO accurately measure char widths using offscreen canvas
7756
+ // and use fast measurements for monospace ascii text
7757
+ if (deltaX === 0) {
7758
+ return {
7759
+ minOffset: 0,
7760
+ maxOffset: Math.ceil(width / averageCharWidth)
7761
+ };
7762
+ }
7763
+ const minOffset = Math.ceil(deltaX / averageCharWidth);
7764
+ const maxOffset = minOffset + Math.ceil(width / averageCharWidth);
7765
+ return {
7766
+ minOffset,
7767
+ maxOffset
7768
+ };
7769
+ };
7770
+ const getDifference = (start, averageCharWidth, deltaX) => {
7771
+ const beforeWidth = start * averageCharWidth;
7772
+ const difference = beforeWidth - deltaX;
7773
+ return difference;
7774
+ };
7775
+ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
7776
+ const lineInfo = [];
7777
+ let decorationIndex = 0;
7778
+ for (; decorationIndex < decorations.length; decorationIndex += 3) {
7779
+ const decorationOffset = decorations[decorationIndex];
7780
+ if (decorationOffset >= lineOffset) {
7781
+ break;
7782
+ }
7783
+ }
7784
+ const tokens = tokenResults.tokens;
7785
+ let {
7786
+ startIndex,
7787
+ start,
7788
+ end
7789
+ } = getStartDefaults(tokens, minOffset);
7790
+ const difference = getDifference(start, averageCharWidth, deltaX);
7791
+ const tokensLength = tokens.length;
7792
+ for (let i = startIndex; i < tokensLength; i += 2) {
7793
+ const tokenType = tokens[i];
7794
+ const tokenLength = tokens[i + 1];
7795
+ const decorationOffset = decorations[decorationIndex];
7796
+ let extraClassName = '';
7797
+ if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
7798
+ // @ts-ignore
7799
+ decorations[++decorationIndex];
7800
+ const decorationType = decorations[++decorationIndex];
7801
+ // @ts-ignore
7802
+ decorations[++decorationIndex];
7803
+ // console.log('MATCHING DECORATION', {
7804
+ // decorationIndex,
7805
+ // decorationLength,
7806
+ // decorationType,
7807
+ // decorationModifiers,
7808
+ // })
7809
+ extraClassName = getDecorationClassName(decorationType);
7810
+ }
7811
+ end += tokenLength;
7812
+ const text = line.slice(start, end);
7813
+ const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
7814
+ const normalizedText = normalizeText(text, normalize, tabSize);
7815
+ lineInfo.push(normalizedText, className);
7816
+ start = end;
7817
+ if (end >= maxOffset) {
7818
+ break;
7819
+ }
7820
+ }
7821
+ return {
7822
+ lineInfo,
7823
+ difference
7824
+ };
7825
+ };
7826
+ const getLineInfo = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
7827
+ const {
7828
+ minOffset,
7829
+ maxOffset
7830
+ } = getOffsets(deltaX, width, averageCharWidth);
7831
+ if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
7832
+ const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
7833
+ if (embeddedResult && embeddedResult.isFull) {
7834
+ return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
7835
+ }
7836
+ }
7837
+ return getLineInfoDefault(line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8063
7838
  };
8064
7839
 
8065
- // TODO only send changed lines to renderer process instead of all lines in viewport
8066
- const getTokensViewport = (editor, startLineIndex, endLineIndex) => {
7840
+ // TODO need lots of tests for this
7841
+ const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
7842
+ const result = [];
7843
+ const differences = [];
8067
7844
  const {
8068
- invalidStartIndex,
8069
- lineCache,
8070
- tokenizerId,
8071
7845
  lines,
7846
+ decorations,
8072
7847
  languageId
8073
7848
  } = editor;
8074
- const tokenizer = get(tokenizerId);
8075
- const {
8076
- hasArrayReturn,
8077
- tokenizeLine,
8078
- initialLineState
8079
- } = tokenizer;
8080
- const tokenizeStartIndex = invalidStartIndex;
8081
- const tokenizeEndIndex = getTokenizeEndIndex(invalidStartIndex, endLineIndex, tokenizeStartIndex);
8082
- const tokenizersToLoad = [];
8083
- const embeddedResults = [];
8084
- const linesWithEmbed = [];
8085
- for (let i = tokenizeStartIndex; i < tokenizeEndIndex; i++) {
8086
- const lineState = i === 0 ? getInitialLineState(initialLineState) : lineCache[i];
7849
+ const tokenMap = get$1(languageId);
7850
+ let offset = minLineOffset;
7851
+ const tabSize = 2;
7852
+ for (let i = minLineY; i < maxLineY; i++) {
8087
7853
  const line = lines[i];
8088
- const result = safeTokenizeLine(languageId, tokenizeLine, line, lineState, hasArrayReturn);
8089
- // TODO if lineCacheEnd matches the one before, skip tokenizing lines after
8090
- lineCache[i + 1] = result;
8091
- if (result.embeddedLanguage) {
8092
- result.embeddedResultIndex = linesWithEmbed.length;
8093
- linesWithEmbed.push(i);
8094
- }
8095
- }
8096
- const visibleLines = lineCache.slice(startLineIndex + 1, endLineIndex + 1);
8097
- if (linesWithEmbed.length > 0) {
7854
+ const normalize = shouldNormalizeText(line);
8098
7855
  const {
8099
- tokenizersToLoad,
8100
- embeddedResults
8101
- } = getTokensViewportEmbedded(languageId, lines, lineCache, linesWithEmbed);
8102
- // TODO support lineCache with embedded content
8103
- editor.invalidStartIndex = 0;
8104
- return {
8105
- tokens: visibleLines,
8106
- tokenizersToLoad,
8107
- embeddedResults
8108
- };
7856
+ lineInfo,
7857
+ difference
7858
+ } = getLineInfo(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
7859
+ result.push(lineInfo);
7860
+ differences.push(difference);
7861
+ offset += line.length + 1;
8109
7862
  }
8110
- editor.invalidStartIndex = Math.max(invalidStartIndex, tokenizeEndIndex);
8111
7863
  return {
8112
- tokens: visibleLines,
7864
+ result,
7865
+ differences
7866
+ };
7867
+ };
7868
+ const getVisible = async (editor, syncIncremental) => {
7869
+ // console.log({ editor })
7870
+ // TODO should separate rendering from business logic somehow
7871
+ // currently hard to test because need to mock editor height, top, left,
7872
+ // invalidStartIndex, lineCache, etc. just for testing editorType
7873
+ // editor.invalidStartIndex = changes[0].start.rowIndex
7874
+ // @ts-ignore
7875
+ const {
7876
+ minLineY,
7877
+ numberOfVisibleLines,
7878
+ lines,
7879
+ width,
7880
+ deltaX,
7881
+ fontWeight,
7882
+ fontSize,
7883
+ fontFamily,
7884
+ letterSpacing,
7885
+ charWidth
7886
+ } = editor;
7887
+ const maxLineY = Math.min(minLineY + numberOfVisibleLines, lines.length);
7888
+ const {
7889
+ tokens,
8113
7890
  tokenizersToLoad,
8114
7891
  embeddedResults
7892
+ } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
7893
+ const minLineOffset = offsetAtSync(editor, minLineY, 0);
7894
+ const averageCharWidth = charWidth;
7895
+ const {
7896
+ result,
7897
+ differences
7898
+ } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
7899
+ if (tokenizersToLoad.length > 0) {
7900
+ loadTokenizers(tokenizersToLoad);
7901
+ }
7902
+ return {
7903
+ textInfos: result,
7904
+ differences
8115
7905
  };
8116
7906
  };
8117
7907
 
8118
- const sentLines = Object.create(null);
8119
-
8120
- // TODO only send changed lines to renderer process instead of all lines in viewport
8121
- const getTokensViewport2 = (editor, startLineIndex, endLineIndex, syncIncremental) => {
8122
- if (getEnabled$1()) {
8123
- if (syncIncremental) {
8124
- const {
8125
- invalidStartIndex,
8126
- lines,
8127
- languageId,
8128
- id
8129
- } = editor;
8130
- let hasLinesToSend = true;
8131
- let linesToSend = lines;
8132
- if (sentLines[id] === lines) {
8133
- hasLinesToSend = false;
8134
- linesToSend = [];
8135
- } else {
8136
- sentLines[id] = lines;
8137
- }
8138
- const slimEditor = {
8139
- languageId,
8140
- invalidStartIndex
8141
- };
8142
- return invoke$1('GetTokensViewport.getTokensViewport', slimEditor, startLineIndex, endLineIndex, hasLinesToSend, id, linesToSend);
8143
- }
8144
- // TODO only send needed lines of text
8145
- return invoke$1('GetTokensViewport.getTokensViewport', editor, startLineIndex, endLineIndex, true, editor.id, editor.lines);
7908
+ const getCursorsVirtualDom = cursors => {
7909
+ const dom = [];
7910
+ for (const translate of cursors) {
7911
+ dom.push({
7912
+ type: Div,
7913
+ className: EditorCursor,
7914
+ translate
7915
+ });
8146
7916
  }
8147
- return getTokensViewport(editor, startLineIndex, endLineIndex);
7917
+ return dom;
8148
7918
  };
8149
7919
 
8150
- const loadTokenizers = async languageIds => {
8151
- for (const languageId of languageIds) {
8152
- // @ts-ignore
8153
- await loadTokenizer(languageId);
7920
+ const Error$1 = 'error';
7921
+ const Warning = 'warning';
7922
+
7923
+ const getDiagnosticClassName = type => {
7924
+ // TODO use classnames enum
7925
+ switch (type) {
7926
+ case Error$1:
7927
+ return 'DiagnosticError';
7928
+ case Warning:
7929
+ return 'DiagnosticWarning';
7930
+ default:
7931
+ return 'DiagnosticError';
8154
7932
  }
8155
7933
  };
7934
+ const getDiagnosticVirtualDom = diagnostic => {
7935
+ const {
7936
+ x,
7937
+ y,
7938
+ width,
7939
+ height,
7940
+ type
7941
+ } = diagnostic;
7942
+ const extraClassName = getDiagnosticClassName(type);
7943
+ return [{
7944
+ type: Div,
7945
+ className: `${Diagnostic} ${extraClassName}`,
7946
+ width,
7947
+ height,
7948
+ top: y,
7949
+ left: x,
7950
+ childCount: 0
7951
+ }];
7952
+ };
8156
7953
 
8157
- // const getTokensIncremental = (editor, min, max) => {
8158
- // const currentLength = editor.lineStateCache.length
8159
- // const tokens = []
8160
- // const lines = editor.lines
8161
- // let lineState = editor.tokenizer.initialLineState
8162
- // for (let i = currentLength; i < max; i++) {
8163
- // const line = lines[i]
8164
- // try {
8165
- // lineState = editor.tokenizer.tokenizeLine(line, lineState)
8166
- // if (!lineState || !lineState.tokens || !lineState.state) {
8167
- // throw new Error('invalid tokenization result')
8168
- // }
8169
- // } catch (error) {
8170
- // tokens.push([{ length: line.length, type: 0 }])
8171
- // console.error(error)
8172
- // // renderWithoutSyntaxHighlighting(state, firstRow, lastRow)
8173
- // continue
8174
- // }
8175
- // const newTokens = lineState.tokens
8176
- // tokens.push(newTokens)
8177
- // }
8178
- // return tokens
8179
- // }
7954
+ const getDiagnosticsVirtualDom = diagnostics => {
7955
+ const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
7956
+ return dom;
7957
+ };
8180
7958
 
8181
- // const getLineInfosIncremental = (editor, tokens, minLineY, maxLineY) => {
8182
- // const result = []
8183
- // const lines = editor.lines
8184
- // const TokenMap = editor.tokenizer.TokenMap
8185
- // for (let i = minLineY; i < maxLineY; i++) {
8186
- // result.push(getLineInfo(lines[i], tokens[i], TokenMap))
8187
- // }
8188
- // return result
8189
- // }
7959
+ const getGutterInfoVirtualDom = gutterInfo => {
7960
+ return [{
7961
+ type: Span,
7962
+ className: 'LineNumber',
7963
+ childCount: 1
7964
+ }, text(gutterInfo)];
7965
+ };
7966
+ const getEditorGutterVirtualDom = gutterInfos => {
7967
+ const dom = gutterInfos.flatMap(getGutterInfoVirtualDom);
7968
+ return dom;
7969
+ };
8190
7970
 
8191
- const getStartDefaults = (tokens, minOffset) => {
8192
- let start = 0;
8193
- let end = 0;
8194
- let startIndex = 0;
8195
- const tokensLength = tokens.length;
8196
- for (let i = 0; i < tokensLength; i += 2) {
8197
- const tokenLength = tokens[i + 1];
8198
- end += tokenLength;
8199
- start = end;
8200
- if (start >= minOffset) {
8201
- start -= tokenLength;
8202
- end -= tokenLength;
8203
- startIndex = i;
8204
- break;
7971
+ const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true) => {
7972
+ const dom = [];
7973
+ for (let i = 0; i < textInfos.length; i++) {
7974
+ const textInfo = textInfos[i];
7975
+ const difference = differences[i];
7976
+ dom.push({
7977
+ type: Div,
7978
+ className: EditorRow,
7979
+ translate: px(difference),
7980
+ childCount: textInfo.length / 2
7981
+ });
7982
+ for (let j = 0; j < textInfo.length; j += 2) {
7983
+ const tokenText = textInfo[j];
7984
+ const className = textInfo[j + 1];
7985
+ dom.push({
7986
+ type: Span,
7987
+ className,
7988
+ childCount: 1
7989
+ }, text(tokenText));
8205
7990
  }
8206
7991
  }
8207
- return {
8208
- start,
8209
- end,
8210
- startIndex
8211
- };
7992
+ return dom;
8212
7993
  };
8213
- const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
8214
- const lineInfo = [];
8215
- const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
8216
- const embeddedTokens = embeddedResult.result.tokens;
8217
- const embeddedTokenMap = embeddedResult.TokenMap;
8218
- const tokensLength = embeddedTokens.length;
8219
- let {
8220
- startIndex,
8221
- start,
8222
- end
8223
- } = getStartDefaults(embeddedTokens, minOffset);
8224
- const difference = getDifference(start, averageCharWidth, deltaX);
8225
- for (let i = startIndex; i < tokensLength; i += 2) {
8226
- const tokenType = embeddedTokens[i];
8227
- const tokenLength = embeddedTokens[i + 1];
8228
- end += tokenLength;
8229
- const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
8230
- const text = line.slice(start, end);
8231
- const normalizedText = normalizeText(text, normalize, tabSize);
8232
- lineInfo.push(normalizedText, className);
8233
- start = end;
8234
- if (end >= maxOffset) {
8235
- break;
7994
+
7995
+ const getIncrementalEdits = async (oldState, newState) => {
7996
+ if (!newState.undoStack) {
7997
+ return undefined;
7998
+ }
7999
+ const lastChanges = newState.undoStack.at(-1);
8000
+ if (lastChanges && lastChanges.length === 1) {
8001
+ const lastChange = lastChanges[0];
8002
+ if (lastChange.origin === EditorType) {
8003
+ const rowIndex = lastChange.start.rowIndex;
8004
+ const lines = newState.lines;
8005
+ const oldLine = oldState.lines[rowIndex];
8006
+ const newLine = lines[rowIndex];
8007
+ const incrementalEdits = await invoke$1('TokenizeIncremental.tokenizeIncremental', newState.uid, newState.languageId, oldLine, newLine, rowIndex, newState.minLineY);
8008
+ if (incrementalEdits && incrementalEdits.length === 1) {
8009
+ return incrementalEdits;
8010
+ }
8236
8011
  }
8237
8012
  }
8013
+ return undefined;
8014
+ };
8015
+
8016
+ const getSelectionsVirtualDom = selections => {
8017
+ const dom = [];
8018
+ for (let i = 0; i < selections.length; i += 4) {
8019
+ const x = selections[i];
8020
+ const y = selections[i + 1];
8021
+ const width = selections[i + 2];
8022
+ const height = selections[i + 3];
8023
+ dom.push({
8024
+ type: Div,
8025
+ className: EditorSelection,
8026
+ left: x,
8027
+ top: y,
8028
+ width,
8029
+ height
8030
+ });
8031
+ }
8032
+ return dom;
8033
+ };
8034
+
8035
+ const None = 'none';
8036
+ const Option = 'option';
8037
+
8038
+ const getFileIconVirtualDom = icon => {
8238
8039
  return {
8239
- lineInfo,
8240
- difference
8040
+ type: Img,
8041
+ className: FileIcon,
8042
+ src: icon,
8043
+ role: None,
8044
+ childCount: 0
8241
8045
  };
8242
8046
  };
8243
- const getOffsets = (deltaX, width, averageCharWidth) => {
8244
- // TODO accurately measure char widths using offscreen canvas
8245
- // and use fast measurements for monospace ascii text
8246
- if (deltaX === 0) {
8247
- return {
8248
- minOffset: 0,
8249
- maxOffset: Math.ceil(width / averageCharWidth)
8250
- };
8047
+
8048
+ const getIconDom = (fileIcon, symbolName) => {
8049
+ if (fileIcon) {
8050
+ return getFileIconVirtualDom(fileIcon);
8251
8051
  }
8252
- const minOffset = Math.ceil(deltaX / averageCharWidth);
8253
- const maxOffset = minOffset + Math.ceil(width / averageCharWidth);
8254
8052
  return {
8255
- minOffset,
8256
- maxOffset
8053
+ type: Div,
8054
+ className: `${ColoredMaskIcon} ${symbolName}`,
8055
+ childCount: 0
8257
8056
  };
8258
8057
  };
8259
- const getDifference = (start, averageCharWidth, deltaX) => {
8260
- const beforeWidth = start * averageCharWidth;
8261
- const difference = beforeWidth - deltaX;
8262
- return difference;
8058
+
8059
+ const label1 = {
8060
+ type: Div,
8061
+ className: Label,
8062
+ childCount: 1
8263
8063
  };
8264
- const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
8265
- const lineInfo = [];
8266
- let decorationIndex = 0;
8267
- for (; decorationIndex < decorations.length; decorationIndex += 3) {
8268
- const decorationOffset = decorations[decorationIndex];
8269
- if (decorationOffset >= lineOffset) {
8270
- break;
8271
- }
8064
+ const completionHighlight = {
8065
+ type: Span,
8066
+ className: EditorCompletionItemHighlight,
8067
+ childCount: 1
8068
+ };
8069
+ const getHighlightedLabelDom = (label, highlights) => {
8070
+ if (highlights.length === 0) {
8071
+ return [label1, text(label)];
8272
8072
  }
8273
- const tokens = tokenResults.tokens;
8274
- let {
8275
- startIndex,
8276
- start,
8277
- end
8278
- } = getStartDefaults(tokens, minOffset);
8279
- const difference = getDifference(start, averageCharWidth, deltaX);
8280
- const tokensLength = tokens.length;
8281
- for (let i = startIndex; i < tokensLength; i += 2) {
8282
- const tokenType = tokens[i];
8283
- const tokenLength = tokens[i + 1];
8284
- const decorationOffset = decorations[decorationIndex];
8285
- let extraClassName = '';
8286
- if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
8287
- // @ts-ignore
8288
- decorations[++decorationIndex];
8289
- const decorationType = decorations[++decorationIndex];
8290
- // @ts-ignore
8291
- decorations[++decorationIndex];
8292
- // console.log('MATCHING DECORATION', {
8293
- // decorationIndex,
8294
- // decorationLength,
8295
- // decorationType,
8296
- // decorationModifiers,
8297
- // })
8298
- extraClassName = getDecorationClassName(decorationType);
8299
- }
8300
- end += tokenLength;
8301
- const text = line.slice(start, end);
8302
- const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
8303
- const normalizedText = normalizeText(text, normalize, tabSize);
8304
- lineInfo.push(normalizedText, className);
8305
- start = end;
8306
- if (end >= maxOffset) {
8307
- break;
8073
+ const dom = [];
8074
+ const labelDom = {
8075
+ type: Div,
8076
+ className: Label,
8077
+ childCount: 0
8078
+ };
8079
+ dom.push(labelDom);
8080
+ let position = 0;
8081
+ for (let i = 0; i < highlights.length; i += 2) {
8082
+ const highlightStart = highlights[i];
8083
+ const highlightEnd = highlights[i + 1];
8084
+ if (position < highlightStart) {
8085
+ const beforeText = label.slice(position, highlightStart);
8086
+ labelDom.childCount++;
8087
+ dom.push(text(beforeText));
8308
8088
  }
8089
+ const highlightText = label.slice(highlightStart, highlightEnd);
8090
+ labelDom.childCount++;
8091
+ dom.push(completionHighlight, text(highlightText));
8092
+ position = highlightEnd;
8309
8093
  }
8310
- return {
8311
- lineInfo,
8312
- difference
8313
- };
8094
+ if (position < label.length) {
8095
+ const afterText = label.slice(position);
8096
+ labelDom.childCount++;
8097
+ dom.push(text(afterText));
8098
+ }
8099
+ return dom;
8314
8100
  };
8315
- const getLineInfo = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth) => {
8101
+
8102
+ const getCompletionItemVirtualDom = visibleItem => {
8316
8103
  const {
8317
- minOffset,
8318
- maxOffset
8319
- } = getOffsets(deltaX, width, averageCharWidth);
8320
- if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
8321
- const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
8322
- if (embeddedResult && embeddedResult.isFull) {
8323
- return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8324
- }
8104
+ top,
8105
+ label,
8106
+ symbolName,
8107
+ highlights,
8108
+ focused,
8109
+ deprecated,
8110
+ fileIcon
8111
+ } = visibleItem;
8112
+ let className = EditorCompletionItem;
8113
+ if (focused) {
8114
+ className += ' ' + EditorCompletionItemFocused;
8325
8115
  }
8326
- return getLineInfoDefault(line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
8116
+ if (deprecated) {
8117
+ className += ' ' + EditorCompletionItemDeprecated;
8118
+ }
8119
+ return [{
8120
+ type: Div,
8121
+ role: Option,
8122
+ className,
8123
+ top,
8124
+ childCount: 2
8125
+ }, getIconDom(fileIcon, symbolName), ...getHighlightedLabelDom(label, highlights)];
8327
8126
  };
8328
8127
 
8329
- // TODO need lots of tests for this
8330
- const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth) => {
8331
- const result = [];
8332
- const differences = [];
8333
- const {
8334
- lines,
8335
- decorations,
8336
- languageId
8337
- } = editor;
8338
- const tokenMap = get$1(languageId);
8339
- let offset = minLineOffset;
8340
- const tabSize = 2;
8341
- for (let i = minLineY; i < maxLineY; i++) {
8342
- const line = lines[i];
8343
- const normalize = shouldNormalizeText(line);
8344
- const {
8345
- lineInfo,
8346
- difference
8347
- } = getLineInfo(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
8348
- result.push(lineInfo);
8349
- differences.push(difference);
8350
- offset += line.length + 1;
8128
+ const getCompletionItemsVirtualDom = visibleItems => {
8129
+ if (visibleItems.length === 0) {
8130
+ return [{
8131
+ type: Div,
8132
+ childCount: 1
8133
+ }, text(noResults())];
8351
8134
  }
8352
- return {
8353
- result,
8354
- differences
8135
+ const root = {
8136
+ type: Div,
8137
+ childCount: visibleItems.length
8355
8138
  };
8139
+ const dom = [root, ...visibleItems.flatMap(getCompletionItemVirtualDom)];
8140
+ return dom;
8356
8141
  };
8357
- const getVisible = async (editor, syncIncremental) => {
8358
- // console.log({ editor })
8359
- // TODO should separate rendering from business logic somehow
8360
- // currently hard to test because need to mock editor height, top, left,
8361
- // invalidStartIndex, lineCache, etc. just for testing editorType
8362
- // editor.invalidStartIndex = changes[0].start.rowIndex
8363
- // @ts-ignore
8364
- const {
8365
- minLineY,
8366
- numberOfVisibleLines,
8367
- lines,
8368
- width,
8369
- deltaX,
8370
- fontWeight,
8371
- fontSize,
8372
- fontFamily,
8373
- letterSpacing,
8374
- charWidth
8375
- } = editor;
8376
- const maxLineY = Math.min(minLineY + numberOfVisibleLines, lines.length);
8377
- const {
8378
- tokens,
8379
- tokenizersToLoad,
8380
- embeddedResults
8381
- } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
8382
- const minLineOffset = offsetAtSync(editor, minLineY, 0);
8383
- const averageCharWidth = charWidth;
8384
- const {
8385
- result,
8386
- differences
8387
- } = getLineInfosViewport(editor, tokens, embeddedResults, minLineY, maxLineY, minLineOffset, width, deltaX, averageCharWidth);
8388
- if (tokenizersToLoad.length > 0) {
8389
- loadTokenizers(tokenizersToLoad);
8142
+
8143
+ const Property = 1;
8144
+ const Value = 2;
8145
+ const Function = 3;
8146
+ const Variable = 4;
8147
+ const Keyword = 5;
8148
+ const Folder = 6;
8149
+ const File = 7;
8150
+ const Field = 8;
8151
+
8152
+ const SymbolProperty = 'SymbolProperty';
8153
+ const SymbolValue = 'SymbolValue';
8154
+ const SymbolFunction = 'SymbolFunction';
8155
+ const SymbolVariable = 'SymbolVariable';
8156
+ const SymbolKeyword = 'SymbolKeyword';
8157
+ const SymbolDefault = 'SymbolDefault';
8158
+ const SymbolField = 'SymbolField';
8159
+ const SymbolNone = '';
8160
+
8161
+ const getSymbolName = kind => {
8162
+ switch (kind) {
8163
+ case Property:
8164
+ return SymbolProperty;
8165
+ case Value:
8166
+ return SymbolValue;
8167
+ case Function:
8168
+ return SymbolFunction;
8169
+ case Variable:
8170
+ return SymbolVariable;
8171
+ case Keyword:
8172
+ return SymbolKeyword;
8173
+ case Field:
8174
+ return SymbolField;
8175
+ case File:
8176
+ return SymbolNone;
8177
+ default:
8178
+ return SymbolDefault;
8179
+ }
8180
+ };
8181
+
8182
+ // TODO
8183
+ const getCompletionFileIcon = kind => {
8184
+ switch (kind) {
8185
+ case File:
8186
+ return EmptyString;
8187
+ case Folder:
8188
+ return EmptyString;
8189
+ default:
8190
+ return EmptyString;
8390
8191
  }
8192
+ };
8193
+
8194
+ const getHighlights = item => {
8195
+ const {
8196
+ matches
8197
+ } = item;
8198
+ return matches.slice(1);
8199
+ };
8200
+
8201
+ const getLabel = item => {
8202
+ return item.label;
8203
+ };
8204
+ const getVisibleIem = (item, itemHeight, leadingWord, i, focusedIndex) => {
8391
8205
  return {
8392
- textInfos: result,
8393
- differences
8206
+ label: getLabel(item),
8207
+ symbolName: getSymbolName(item.kind),
8208
+ top: i * itemHeight,
8209
+ highlights: getHighlights(item),
8210
+ focused: i === focusedIndex,
8211
+ deprecated: item.flags & Deprecated,
8212
+ fileIcon: getCompletionFileIcon(item.kind)
8394
8213
  };
8395
8214
  };
8396
8215
 
8397
- const getCursorsVirtualDom = cursors => {
8398
- const dom = [];
8399
- for (const translate of cursors) {
8400
- dom.push({
8401
- type: Div,
8402
- className: EditorCursor,
8403
- translate
8404
- });
8216
+ const getVisibleItems = (filteredItems, itemHeight, leadingWord, minLineY, maxLineY, focusedIndex) => {
8217
+ const visibleItems = [];
8218
+ for (let i = minLineY; i < maxLineY; i++) {
8219
+ const filteredItem = filteredItems[i];
8220
+ visibleItems.push(getVisibleIem(filteredItem, itemHeight, leadingWord, i, focusedIndex));
8405
8221
  }
8406
- return dom;
8222
+ return visibleItems;
8407
8223
  };
8408
8224
 
8409
- const Error$1 = 'error';
8410
- const Warning = 'warning';
8411
-
8412
- const getDiagnosticClassName = type => {
8413
- // TODO use classnames enum
8414
- switch (type) {
8415
- case Error$1:
8416
- return 'DiagnosticError';
8417
- case Warning:
8418
- return 'DiagnosticWarning';
8419
- default:
8420
- return 'DiagnosticError';
8225
+ const renderItems = {
8226
+ isEqual(oldState, newState) {
8227
+ return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
8228
+ },
8229
+ apply(oldState, newState) {
8230
+ const visibleItems = getVisibleItems(newState.items, newState.itemHeight, newState.leadingWord, newState.minLineY, newState.maxLineY, newState.focusedIndex);
8231
+ const dom = getCompletionItemsVirtualDom(visibleItems);
8232
+ return ['setDom', dom];
8421
8233
  }
8422
8234
  };
8423
- const getDiagnosticVirtualDom = diagnostic => {
8424
- const {
8425
- x,
8426
- y,
8427
- width,
8428
- height,
8429
- type
8430
- } = diagnostic;
8431
- const extraClassName = getDiagnosticClassName(type);
8432
- return [{
8433
- type: Div,
8434
- className: `${Diagnostic} ${extraClassName}`,
8435
- width,
8436
- height,
8437
- top: y,
8438
- left: x,
8439
- childCount: 0
8440
- }];
8235
+ const renderBounds = {
8236
+ isEqual(oldState, newState) {
8237
+ return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.x === newState.x && oldState.y === newState.y;
8238
+ },
8239
+ apply(oldState, newState) {
8240
+ const {
8241
+ x,
8242
+ y,
8243
+ width,
8244
+ height
8245
+ } = newState;
8246
+ return [/* method */SetBounds, /* x */x, /* y */y, /* width */width, /* height */height];
8247
+ }
8441
8248
  };
8442
-
8443
- const getDiagnosticsVirtualDom = diagnostics => {
8444
- const dom = diagnostics.flatMap(getDiagnosticVirtualDom);
8445
- return dom;
8249
+ const renderHeight = {
8250
+ isEqual(oldState, newState) {
8251
+ return oldState.items.length === newState.items.length;
8252
+ },
8253
+ apply(oldState, newState) {
8254
+ const {
8255
+ itemHeight
8256
+ } = newState;
8257
+ const contentHeight = newState.items.length * itemHeight;
8258
+ return [/* method */SetContentHeight, /* contentHeight */contentHeight];
8259
+ }
8446
8260
  };
8447
-
8448
- const getGutterInfoVirtualDom = gutterInfo => {
8449
- return [{
8450
- type: Span,
8451
- className: 'LineNumber',
8452
- childCount: 1
8453
- }, text(gutterInfo)];
8261
+ const renderNegativeMargin = {
8262
+ isEqual(oldState, newState) {
8263
+ return oldState.deltaY === newState.deltaY;
8264
+ },
8265
+ apply(oldState, newState) {
8266
+ return [/* method */SetNegativeMargin, /* negativeMargin */-newState.deltaY];
8267
+ }
8454
8268
  };
8455
- const getEditorGutterVirtualDom = gutterInfos => {
8456
- const dom = gutterInfos.flatMap(getGutterInfoVirtualDom);
8457
- return dom;
8269
+ const renderScrollBar = {
8270
+ isEqual(oldState, newState) {
8271
+ return oldState.negativeMargin === newState.negativeMargin && oldState.deltaY === newState.deltaY && oldState.height === newState.height && oldState.finalDeltaY === newState.finalDeltaY && oldState.items.length === newState.items.length;
8272
+ },
8273
+ apply(oldState, newState) {
8274
+ const total = newState.items.length;
8275
+ const contentHeight = total * newState.itemHeight;
8276
+ const scrollBarHeight = getScrollBarSize(newState.height, contentHeight, newState.minimumSliderSize);
8277
+ const scrollBarY = getScrollBarY(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
8278
+ return [/* method */SetScrollBar, /* scrollBarY */scrollBarY, /* scrollBarHeight */scrollBarHeight];
8279
+ }
8458
8280
  };
8459
-
8460
- const getEditorRowsVirtualDom = (textInfos, differences, lineNumbers = true) => {
8461
- const dom = [];
8462
- for (let i = 0; i < textInfos.length; i++) {
8463
- const textInfo = textInfos[i];
8464
- const difference = differences[i];
8465
- dom.push({
8466
- type: Div,
8467
- className: EditorRow,
8468
- translate: px(difference),
8469
- childCount: textInfo.length / 2
8470
- });
8471
- for (let j = 0; j < textInfo.length; j += 2) {
8472
- const tokenText = textInfo[j];
8473
- const className = textInfo[j + 1];
8474
- dom.push({
8475
- type: Span,
8476
- className,
8477
- childCount: 1
8478
- }, text(tokenText));
8281
+ const render$2 = [renderItems, renderBounds, renderHeight, renderNegativeMargin, renderScrollBar];
8282
+ const renderCompletion = (oldState, newState) => {
8283
+ const commands = [];
8284
+ for (const item of render$2) {
8285
+ if (!item.isEqual(oldState, newState)) {
8286
+ commands.push(item.apply(oldState, newState));
8479
8287
  }
8480
8288
  }
8481
- return dom;
8289
+ return commands;
8482
8290
  };
8483
8291
 
8484
- const getIncrementalEdits = async (oldState, newState) => {
8485
- if (!newState.undoStack) {
8486
- return undefined;
8292
+ const render$1 = (oldState, newState) => {
8293
+ const commands = renderCompletion(oldState, newState);
8294
+ const wrappedCommands = [];
8295
+ const uid = newState.uid;
8296
+ for (const command of commands) {
8297
+ wrappedCommands.push(['Viewlet.send', uid, ...command]);
8487
8298
  }
8488
- const lastChanges = newState.undoStack.at(-1);
8489
- if (lastChanges && lastChanges.length === 1) {
8490
- const lastChange = lastChanges[0];
8491
- if (lastChange.origin === EditorType) {
8492
- const rowIndex = lastChange.start.rowIndex;
8493
- const lines = newState.lines;
8494
- const oldLine = oldState.lines[rowIndex];
8495
- const newLine = lines[rowIndex];
8496
- const incrementalEdits = await invoke$1('TokenizeIncremental.tokenizeIncremental', newState.uid, newState.languageId, oldLine, newLine, rowIndex, newState.minLineY);
8497
- if (incrementalEdits && incrementalEdits.length === 1) {
8498
- return incrementalEdits;
8499
- }
8500
- }
8299
+ return wrappedCommands;
8300
+ };
8301
+ const add = widget => {
8302
+ const commands = render$1(widget.oldState, widget.newState);
8303
+ const id = 'EditorCompletion';
8304
+ // TODO how to generate a unique integer id
8305
+ // that doesn't collide with ids created in renderer worker?
8306
+ const uid = widget.newState.uid;
8307
+ const allCommands = [];
8308
+ allCommands.push(['Viewlet.create', id, uid]);
8309
+ allCommands.push(...commands);
8310
+ return allCommands;
8311
+ };
8312
+ const remove = widget => {
8313
+ return [['Viewlet.send', widget.newState.uid, 'dispose']];
8314
+ };
8315
+ const handleEditorType = (editor, state) => {
8316
+ const {
8317
+ unfilteredItems,
8318
+ itemHeight,
8319
+ maxHeight
8320
+ } = state;
8321
+ const {
8322
+ selections
8323
+ } = editor;
8324
+ const rowIndex = selections[0];
8325
+ const columnIndex = selections[1];
8326
+ const x$1 = x(editor, rowIndex, columnIndex);
8327
+ const y$1 = y(editor, rowIndex);
8328
+ const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
8329
+ const items = filterCompletionItems(unfilteredItems, wordAtOffset);
8330
+ const newMinLineY = 0;
8331
+ const newMaxLineY = Math.min(items.length, 8);
8332
+ const height = getListHeight(items.length, itemHeight, maxHeight);
8333
+ const finalDeltaY = items.length * itemHeight - height;
8334
+ return {
8335
+ ...state,
8336
+ items,
8337
+ x: x$1,
8338
+ y: y$1,
8339
+ minLineY: newMinLineY,
8340
+ maxLineY: newMaxLineY,
8341
+ leadingWord: wordAtOffset,
8342
+ height,
8343
+ finalDeltaY
8344
+ };
8345
+ };
8346
+ const handleEditorDeleteLeft = (editor, state) => {
8347
+ const {
8348
+ unfilteredItems,
8349
+ itemHeight,
8350
+ maxHeight
8351
+ } = state;
8352
+ const {
8353
+ selections
8354
+ } = editor;
8355
+ const rowIndex = selections[0];
8356
+ const columnIndex = selections[1];
8357
+ const x$1 = x(editor, rowIndex, columnIndex);
8358
+ const y$1 = y(editor, rowIndex);
8359
+ const wordAtOffset = getWordBefore(editor, rowIndex, columnIndex);
8360
+ if (!wordAtOffset) {
8361
+ return undefined;
8501
8362
  }
8502
- return undefined;
8363
+ const items = filterCompletionItems(unfilteredItems, wordAtOffset);
8364
+ const newMaxLineY = Math.min(items.length, 8);
8365
+ const height = getListHeight(items.length, itemHeight, maxHeight);
8366
+ return {
8367
+ ...state,
8368
+ items,
8369
+ x: x$1,
8370
+ y: y$1,
8371
+ maxLineY: newMaxLineY,
8372
+ leadingWord: wordAtOffset,
8373
+ height
8374
+ };
8503
8375
  };
8504
8376
 
8505
- const getSelectionsVirtualDom = selections => {
8506
- const dom = [];
8507
- for (let i = 0; i < selections.length; i += 4) {
8508
- const x = selections[i];
8509
- const y = selections[i + 1];
8510
- const width = selections[i + 2];
8511
- const height = selections[i + 3];
8512
- dom.push({
8513
- type: Div,
8514
- className: EditorSelection,
8515
- left: x,
8516
- top: y,
8517
- width,
8518
- height
8519
- });
8520
- }
8521
- return dom;
8377
+ const EditorCompletionWidget = {
8378
+ __proto__: null,
8379
+ add,
8380
+ handleEditorDeleteLeft,
8381
+ handleEditorType,
8382
+ remove,
8383
+ render: render$1
8522
8384
  };
8523
8385
 
8524
8386
  const addWidget = widget => {
@@ -8538,7 +8400,7 @@ const renderWidget = widget => {
8538
8400
  } = widget;
8539
8401
  switch (id) {
8540
8402
  case Completion:
8541
- return render$2(widget.oldState, widget.newState);
8403
+ return render$1(widget.oldState, widget.newState);
8542
8404
  default:
8543
8405
  throw new Error(`unsupported widget`);
8544
8406
  }
@@ -8549,7 +8411,7 @@ const removeWidget = widget => {
8549
8411
  } = widget;
8550
8412
  switch (id) {
8551
8413
  case Completion:
8552
- return remove$1(widget);
8414
+ return remove(widget);
8553
8415
  default:
8554
8416
  throw new Error('unsupported widget');
8555
8417
  }
@@ -8897,8 +8759,8 @@ const commandMap = {
8897
8759
  'EditorCompletion.focusPrevious': focusPrevious$1,
8898
8760
  'EditorCompletion.handleEditorBlur': handleEditorBlur,
8899
8761
  'EditorCompletion.handleEditorClick': handleEditorClick,
8900
- 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft,
8901
- 'EditorCompletion.handleEditorType': handleEditorType,
8762
+ 'EditorCompletion.handleEditorDeleteLeft': handleEditorDeleteLeft$1,
8763
+ 'EditorCompletion.handleEditorType': handleEditorType$1,
8902
8764
  'EditorCompletion.loadContent': loadContent$2,
8903
8765
  'EditorCompletion.selectCurrent': selectCurrent,
8904
8766
  'EditorCompletion.selectIndex': selectIndex,