@pierre/diffs 1.3.0-beta.7 → 1.3.0-beta.9

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.
Files changed (129) hide show
  1. package/dist/components/CodeView.d.ts +86 -4
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +248 -33
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +1 -0
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +15 -4
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +2 -0
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +21 -6
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/FileStream.d.ts.map +1 -1
  14. package/dist/components/FileStream.js +2 -1
  15. package/dist/components/FileStream.js.map +1 -1
  16. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  17. package/dist/components/VirtualizedFile.js +3 -1
  18. package/dist/components/VirtualizedFile.js.map +1 -1
  19. package/dist/components/VirtualizedFileDiff.d.ts +3 -1
  20. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  21. package/dist/components/VirtualizedFileDiff.js +14 -6
  22. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  23. package/dist/components/Virtualizer.d.ts +2 -0
  24. package/dist/components/Virtualizer.d.ts.map +1 -1
  25. package/dist/components/Virtualizer.js +18 -4
  26. package/dist/components/Virtualizer.js.map +1 -1
  27. package/dist/constants.d.ts +3 -1
  28. package/dist/constants.d.ts.map +1 -1
  29. package/dist/constants.js +3 -1
  30. package/dist/constants.js.map +1 -1
  31. package/dist/editor/command.d.ts +1 -1
  32. package/dist/editor/command.d.ts.map +1 -1
  33. package/dist/editor/command.js +8 -0
  34. package/dist/editor/command.js.map +1 -1
  35. package/dist/editor/editStack.d.ts.map +1 -1
  36. package/dist/editor/editStack.js +1 -1
  37. package/dist/editor/editStack.js.map +1 -1
  38. package/dist/editor/editor.d.ts +8 -1
  39. package/dist/editor/editor.d.ts.map +1 -1
  40. package/dist/editor/editor.js +188 -42
  41. package/dist/editor/editor.js.map +1 -1
  42. package/dist/editor/editor2.js +1 -1
  43. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  44. package/dist/editor/lineAnnotations.js +84 -12
  45. package/dist/editor/lineAnnotations.js.map +1 -1
  46. package/dist/editor/marker.d.ts +5 -3
  47. package/dist/editor/marker.d.ts.map +1 -1
  48. package/dist/editor/marker.js +35 -12
  49. package/dist/editor/marker.js.map +1 -1
  50. package/dist/editor/matchBrackets.d.ts +8 -0
  51. package/dist/editor/matchBrackets.d.ts.map +1 -0
  52. package/dist/editor/matchBrackets.js +146 -0
  53. package/dist/editor/matchBrackets.js.map +1 -0
  54. package/dist/editor/popover.d.ts +45 -0
  55. package/dist/editor/popover.d.ts.map +1 -0
  56. package/dist/editor/popover.js +145 -0
  57. package/dist/editor/popover.js.map +1 -0
  58. package/dist/editor/searchPanel.d.ts +1 -0
  59. package/dist/editor/searchPanel.d.ts.map +1 -1
  60. package/dist/editor/searchPanel.js +6 -6
  61. package/dist/editor/searchPanel.js.map +1 -1
  62. package/dist/editor/selection.d.ts +13 -1
  63. package/dist/editor/selection.d.ts.map +1 -1
  64. package/dist/editor/selection.js +55 -4
  65. package/dist/editor/selection.js.map +1 -1
  66. package/dist/editor/selectionAction.d.ts +20 -3
  67. package/dist/editor/selectionAction.d.ts.map +1 -1
  68. package/dist/editor/selectionAction.js +41 -10
  69. package/dist/editor/selectionAction.js.map +1 -1
  70. package/dist/editor/textDocument.d.ts +2 -0
  71. package/dist/editor/textDocument.d.ts.map +1 -1
  72. package/dist/editor/textDocument.js +22 -6
  73. package/dist/editor/textDocument.js.map +1 -1
  74. package/dist/editor/tokenzier.d.ts +4 -7
  75. package/dist/editor/tokenzier.d.ts.map +1 -1
  76. package/dist/editor/tokenzier.js +76 -35
  77. package/dist/editor/tokenzier.js.map +1 -1
  78. package/dist/index.d.ts +7 -7
  79. package/dist/index.js +5 -5
  80. package/dist/managers/ResizeManager.d.ts +10 -2
  81. package/dist/managers/ResizeManager.d.ts.map +1 -1
  82. package/dist/managers/ResizeManager.js +33 -13
  83. package/dist/managers/ResizeManager.js.map +1 -1
  84. package/dist/managers/UniversalRenderingManager.d.ts +2 -1
  85. package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
  86. package/dist/managers/UniversalRenderingManager.js +6 -1
  87. package/dist/managers/UniversalRenderingManager.js.map +1 -1
  88. package/dist/react/CodeView.d.ts +25 -1
  89. package/dist/react/CodeView.d.ts.map +1 -1
  90. package/dist/react/CodeView.js +76 -17
  91. package/dist/react/CodeView.js.map +1 -1
  92. package/dist/react/EditorContext.d.ts +2 -2
  93. package/dist/react/EditorContext.d.ts.map +1 -1
  94. package/dist/react/EditorContext.js.map +1 -1
  95. package/dist/react/index.d.ts +2 -2
  96. package/dist/renderers/DiffHunksRenderer.d.ts +1 -1
  97. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  98. package/dist/renderers/DiffHunksRenderer.js +24 -15
  99. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  100. package/dist/renderers/FileRenderer.d.ts +2 -1
  101. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  102. package/dist/renderers/FileRenderer.js +58 -38
  103. package/dist/renderers/FileRenderer.js.map +1 -1
  104. package/dist/ssr/index.d.ts +2 -2
  105. package/dist/types.d.ts +42 -2
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/utils/applyLineTextWithNewline.d.ts +5 -0
  108. package/dist/utils/applyLineTextWithNewline.d.ts.map +1 -0
  109. package/dist/utils/applyLineTextWithNewline.js +11 -0
  110. package/dist/utils/applyLineTextWithNewline.js.map +1 -0
  111. package/dist/utils/areManagedSnapshotsEqual.d.ts +2 -2
  112. package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
  113. package/dist/utils/areManagedSnapshotsEqual.js +5 -0
  114. package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
  115. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts +5 -0
  116. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts.map +1 -0
  117. package/dist/utils/createCodeViewHeaderFooterHostElement.js +19 -0
  118. package/dist/utils/createCodeViewHeaderFooterHostElement.js.map +1 -0
  119. package/dist/utils/updateDiffHunks.d.ts.map +1 -1
  120. package/dist/utils/updateDiffHunks.js +23 -1
  121. package/dist/utils/updateDiffHunks.js.map +1 -1
  122. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  123. package/dist/worker/WorkerPoolManager.js +4 -2
  124. package/dist/worker/WorkerPoolManager.js.map +1 -1
  125. package/dist/worker/types.d.ts +4 -0
  126. package/dist/worker/types.d.ts.map +1 -1
  127. package/dist/worker/worker-portable.js.map +1 -1
  128. package/dist/worker/worker.js.map +1 -1
  129. package/package.json +4 -2
@@ -1,4 +1,4 @@
1
- import { queueRender } from "../managers/UniversalRenderingManager.js";
1
+ import { dequeueRender, queueRender } from "../managers/UniversalRenderingManager.js";
2
2
  import { getFiletypeFromFileName } from "../utils/getFiletypeFromFileName.js";
3
3
  import { isMoveCursorShortcut, isPrimaryModifier, isSafari } from "./platform.js";
4
4
  import { resolveEditorCommandFromKeyboardEvent, resolveFindAgainShortcut } from "./command.js";
@@ -6,8 +6,10 @@ import { EditStack } from "./editStack.js";
6
6
  import editor_default from "./editor2.js";
7
7
  import { addEventListener, clampDomOffset, extend, getLineNumberAttr, h, round } from "./utils.js";
8
8
  import { applyDocumentChangeToLineAnnotations, renderLineAnnotations } from "./lineAnnotations.js";
9
- import { applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveIndentEdits, resolveSelectionCut, selectionIntersects } from "./selection.js";
9
+ import { PopoverManager } from "./popover.js";
10
+ import { applyDeleteCharacterToSelections, applyDeleteHardLineForwardToSelections, applyDeleteSoftLineBackwardToSelections, applyDeleteWordBackwardToSelections, applyTextChangeToSelections, applyTextReplaceToSelections, applyTransposeToSelections, comparePosition, convertSelection, createSelectionFrom, createSelectionFromAnchorAndFocusOffsets, expandCollapsedSelectionToWord, extendSelection, extendSelections, findNexMatch, getAutoSurroundReplacementTexts, getCaretPosition, getDocumentBoundarySelection, getDocumentFullSelection, getSelectedLineBlocks, getSelectionAnchor, getSelectionText, isCollapsedSelection, isLineEditable, mapCursorMove, mapSelectionShift, mergeOverlappingSelections, remapSelectionsAfterEdits, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines } from "./selection.js";
10
11
  import { MarkerRenderer, markerSeverityDatasetKey } from "./marker.js";
12
+ import { findBracketMatchRanges } from "./matchBrackets.js";
11
13
  import { createSpriteElement } from "./sprite.js";
12
14
  import { SearchPanelWidget } from "./searchPanel.js";
13
15
  import { SelectionActionWidget } from "./selectionAction.js";
@@ -15,11 +17,24 @@ import { TextDocument } from "./textDocument.js";
15
17
  import { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, snapTextOffsetToUnicodeBoundary } from "./textMeasure.js";
16
18
  import { EditorTokenizer, renderLineTokens } from "./tokenzier.js";
17
19
  //#region src/editor/editor.ts
20
+ function getShadowRootRange(shadowRoot) {
21
+ const selection = shadowRoot.getSelection?.();
22
+ if (selection == null || selection.rangeCount === 0) return;
23
+ const range = selection.getRangeAt(0);
24
+ return {
25
+ collapsed: range.collapsed,
26
+ startContainer: range.startContainer,
27
+ startOffset: range.startOffset,
28
+ endContainer: range.endContainer,
29
+ endOffset: range.endOffset
30
+ };
31
+ }
18
32
  const MAX_EDIT_WIDEN_WINDOW_MULTIPLE = 2;
19
33
  var Editor = class {
20
34
  #options;
21
35
  #metrics = new Metrics();
22
36
  #tokenizer;
37
+ #popoverManager;
23
38
  #editorEventDisposes;
24
39
  #globalEventDisposes;
25
40
  #selectEventDisposes;
@@ -85,6 +100,12 @@ var Editor = class {
85
100
  constructor(options = {}) {
86
101
  this.#options = options;
87
102
  }
103
+ setOptions(options) {
104
+ this.#options = {
105
+ ...this.#options,
106
+ ...options
107
+ };
108
+ }
88
109
  edit(fileInstance) {
89
110
  const { useTokenTransformer, enableGutterUtility, enableLineSelection, expandUnchanged, lineHoverHighlight = "disabled", ...rest } = fileInstance.options;
90
111
  if (useTokenTransformer !== true || enableGutterUtility === true || enableLineSelection === true || expandUnchanged !== true && fileInstance.type === "file-diff" || lineHoverHighlight !== "disabled") {
@@ -197,6 +218,7 @@ var Editor = class {
197
218
  return;
198
219
  }
199
220
  this.#markerRenderer ??= new MarkerRenderer({
221
+ popoverManager: this.#getPopoverManager(),
200
222
  getLineHeight: () => this.#metrics.lineHeight,
201
223
  getOverlayElement: () => this.#overlayElement,
202
224
  getGutterWidth: () => this.#getGutterWidth(),
@@ -219,11 +241,14 @@ var Editor = class {
219
241
  blur() {
220
242
  this.#contentElement?.blur();
221
243
  }
222
- cleanUp() {
244
+ cleanUp(recycle = false) {
245
+ dequeueRender(this.#handleCustomPasteEvent);
223
246
  this.#tokenizer?.cleanUp();
224
247
  this.#tokenizer = void 0;
225
- this.#textDocument = void 0;
226
- this.#fileInfo = void 0;
248
+ if (!recycle) {
249
+ this.#textDocument = void 0;
250
+ this.#fileInfo = void 0;
251
+ }
227
252
  this.#globalEventDisposes?.forEach((dispose) => dispose());
228
253
  this.#globalEventDisposes = void 0;
229
254
  this.#editorEventDisposes?.forEach((dispose) => dispose());
@@ -247,6 +272,8 @@ var Editor = class {
247
272
  this.#spriteElement?.remove();
248
273
  this.#spriteElement = void 0;
249
274
  this.#fileContainer = void 0;
275
+ this.#popoverManager?.cleanUp();
276
+ this.#popoverManager = void 0;
250
277
  this.#gutterElement = void 0;
251
278
  this.#contentElement?.removeAttribute("contentEditable");
252
279
  this.#contentElement = void 0;
@@ -293,6 +320,7 @@ var Editor = class {
293
320
  break;
294
321
  }
295
322
  if (codeElement === void 0 || contentEl === void 0) return;
323
+ this.#getPopoverManager().setViewportElements(fileContainer, codeElement);
296
324
  if (this.#fileContainer !== fileContainer) {
297
325
  this.#fileContainer = fileContainer;
298
326
  if (this.#globalStyleElement !== void 0) fileContainer.appendChild(this.#globalStyleElement);
@@ -314,17 +342,7 @@ var Editor = class {
314
342
  };
315
343
  this.#textDocument = textDocument;
316
344
  this.#tokenizer?.cleanUp();
317
- this.#tokenizer = new EditorTokenizer({
318
- highlighter,
319
- textDocument,
320
- codeOptions: this.#fileInstance?.options ?? {},
321
- onDeferTokenize: this.#onDeferTokenize,
322
- onThemeChange: () => this.#scheduleThemeSelectionRefresh(),
323
- setStyle: (css) => {
324
- this.#themeStyleElement.textContent = css;
325
- },
326
- __debug: this.#options.__debug
327
- });
345
+ this.#tokenizer = void 0;
328
346
  this.#resetState();
329
347
  this.#selections = this.#initSelections;
330
348
  requestAnimationFrame(() => {
@@ -332,6 +350,19 @@ var Editor = class {
332
350
  });
333
351
  if (this.#textDocument !== void 0 && this.#options.__debug === true) console.log("[diffs/editor] text document rebuilt from", fileOrDiff.name);
334
352
  }
353
+ const textDocument = this.#textDocument;
354
+ if (this.#tokenizer == null && textDocument != null) this.#tokenizer = new EditorTokenizer({
355
+ highlighter,
356
+ textDocument,
357
+ codeOptions: this.#fileInstance?.options ?? {},
358
+ matchBrackets: this.#options.matchBrackets,
359
+ onDeferTokenize: this.#onDeferTokenize,
360
+ onThemeChange: () => this.#scheduleThemeSelectionRefresh(),
361
+ setStyle: (css) => {
362
+ this.#themeStyleElement.textContent = css;
363
+ },
364
+ __debug: this.#options.__debug
365
+ });
335
366
  if (this.#contentElement !== contentEl) {
336
367
  this.#gutterElement = gutterEl;
337
368
  this.#contentElement = extend(contentEl, {
@@ -384,6 +415,12 @@ var Editor = class {
384
415
  get #isWrap() {
385
416
  return this.#fileInstance?.options.overflow === "wrap";
386
417
  }
418
+ #getPopoverManager() {
419
+ return this.#popoverManager ??= new PopoverManager({
420
+ hasActivePopover: () => this.#selectionAction !== void 0,
421
+ updateActivePopover: () => this.#updateSelectionActionPopover()
422
+ });
423
+ }
387
424
  #resetCache() {
388
425
  this.#lineYCache.clear();
389
426
  this.#wrapLineOffsetsCache.clear();
@@ -430,8 +467,8 @@ var Editor = class {
430
467
  if (this.#shouldIgnoreSelectionChange || this.#suppressNativeSelectionSync || shadowRoot == null || !this.#contentHasFocus) return;
431
468
  if (this.#selections !== void 0 && this.#selections.length > 1 && !this.#isContentMouseDown) return;
432
469
  const selectionRaw = document.getSelection();
433
- if (selectionRaw == null || typeof selectionRaw.getComposedRanges !== "function") return;
434
- const composedRange = selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0];
470
+ if (selectionRaw == null) return;
471
+ const composedRange = typeof selectionRaw.getComposedRanges === "function" ? selectionRaw.getComposedRanges({ shadowRoots: [shadowRoot] })?.[0] : getShadowRootRange(shadowRoot);
435
472
  if (composedRange === void 0 || !this.#rangeBelongsToEditor(composedRange)) return;
436
473
  let selection = convertSelection(composedRange, 0);
437
474
  if (selection === void 0) return;
@@ -443,6 +480,10 @@ var Editor = class {
443
480
  if (this.#isContentMouseDown) if (this.#selectionStart !== void 0) selection = createSelectionFrom(this.#selectionStart, selection);
444
481
  else this.#selectionStart = selection;
445
482
  else if (this.#selectionStart !== void 0) selection.direction = createSelectionFrom(this.#selectionStart, selection).direction;
483
+ else if (this.#selections !== void 0 && this.#selections.length === 1) {
484
+ const previous = this.#selections[0];
485
+ if (comparePosition(previous.start, selection.start) === 0 && comparePosition(previous.end, selection.end) === 0) selection.direction = previous.direction;
486
+ }
446
487
  if (this.#reservedSelections !== void 0) this.#updateSelections([...this.#reservedSelections.filter((reservedSelection) => !selectionIntersects(reservedSelection, selection)), selection]);
447
488
  else this.#updateSelections([selection]);
448
489
  }, { passive: true }),
@@ -474,6 +515,7 @@ var Editor = class {
474
515
  this.#selectEventDisposes = disposes;
475
516
  }
476
517
  #listenContentElement(contentEl, gutterEl) {
518
+ const { onFocus, onBlur } = this.#options;
477
519
  const targetIsContentElement = (e) => {
478
520
  const target = e.composedPath()[0];
479
521
  return target !== void 0 && (target === contentEl || contentEl.contains(target));
@@ -482,10 +524,12 @@ var Editor = class {
482
524
  this.#editorEventDisposes = [
483
525
  addEventListener(contentEl, "focus", () => {
484
526
  this.#contentHasFocus = true;
527
+ onFocus?.();
485
528
  if (!this.#isContentMouseDown && !this.#shouldIgnoreSelectionChange && this.#selections !== void 0 && this.#selections.length > 0) this.#setWindowSelection(this.#selections.at(-1));
486
529
  }, { passive: true }),
487
530
  addEventListener(contentEl, "blur", () => {
488
531
  this.#contentHasFocus = false;
532
+ onBlur?.();
489
533
  }, { passive: true }),
490
534
  addEventListener(contentEl, "pointerdown", (e) => {
491
535
  this.#suppressNativeSelectionSync = false;
@@ -524,7 +568,7 @@ var Editor = class {
524
568
  addEventListener(contentEl, "keydown", (e) => {
525
569
  if (e.key === "Escape") {
526
570
  e.preventDefault();
527
- this.#searchPanel?.cleanup();
571
+ this.#searchPanel?.close();
528
572
  this.#searchPanel = void 0;
529
573
  this.#retainSearchPanelFocus = false;
530
574
  this.#selectionAction?.cleanup();
@@ -554,10 +598,16 @@ var Editor = class {
554
598
  e.preventDefault();
555
599
  return;
556
600
  }
557
- if (e.key === "v" && isPrimaryModifier(e) && this.#options.clipboard !== void 0) {
558
- e.preventDefault();
559
- queueRender(this.#handleCustomPasteEvent);
560
- return;
601
+ if ((e.key.length === 1 ? e.key.toLowerCase() : e.key) === "v" && isPrimaryModifier(e)) {
602
+ if (e.repeat && this.#isDiff) {
603
+ e.preventDefault();
604
+ return;
605
+ }
606
+ if (this.#options.clipboard !== void 0) {
607
+ e.preventDefault();
608
+ queueRender(this.#handleCustomPasteEvent);
609
+ return;
610
+ }
561
611
  }
562
612
  if (this.#searchPanel !== void 0) {
563
613
  const findAgain = resolveFindAgainShortcut(e);
@@ -721,6 +771,10 @@ var Editor = class {
721
771
  }
722
772
  break;
723
773
  }
774
+ case "moveLineUp":
775
+ case "moveLineDown":
776
+ this.#moveSelectedLines(command === "moveLineUp" ? -1 : 1);
777
+ break;
724
778
  case "indent":
725
779
  case "outdent":
726
780
  if (this.#selections !== void 0) {
@@ -834,6 +888,62 @@ var Editor = class {
834
888
  break;
835
889
  }
836
890
  }
891
+ #moveSelectedLines(direction) {
892
+ const textDocument = this.#textDocument;
893
+ const selections = this.#selections;
894
+ if (textDocument === void 0 || selections === void 0) return;
895
+ const blocks = getSelectedLineBlocks(selections);
896
+ if (blocks.length === 0 || direction < 0 && blocks[0].startLine === 0 || direction > 0 && blocks.at(-1).endLine >= textDocument.lineCount - 1) return;
897
+ const lineCount = textDocument.lineCount;
898
+ const lineRangeEnd = (line) => line < lineCount - 1 ? {
899
+ line: line + 1,
900
+ character: 0
901
+ } : {
902
+ line,
903
+ character: textDocument.getLineLength(line)
904
+ };
905
+ const getLinesText = (lines, appendFinalLineBreak) => {
906
+ const text = lines.map((line) => textDocument.getLineText(line)).join(textDocument.eol);
907
+ return appendFinalLineBreak ? text + textDocument.eol : text;
908
+ };
909
+ const edits = [];
910
+ if (direction < 0) for (const block of blocks) {
911
+ const previousLine = block.startLine - 1;
912
+ const blockLines = [];
913
+ for (let line = block.startLine; line <= block.endLine; line++) blockLines.push(line);
914
+ edits.push({
915
+ range: {
916
+ start: {
917
+ line: previousLine,
918
+ character: 0
919
+ },
920
+ end: lineRangeEnd(block.endLine)
921
+ },
922
+ newText: getLinesText([...blockLines, previousLine], block.endLine < lineCount - 1)
923
+ });
924
+ }
925
+ else for (let index = blocks.length - 1; index >= 0; index--) {
926
+ const block = blocks[index];
927
+ const nextLine = block.endLine + 1;
928
+ const blockLines = [];
929
+ for (let line = block.startLine; line <= block.endLine; line++) blockLines.push(line);
930
+ edits.push({
931
+ range: {
932
+ start: {
933
+ line: block.startLine,
934
+ character: 0
935
+ },
936
+ end: lineRangeEnd(nextLine)
937
+ },
938
+ newText: getLinesText([nextLine, ...blockLines], nextLine < lineCount - 1)
939
+ });
940
+ }
941
+ const lastBlock = blocks.at(-1);
942
+ const lastLineLengthAfterMove = direction > 0 && lastBlock.endLine === lineCount - 2 ? textDocument.getLineLength(lastBlock.endLine) : textDocument.getLineLength(lineCount - 1);
943
+ const nextSelections = selections.map((selection) => shiftSelectionLines(selection, direction, lineCount, (line) => line === lineCount - 1 ? lastLineLengthAfterMove : textDocument.getLineLength(line)));
944
+ const change = textDocument.applyEdits(edits, true, selections, nextSelections, true);
945
+ if (change !== void 0) this.#applyChange(change, nextSelections);
946
+ }
837
947
  #handleLayoutResize = () => {
838
948
  const lineAnnotations = this.#lineAnnotations?.length ?? 0;
839
949
  const prevGutterWidth = this.#gutterWidthCache;
@@ -1046,11 +1156,9 @@ var Editor = class {
1046
1156
  }
1047
1157
  }
1048
1158
  #getScrollMargin() {
1049
- const componentTop = this.#fileInstance?.top ?? 0;
1050
1159
  const top = this.#searchPanel !== void 0 ? 48 : 0;
1051
1160
  const start = this.#getGutterWidth() + this.#metrics.ch;
1052
- const end = this.#metrics.ch;
1053
- return `${componentTop + top}px ${end}px 0 ${start}px`;
1161
+ return `${top}px ${this.#metrics.ch}px 0 ${start}px`;
1054
1162
  }
1055
1163
  #scrollToLine(line, char = 0, noFocus = false) {
1056
1164
  this.__postponeBgTokenizeToNextFrame();
@@ -1115,12 +1223,7 @@ var Editor = class {
1115
1223
  block: "center",
1116
1224
  inline: "nearest"
1117
1225
  });
1118
- if (this.#scrollingToLine === line && yFix === 0) {
1119
- this.#scrollingToLine = void 0;
1120
- this.#scrollingToLineChar = void 0;
1121
- this.#scrollingToLineFixed = false;
1122
- this.#scrollingToLineNoFocus = false;
1123
- } else if (this.#scrollingToLine === line && this.#scrollingToLineFixed) {
1226
+ if (this.#scrollingToLine === line && (yFix === 0 || this.#scrollingToLineFixed)) {
1124
1227
  this.#scrollingToLine = void 0;
1125
1228
  this.#scrollingToLineChar = void 0;
1126
1229
  this.#scrollingToLineFixed = false;
@@ -1266,6 +1369,8 @@ var Editor = class {
1266
1369
  if (!isCollapsedSelection(selection)) this.#renderSelection(renderCtx, "selection", selection);
1267
1370
  this.#renderCaret(renderCtx, selection, selection === primarySelection);
1268
1371
  }
1372
+ const bracketMatchRanges = this.#options.matchBrackets !== false && this.#textDocument !== void 0 && this.#tokenizer !== void 0 && isCollapsedSelection(primarySelection) ? findBracketMatchRanges(this.#textDocument, this.#tokenizer, primarySelection.start) : void 0;
1373
+ if (bracketMatchRanges !== void 0) for (const range of bracketMatchRanges) this.#renderSelection(renderCtx, "bracketMatch", range);
1269
1374
  }
1270
1375
  const textDocument = this.#textDocument;
1271
1376
  if (this.#matches !== void 0 && textDocument !== void 0) {
@@ -1454,15 +1559,17 @@ var Editor = class {
1454
1559
  const overlayElement = this.#overlayElement;
1455
1560
  const textDocument = this.#textDocument;
1456
1561
  const renderSelectionAction = this.#options.renderSelectionAction;
1457
- if (this.#options.enabledSelectionAction !== true || renderSelectionAction === void 0 || primarySelection === void 0 || isCollapsedSelection(primarySelection) || this.#isContentMouseDown || overlayElement === void 0 || textDocument === void 0) {
1562
+ const cleanup = () => {
1458
1563
  this.#selectionAction?.cleanup();
1459
1564
  this.#selectionAction = void 0;
1565
+ };
1566
+ if (this.#options.enabledSelectionAction !== true || renderSelectionAction === void 0 || primarySelection === void 0 || isCollapsedSelection(primarySelection) || this.#isContentMouseDown || overlayElement === void 0 || textDocument === void 0) {
1567
+ cleanup();
1460
1568
  return;
1461
1569
  }
1462
1570
  const head = getCaretPosition(primarySelection);
1463
1571
  if (!this.#isLineVisible(head.line)) {
1464
- this.#selectionAction?.cleanup();
1465
- this.#selectionAction = void 0;
1572
+ cleanup();
1466
1573
  return;
1467
1574
  }
1468
1575
  if (this.#selectionAction === void 0) {
@@ -1478,18 +1585,56 @@ var Editor = class {
1478
1585
  this.#replaceSelectionText(text, [getActiveSelection()]);
1479
1586
  },
1480
1587
  close: () => {
1481
- this.#selectionAction?.cleanup();
1482
- this.#selectionAction = void 0;
1588
+ cleanup();
1483
1589
  this.#scrollToPrimaryCaret();
1484
1590
  }
1485
1591
  });
1486
- this.#selectionAction = new SelectionActionWidget(head.line, selectionActionElement, overlayElement);
1592
+ this.#selectionAction = new SelectionActionWidget(selectionActionElement, overlayElement, () => this.#updateSelectionActionPopover());
1593
+ this.#getPopoverManager().resetPlacement();
1487
1594
  }
1488
- const [left, wrapLine] = this.#getCharX(head.line, head.character);
1489
1595
  const lineHeight = this.#metrics.lineHeight;
1490
- const top = this.#getLineY(head.line) + wrapLine * lineHeight + lineHeight;
1491
- this.#selectionAction.line = head.line;
1492
- this.#selectionAction.reposition(left, top, this.#getGutterWidth());
1596
+ const isBackward = primarySelection.direction === -1;
1597
+ const preferred = {
1598
+ placeAbove: isBackward,
1599
+ anchor: head
1600
+ };
1601
+ const fallback = isBackward ? {
1602
+ placeAbove: false,
1603
+ anchor: primarySelection.end
1604
+ } : {
1605
+ placeAbove: true,
1606
+ anchor: primarySelection.start
1607
+ };
1608
+ const popoverHeight = this.#selectionAction.height;
1609
+ const candidateGeometry = (candidate) => {
1610
+ const [left, candidateWrapLine] = this.#getCharX(candidate.anchor.line, candidate.anchor.character);
1611
+ const rowTop = this.#getLineY(candidate.anchor.line) + candidateWrapLine * lineHeight;
1612
+ const anchorTop = candidate.placeAbove ? rowTop : rowTop + lineHeight;
1613
+ const top = candidate.placeAbove ? anchorTop - popoverHeight : anchorTop;
1614
+ return {
1615
+ top,
1616
+ bottom: top + popoverHeight,
1617
+ left,
1618
+ anchorTop
1619
+ };
1620
+ };
1621
+ const preferredGeometry = candidateGeometry(preferred);
1622
+ const fallbackGeometry = candidateGeometry(fallback);
1623
+ const lineCount = textDocument.lineCount;
1624
+ const atDocumentEdge = isBackward ? head.line < 3 : head.line >= lineCount - 3;
1625
+ const canUseFallback = this.#isLineVisible(fallback.anchor.line);
1626
+ const popoverManager = this.#getPopoverManager();
1627
+ const useFallback = canUseFallback && popoverManager.choosePlacement({
1628
+ preferred: preferredGeometry,
1629
+ fallback: fallbackGeometry,
1630
+ viewport: popoverManager.getPlacementBounds(),
1631
+ popoverHeight,
1632
+ atDocumentEdge
1633
+ }) === "fallback";
1634
+ if (!canUseFallback) popoverManager.setPlacement("preferred");
1635
+ const { placeAbove } = useFallback ? fallback : preferred;
1636
+ const { left, anchorTop } = useFallback ? fallbackGeometry : preferredGeometry;
1637
+ this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove);
1493
1638
  }
1494
1639
  #openSearchPanel(mode) {
1495
1640
  if (this.#searchPanel !== void 0) {
@@ -1743,6 +1888,7 @@ var Editor = class {
1743
1888
  #lineBackgroundColor(line) {
1744
1889
  const lineElement = this.#getLineElement(line);
1745
1890
  if (lineElement === void 0) return;
1891
+ if (navigator.userAgent.includes("jsdom")) return;
1746
1892
  const backgroundColor = getComputedStyle(lineElement, "::after").backgroundColor;
1747
1893
  return backgroundColor === "" || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)" ? void 0 : backgroundColor;
1748
1894
  }