@pierre/diffs 1.3.0-beta.6 → 1.3.0-beta.8

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 (180) hide show
  1. package/dist/components/CodeView.d.ts +90 -5
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +291 -38
  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 +20 -6
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +38 -23
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +144 -61
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts +3 -2
  14. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  15. package/dist/components/UnresolvedFile.js +4 -2
  16. package/dist/components/UnresolvedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  18. package/dist/components/VirtualizedFile.js +6 -8
  19. package/dist/components/VirtualizedFile.js.map +1 -1
  20. package/dist/components/VirtualizedFileDiff.d.ts +13 -5
  21. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  22. package/dist/components/VirtualizedFileDiff.js +192 -55
  23. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  24. package/dist/components/Virtualizer.d.ts +2 -0
  25. package/dist/components/Virtualizer.d.ts.map +1 -1
  26. package/dist/components/Virtualizer.js +16 -3
  27. package/dist/components/Virtualizer.js.map +1 -1
  28. package/dist/constants.d.ts +3 -1
  29. package/dist/constants.d.ts.map +1 -1
  30. package/dist/constants.js +3 -1
  31. package/dist/constants.js.map +1 -1
  32. package/dist/editor/command.d.ts +1 -1
  33. package/dist/editor/command.d.ts.map +1 -1
  34. package/dist/editor/command.js +8 -0
  35. package/dist/editor/command.js.map +1 -1
  36. package/dist/editor/editor.d.ts +10 -3
  37. package/dist/editor/editor.d.ts.map +1 -1
  38. package/dist/editor/editor.js +325 -135
  39. package/dist/editor/editor.js.map +1 -1
  40. package/dist/editor/editor2.js +1 -1
  41. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  42. package/dist/editor/lineAnnotations.js +84 -12
  43. package/dist/editor/lineAnnotations.js.map +1 -1
  44. package/dist/editor/marker.d.ts +5 -3
  45. package/dist/editor/marker.d.ts.map +1 -1
  46. package/dist/editor/marker.js +35 -12
  47. package/dist/editor/marker.js.map +1 -1
  48. package/dist/editor/matchBrackets.d.ts +8 -0
  49. package/dist/editor/matchBrackets.d.ts.map +1 -0
  50. package/dist/editor/matchBrackets.js +146 -0
  51. package/dist/editor/matchBrackets.js.map +1 -0
  52. package/dist/editor/popover.d.ts +45 -0
  53. package/dist/editor/popover.d.ts.map +1 -0
  54. package/dist/editor/popover.js +145 -0
  55. package/dist/editor/popover.js.map +1 -0
  56. package/dist/editor/selection.d.ts +14 -2
  57. package/dist/editor/selection.d.ts.map +1 -1
  58. package/dist/editor/selection.js +142 -41
  59. package/dist/editor/selection.js.map +1 -1
  60. package/dist/editor/selectionAction.d.ts +20 -3
  61. package/dist/editor/selectionAction.d.ts.map +1 -1
  62. package/dist/editor/selectionAction.js +41 -10
  63. package/dist/editor/selectionAction.js.map +1 -1
  64. package/dist/editor/textDocument.d.ts +2 -0
  65. package/dist/editor/textDocument.d.ts.map +1 -1
  66. package/dist/editor/textDocument.js +22 -6
  67. package/dist/editor/textDocument.js.map +1 -1
  68. package/dist/editor/textMeasure.d.ts.map +1 -1
  69. package/dist/editor/textMeasure.js +25 -7
  70. package/dist/editor/textMeasure.js.map +1 -1
  71. package/dist/editor/tokenzier.d.ts +6 -7
  72. package/dist/editor/tokenzier.d.ts.map +1 -1
  73. package/dist/editor/tokenzier.js +83 -34
  74. package/dist/editor/tokenzier.js.map +1 -1
  75. package/dist/editor/utils.d.ts +3 -1
  76. package/dist/editor/utils.d.ts.map +1 -1
  77. package/dist/editor/utils.js +14 -1
  78. package/dist/editor/utils.js.map +1 -1
  79. package/dist/index.d.ts +9 -7
  80. package/dist/index.js +6 -4
  81. package/dist/managers/InteractionManager.d.ts.map +1 -1
  82. package/dist/managers/InteractionManager.js +0 -1
  83. package/dist/managers/InteractionManager.js.map +1 -1
  84. package/dist/managers/ResizeManager.d.ts +10 -2
  85. package/dist/managers/ResizeManager.d.ts.map +1 -1
  86. package/dist/managers/ResizeManager.js +33 -13
  87. package/dist/managers/ResizeManager.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/MultiFileDiff.d.ts +3 -4
  96. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  97. package/dist/react/MultiFileDiff.js.map +1 -1
  98. package/dist/react/index.d.ts +2 -2
  99. package/dist/react/utils/useFileDiffInstance.js +14 -15
  100. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  101. package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
  102. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  103. package/dist/renderers/DiffHunksRenderer.js +45 -23
  104. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  105. package/dist/renderers/FileRenderer.d.ts +2 -1
  106. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  107. package/dist/renderers/FileRenderer.js +58 -38
  108. package/dist/renderers/FileRenderer.js.map +1 -1
  109. package/dist/ssr/index.d.ts +2 -2
  110. package/dist/ssr/preloadDiffs.d.ts +11 -10
  111. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  112. package/dist/ssr/preloadDiffs.js +14 -6
  113. package/dist/ssr/preloadDiffs.js.map +1 -1
  114. package/dist/types.d.ts +100 -6
  115. package/dist/types.d.ts.map +1 -1
  116. package/dist/utils/applyLineTextWithNewline.d.ts +5 -0
  117. package/dist/utils/applyLineTextWithNewline.d.ts.map +1 -0
  118. package/dist/utils/applyLineTextWithNewline.js +11 -0
  119. package/dist/utils/applyLineTextWithNewline.js.map +1 -0
  120. package/dist/utils/areHunkDataEqual.js +1 -1
  121. package/dist/utils/areHunkDataEqual.js.map +1 -1
  122. package/dist/utils/areManagedSnapshotsEqual.d.ts +2 -2
  123. package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
  124. package/dist/utils/areManagedSnapshotsEqual.js +5 -0
  125. package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
  126. package/dist/utils/awaitWithTimeout.d.ts +5 -0
  127. package/dist/utils/awaitWithTimeout.d.ts.map +1 -0
  128. package/dist/utils/awaitWithTimeout.js +15 -0
  129. package/dist/utils/awaitWithTimeout.js.map +1 -0
  130. package/dist/utils/cloneFileDiffMetadata.d.ts +7 -0
  131. package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -0
  132. package/dist/utils/cloneFileDiffMetadata.js +16 -0
  133. package/dist/utils/cloneFileDiffMetadata.js.map +1 -0
  134. package/dist/utils/computeEstimatedDiffHeights.d.ts +3 -1
  135. package/dist/utils/computeEstimatedDiffHeights.d.ts.map +1 -1
  136. package/dist/utils/computeEstimatedDiffHeights.js +8 -1
  137. package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
  138. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts +5 -0
  139. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts.map +1 -0
  140. package/dist/utils/createCodeViewHeaderFooterHostElement.js +19 -0
  141. package/dist/utils/createCodeViewHeaderFooterHostElement.js.map +1 -0
  142. package/dist/utils/createPreElement.js +0 -1
  143. package/dist/utils/createPreElement.js.map +1 -1
  144. package/dist/utils/getDiffFileInput.d.ts +14 -0
  145. package/dist/utils/getDiffFileInput.d.ts.map +1 -0
  146. package/dist/utils/getDiffFileInput.js +24 -0
  147. package/dist/utils/getDiffFileInput.js.map +1 -0
  148. package/dist/utils/getDiffHunksRendererOptions.js +1 -0
  149. package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
  150. package/dist/utils/getFiletypeFromFileName.d.ts.map +1 -1
  151. package/dist/utils/getFiletypeFromFileName.js +2 -0
  152. package/dist/utils/getFiletypeFromFileName.js.map +1 -1
  153. package/dist/utils/hydratePartialDiff.d.ts +10 -0
  154. package/dist/utils/hydratePartialDiff.d.ts.map +1 -0
  155. package/dist/utils/hydratePartialDiff.js +140 -0
  156. package/dist/utils/hydratePartialDiff.js.map +1 -0
  157. package/dist/utils/iterateOverDiff.js +3 -3
  158. package/dist/utils/iterateOverDiff.js.map +1 -1
  159. package/dist/utils/parseDiffFromFile.d.ts +1 -1
  160. package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
  161. package/dist/utils/parseDiffFromFile.js +26 -5
  162. package/dist/utils/parseDiffFromFile.js.map +1 -1
  163. package/dist/utils/setWrapperNodeProps.js +0 -1
  164. package/dist/utils/setWrapperNodeProps.js.map +1 -1
  165. package/dist/utils/updateDiffHunks.d.ts +5 -1
  166. package/dist/utils/updateDiffHunks.d.ts.map +1 -1
  167. package/dist/utils/updateDiffHunks.js +48 -4
  168. package/dist/utils/updateDiffHunks.js.map +1 -1
  169. package/dist/worker/WorkerPoolManager.d.ts +7 -2
  170. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  171. package/dist/worker/WorkerPoolManager.js +78 -15
  172. package/dist/worker/WorkerPoolManager.js.map +1 -1
  173. package/dist/worker/index.d.ts +2 -2
  174. package/dist/worker/types.d.ts +7 -1
  175. package/dist/worker/types.d.ts.map +1 -1
  176. package/dist/worker/worker-portable.js +5 -3
  177. package/dist/worker/worker-portable.js.map +1 -1
  178. package/dist/worker/worker.js +5 -3
  179. package/dist/worker/worker.js.map +1 -1
  180. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchBrackets.js","names":[],"sources":["../../src/editor/matchBrackets.ts"],"sourcesContent":["import type { Position, Range, TextDocument } from './textDocument';\nimport type { EditorTokenizer } from './tokenzier';\n\nconst OPEN_BRACKETS = new Map([\n ['(', ')'],\n ['[', ']'],\n ['{', '}'],\n]);\n\nconst CLOSE_BRACKETS = new Map(\n [...OPEN_BRACKETS].map(([open, close]) => [close, open])\n);\n\nconst MAX_BRACKET_SCAN_LINES = 1_000;\nconst MAX_BRACKET_SCAN_CHARACTERS = 50_000;\n\ninterface BracketPosition extends Position {\n char: string;\n}\n\nexport function findBracketMatchRanges<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): [open: Range, close: Range] | undefined {\n const bracketPosition = findAdjacentBracket(\n textDocument,\n tokenizer,\n textDocument.normalizePosition(position)\n );\n if (bracketPosition === undefined) {\n return undefined;\n }\n\n const closingBracket = OPEN_BRACKETS.get(bracketPosition.char);\n const openingBracket = CLOSE_BRACKETS.get(bracketPosition.char);\n if (closingBracket !== undefined) {\n const matchPosition = findClosingBracket(\n textDocument,\n tokenizer,\n bracketPosition,\n closingBracket\n );\n return createBracketMatchRanges(bracketPosition, matchPosition);\n }\n if (openingBracket !== undefined) {\n const matchPosition = findOpeningBracket(\n textDocument,\n tokenizer,\n bracketPosition,\n openingBracket\n );\n return createBracketMatchRanges(matchPosition, bracketPosition);\n }\n return undefined;\n}\n\nfunction findAdjacentBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): BracketPosition | undefined {\n const previousPosition = getPreviousCharacterPosition(textDocument, position);\n if (previousPosition !== undefined) {\n const previousBracket = getBracketAtPosition(\n textDocument,\n tokenizer,\n previousPosition\n );\n if (previousBracket !== undefined) {\n return previousBracket;\n }\n }\n return getBracketAtPosition(textDocument, tokenizer, position);\n}\n\nfunction getPreviousCharacterPosition<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n position: Position\n): Position | undefined {\n if (position.character > 0) {\n return { line: position.line, character: position.character - 1 };\n }\n if (position.line <= 0) {\n return undefined;\n }\n const previousLine = position.line - 1;\n const previousLineLength = textDocument.getLineText(previousLine).length;\n if (previousLineLength === 0) {\n return undefined;\n }\n return { line: previousLine, character: previousLineLength - 1 };\n}\n\nfunction getBracketAtPosition<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n position: Position\n): BracketPosition | undefined {\n const lineText = textDocument.getLineText(position.line);\n const char = lineText[position.character];\n if (\n char === undefined ||\n (!OPEN_BRACKETS.has(char) && !CLOSE_BRACKETS.has(char)) ||\n isInIgnoredTokenRange(tokenizer, position)\n ) {\n return undefined;\n }\n return { ...position, char };\n}\n\nfunction findClosingBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n bracketPosition: BracketPosition,\n closingBracket: string\n): BracketPosition | undefined {\n let depth = 0;\n let scannedLines = 0;\n let scannedCharacters = 0;\n for (let line = bracketPosition.line; line < textDocument.lineCount; line++) {\n if (scannedLines >= MAX_BRACKET_SCAN_LINES) {\n return undefined;\n }\n scannedLines++;\n const lineText = textDocument.getLineText(line);\n const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);\n const ignoredRangeCursor = { index: 0 };\n const startCharacter =\n line === bracketPosition.line ? bracketPosition.character : 0;\n for (\n let character = startCharacter;\n character < lineText.length;\n character++\n ) {\n if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) {\n return undefined;\n }\n scannedCharacters++;\n if (\n isCharacterIgnoredForward(ignoredRanges, character, ignoredRangeCursor)\n ) {\n continue;\n }\n const char = lineText[character];\n if (char === bracketPosition.char) {\n depth++;\n } else if (char === closingBracket) {\n depth--;\n if (depth === 0) {\n return { line, character, char };\n }\n }\n }\n }\n return undefined;\n}\n\nfunction findOpeningBracket<LAnnotation>(\n textDocument: TextDocument<LAnnotation>,\n tokenizer: EditorTokenizer,\n bracketPosition: BracketPosition,\n openingBracket: string\n): BracketPosition | undefined {\n let depth = 0;\n let scannedLines = 0;\n let scannedCharacters = 0;\n for (let line = bracketPosition.line; line >= 0; line--) {\n if (scannedLines >= MAX_BRACKET_SCAN_LINES) {\n return undefined;\n }\n scannedLines++;\n const lineText = textDocument.getLineText(line);\n const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);\n const ignoredRangeCursor = {\n index: ignoredRanges === null ? -1 : ignoredRanges.length - 1,\n };\n const startCharacter =\n line === bracketPosition.line\n ? bracketPosition.character\n : lineText.length - 1;\n for (let character = startCharacter; character >= 0; character--) {\n if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) {\n return undefined;\n }\n scannedCharacters++;\n if (\n isCharacterIgnoredBackward(ignoredRanges, character, ignoredRangeCursor)\n ) {\n continue;\n }\n const char = lineText[character];\n if (char === bracketPosition.char) {\n depth++;\n } else if (char === openingBracket) {\n depth--;\n if (depth === 0) {\n return { line, character, char };\n }\n }\n }\n }\n return undefined;\n}\n\nfunction isInIgnoredTokenRange(\n tokenizer: EditorTokenizer,\n position: Position\n): boolean {\n const ranges = tokenizer.getStringCommentRegexpRangesInLine(position.line);\n return isCharacterInIgnoredRanges(ranges, position.character);\n}\n\nfunction isCharacterInIgnoredRanges(\n ranges: [start: number, end: number][] | null,\n character: number\n): boolean {\n if (ranges === null) {\n return false;\n }\n for (const [start, end] of ranges) {\n if (character < start) {\n return false;\n }\n if (character < end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCharacterIgnoredForward(\n ranges: [start: number, end: number][] | null,\n character: number,\n cursor: { index: number }\n): boolean {\n if (ranges === null) {\n return false;\n }\n while (cursor.index < ranges.length && character >= ranges[cursor.index][1]) {\n cursor.index++;\n }\n const range = ranges[cursor.index];\n return range !== undefined && character >= range[0];\n}\n\nfunction isCharacterIgnoredBackward(\n ranges: [start: number, end: number][] | null,\n character: number,\n cursor: { index: number }\n): boolean {\n if (ranges === null) {\n return false;\n }\n while (cursor.index >= 0 && character < ranges[cursor.index][0]) {\n cursor.index--;\n }\n const range = ranges[cursor.index];\n return range !== undefined && character < range[1];\n}\n\nfunction createBracketMatchRanges(\n firstPosition: Position | undefined,\n secondPosition: Position | undefined\n): [open: Range, close: Range] | undefined {\n if (firstPosition === undefined || secondPosition === undefined) {\n return;\n }\n return [\n createCharacterRange(firstPosition),\n createCharacterRange(secondPosition),\n ];\n}\n\nfunction createCharacterRange(position: Position): Range {\n return {\n start: { line: position.line, character: position.character },\n end: { line: position.line, character: position.character + 1 },\n };\n}\n"],"mappings":";AAGA,MAAM,gCAAgB,IAAI,IAAI;CAC5B,CAAC,KAAK,GAAG;CACT,CAAC,KAAK,GAAG;CACT,CAAC,KAAK,GAAG;AACX,CAAC;AAED,MAAM,iBAAiB,IAAI,IACzB,CAAC,GAAG,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,OAAO,IAAI,CAAC,CACzD;AAEA,MAAM,yBAAyB;AAC/B,MAAM,8BAA8B;AAMpC,SAAgB,uBACd,cACA,WACA,UACyC;CACzC,MAAM,kBAAkB,oBACtB,cACA,WACA,aAAa,kBAAkB,QAAQ,CACzC;CACA,IAAI,oBAAoB,KAAA,GACtB;CAGF,MAAM,iBAAiB,cAAc,IAAI,gBAAgB,IAAI;CAC7D,MAAM,iBAAiB,eAAe,IAAI,gBAAgB,IAAI;CAC9D,IAAI,mBAAmB,KAAA,GAOrB,OAAO,yBAAyB,iBANV,mBACpB,cACA,WACA,iBACA,cAE2D,CAAC;CAEhE,IAAI,mBAAmB,KAAA,GAOrB,OAAO,yBANe,mBACpB,cACA,WACA,iBACA,cAE0C,GAAG,eAAe;AAGlE;AAEA,SAAS,oBACP,cACA,WACA,UAC6B;CAC7B,MAAM,mBAAmB,6BAA6B,cAAc,QAAQ;CAC5E,IAAI,qBAAqB,KAAA,GAAW;EAClC,MAAM,kBAAkB,qBACtB,cACA,WACA,gBACF;EACA,IAAI,oBAAoB,KAAA,GACtB,OAAO;CAEX;CACA,OAAO,qBAAqB,cAAc,WAAW,QAAQ;AAC/D;AAEA,SAAS,6BACP,cACA,UACsB;CACtB,IAAI,SAAS,YAAY,GACvB,OAAO;EAAE,MAAM,SAAS;EAAM,WAAW,SAAS,YAAY;CAAE;CAElE,IAAI,SAAS,QAAQ,GACnB;CAEF,MAAM,eAAe,SAAS,OAAO;CACrC,MAAM,qBAAqB,aAAa,YAAY,YAAY,CAAC,CAAC;CAClE,IAAI,uBAAuB,GACzB;CAEF,OAAO;EAAE,MAAM;EAAc,WAAW,qBAAqB;CAAE;AACjE;AAEA,SAAS,qBACP,cACA,WACA,UAC6B;CAE7B,MAAM,OADW,aAAa,YAAY,SAAS,IAC/B,CAAC,CAAC,SAAS;CAC/B,IACE,SAAS,KAAA,KACR,CAAC,cAAc,IAAI,IAAI,KAAK,CAAC,eAAe,IAAI,IAAI,KACrD,sBAAsB,WAAW,QAAQ,GAEzC;CAEF,OAAO;EAAE,GAAG;EAAU;CAAK;AAC7B;AAEA,SAAS,mBACP,cACA,WACA,iBACA,gBAC6B;CAC7B,IAAI,QAAQ;CACZ,IAAI,eAAe;CACnB,IAAI,oBAAoB;CACxB,KAAK,IAAI,OAAO,gBAAgB,MAAM,OAAO,aAAa,WAAW,QAAQ;EAC3E,IAAI,gBAAgB,wBAClB;EAEF;EACA,MAAM,WAAW,aAAa,YAAY,IAAI;EAC9C,MAAM,gBAAgB,UAAU,mCAAmC,IAAI;EACvE,MAAM,qBAAqB,EAAE,OAAO,EAAE;EACtC,MAAM,iBACJ,SAAS,gBAAgB,OAAO,gBAAgB,YAAY;EAC9D,KACE,IAAI,YAAY,gBAChB,YAAY,SAAS,QACrB,aACA;GACA,IAAI,qBAAqB,6BACvB;GAEF;GACA,IACE,0BAA0B,eAAe,WAAW,kBAAkB,GAEtE;GAEF,MAAM,OAAO,SAAS;GACtB,IAAI,SAAS,gBAAgB,MAC3B;QACK,IAAI,SAAS,gBAAgB;IAClC;IACA,IAAI,UAAU,GACZ,OAAO;KAAE;KAAM;KAAW;IAAK;GAEnC;EACF;CACF;AAEF;AAEA,SAAS,mBACP,cACA,WACA,iBACA,gBAC6B;CAC7B,IAAI,QAAQ;CACZ,IAAI,eAAe;CACnB,IAAI,oBAAoB;CACxB,KAAK,IAAI,OAAO,gBAAgB,MAAM,QAAQ,GAAG,QAAQ;EACvD,IAAI,gBAAgB,wBAClB;EAEF;EACA,MAAM,WAAW,aAAa,YAAY,IAAI;EAC9C,MAAM,gBAAgB,UAAU,mCAAmC,IAAI;EACvE,MAAM,qBAAqB,EACzB,OAAO,kBAAkB,OAAO,KAAK,cAAc,SAAS,EAC9D;EACA,MAAM,iBACJ,SAAS,gBAAgB,OACrB,gBAAgB,YAChB,SAAS,SAAS;EACxB,KAAK,IAAI,YAAY,gBAAgB,aAAa,GAAG,aAAa;GAChE,IAAI,qBAAqB,6BACvB;GAEF;GACA,IACE,2BAA2B,eAAe,WAAW,kBAAkB,GAEvE;GAEF,MAAM,OAAO,SAAS;GACtB,IAAI,SAAS,gBAAgB,MAC3B;QACK,IAAI,SAAS,gBAAgB;IAClC;IACA,IAAI,UAAU,GACZ,OAAO;KAAE;KAAM;KAAW;IAAK;GAEnC;EACF;CACF;AAEF;AAEA,SAAS,sBACP,WACA,UACS;CAET,OAAO,2BADQ,UAAU,mCAAmC,SAAS,IAC9B,GAAG,SAAS,SAAS;AAC9D;AAEA,SAAS,2BACP,QACA,WACS;CACT,IAAI,WAAW,MACb,OAAO;CAET,KAAK,MAAM,CAAC,OAAO,QAAQ,QAAQ;EACjC,IAAI,YAAY,OACd,OAAO;EAET,IAAI,YAAY,KACd,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAS,0BACP,QACA,WACA,QACS;CACT,IAAI,WAAW,MACb,OAAO;CAET,OAAO,OAAO,QAAQ,OAAO,UAAU,aAAa,OAAO,OAAO,MAAM,CAAC,IACvE,OAAO;CAET,MAAM,QAAQ,OAAO,OAAO;CAC5B,OAAO,UAAU,KAAA,KAAa,aAAa,MAAM;AACnD;AAEA,SAAS,2BACP,QACA,WACA,QACS;CACT,IAAI,WAAW,MACb,OAAO;CAET,OAAO,OAAO,SAAS,KAAK,YAAY,OAAO,OAAO,MAAM,CAAC,IAC3D,OAAO;CAET,MAAM,QAAQ,OAAO,OAAO;CAC5B,OAAO,UAAU,KAAA,KAAa,YAAY,MAAM;AAClD;AAEA,SAAS,yBACP,eACA,gBACyC;CACzC,IAAI,kBAAkB,KAAA,KAAa,mBAAmB,KAAA,GACpD;CAEF,OAAO,CACL,qBAAqB,aAAa,GAClC,qBAAqB,cAAc,CACrC;AACF;AAEA,SAAS,qBAAqB,UAA2B;CACvD,OAAO;EACL,OAAO;GAAE,MAAM,SAAS;GAAM,WAAW,SAAS;EAAU;EAC5D,KAAK;GAAE,MAAM,SAAS;GAAM,WAAW,SAAS,YAAY;EAAE;CAChE;AACF"}
@@ -0,0 +1,45 @@
1
+ //#region src/editor/popover.d.ts
2
+ declare const POPOVER_BOUNDARY_LINES = 3;
3
+ declare const POPOVER_FLIP_HYSTERESIS_PX = 4;
4
+ /**
5
+ * The visible scroll viewport expressed in the overlay's coordinate space (the
6
+ * same space --popover-y uses), or undefined when no usable layout geometry
7
+ * exists (e.g. a detached unit-test DOM, where every rect is zero).
8
+ */
9
+ interface PopoverPlacementBounds {
10
+ /** The top of the popover in overlay coordinate space. */
11
+ top: number;
12
+ /** The bottom of the popover in overlay coordinate space. */
13
+ bottom: number;
14
+ }
15
+ interface PopoverManagerOptions {
16
+ hasActivePopover: () => boolean;
17
+ updateActivePopover: () => void;
18
+ }
19
+ /**
20
+ * Shared placement logic for the editor's overlay popovers: each anchors below
21
+ * or above a document position, flipping to the opposite side only when the
22
+ * preferred side would be clipped by the visible scrollport.
23
+ */
24
+ declare class PopoverManager {
25
+ #private;
26
+ constructor(options: PopoverManagerOptions);
27
+ setViewportElements(fileContainer: HTMLElement, codeElement: HTMLElement): void;
28
+ cleanUp(): void;
29
+ resetPlacement(): void;
30
+ setPlacement(placement: 'preferred' | 'fallback'): void;
31
+ choosePlacement(input: {
32
+ /** Bounds for the anchor's preferred side (e.g. above for backward, below for forward). */preferred: PopoverPlacementBounds; /** Bounds for the opposite edge we flip to when the preferred side has no room. */
33
+ fallback: PopoverPlacementBounds; /** The visible scrollport in overlay coordinate space, or undefined without layout geometry (e.g. a detached test DOM). */
34
+ viewport: PopoverPlacementBounds | undefined; /** The popover's measured height; 0 before it has laid out. */
35
+ popoverHeight: number; /** Whether the anchor is within the document's first/last rows; only used as a fallback signal when `viewport` is unavailable. */
36
+ atDocumentEdge: boolean;
37
+ }): 'preferred' | 'fallback';
38
+ /**
39
+ * Returns the bounds of the popover in overlay coordinate space.
40
+ */
41
+ getPlacementBounds(): PopoverPlacementBounds | undefined;
42
+ }
43
+ //#endregion
44
+ export { POPOVER_BOUNDARY_LINES, POPOVER_FLIP_HYSTERESIS_PX, PopoverManager, PopoverManagerOptions, PopoverPlacementBounds };
45
+ //# sourceMappingURL=popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.d.ts","names":[],"sources":["../../src/editor/popover.ts"],"mappings":";cAIa,sBAAA;AAAA,cAKA,0BAAA;;;;AALsB;AAKnC;UAOiB,sBAAA;;EAEf,GAAA;EATqC;EAWrC,MAAM;AAAA;AAAA,UAGS,qBAAA;EACf,gBAAA;EACA,mBAAmB;AAAA;;;;AAAA;AAQrB;cAAa,cAAA;EAAA;EAcX,WAAA,CAAY,OAAA,EAAS,qBAAA;EAKrB,mBAAA,CACE,aAAA,EAAe,WAAA,EACf,WAAA,EAAa,WAAA;EAaf,OAAA;EAaA,cAAA;EAIA,YAAA,CAAa,SAAA;EAOb,eAAA,CAAgB,KAAA;IAwCM,2FAtCpB,SAAA,EAAW,sBAAA,EAsC+B;IApC1C,QAAA,EAAU,sBAAA,EAhDZ;IAkDE,QAAA,EAAU,sBAAA,cAlDA;IAoDV,aAAA,UA9Ce;IAgDf,cAAA;EAAA;EA/CA;;;EA6EF,kBAAA,IAAsB,sBAAA;AAAA"}
@@ -0,0 +1,145 @@
1
+ import { addEventListener } from "./utils.js";
2
+ //#region src/editor/popover.ts
3
+ const POPOVER_BOUNDARY_LINES = 3;
4
+ const POPOVER_FLIP_HYSTERESIS_PX = 4;
5
+ /**
6
+ * Shared placement logic for the editor's overlay popovers: each anchors below
7
+ * or above a document position, flipping to the opposite side only when the
8
+ * preferred side would be clipped by the visible scrollport.
9
+ */
10
+ var PopoverManager = class {
11
+ #fileContainer;
12
+ #codeElement;
13
+ #scrollContainer;
14
+ #scrollContainerSource;
15
+ #cachedCodeRect;
16
+ #cachedScrollContainerRect;
17
+ #viewportRectsDirty = true;
18
+ #viewportRectListenersDisposes;
19
+ #placement;
20
+ #hasActivePopover;
21
+ #updateActivePopover;
22
+ constructor(options) {
23
+ this.#hasActivePopover = options.hasActivePopover;
24
+ this.#updateActivePopover = options.updateActivePopover;
25
+ }
26
+ setViewportElements(fileContainer, codeElement) {
27
+ this.#fileContainer = fileContainer;
28
+ if (this.#codeElement === codeElement) return;
29
+ this.#codeElement = codeElement;
30
+ this.#viewportRectsDirty = true;
31
+ }
32
+ cleanUp() {
33
+ this.#codeElement = void 0;
34
+ this.#fileContainer = void 0;
35
+ this.#scrollContainer = void 0;
36
+ this.#scrollContainerSource = void 0;
37
+ this.#cachedCodeRect = void 0;
38
+ this.#cachedScrollContainerRect = void 0;
39
+ this.#viewportRectsDirty = true;
40
+ this.#viewportRectListenersDisposes?.forEach((dispose) => dispose());
41
+ this.#viewportRectListenersDisposes = void 0;
42
+ this.#placement = void 0;
43
+ }
44
+ resetPlacement() {
45
+ this.#placement = void 0;
46
+ }
47
+ setPlacement(placement) {
48
+ this.#placement = placement;
49
+ }
50
+ choosePlacement(input) {
51
+ const { preferred, fallback, viewport, popoverHeight, atDocumentEdge } = input;
52
+ let placement;
53
+ if (viewport !== void 0 && popoverHeight > 0) {
54
+ const fits = (bounds, margin = 0) => bounds.top >= viewport.top + margin && bounds.bottom <= viewport.bottom - margin;
55
+ if (this.#placement === "fallback" && fits(fallback) && !fits(preferred, 4)) placement = "fallback";
56
+ else if (!fits(preferred) && fits(fallback)) placement = "fallback";
57
+ else placement = "preferred";
58
+ } else placement = atDocumentEdge ? "fallback" : "preferred";
59
+ this.#placement = placement;
60
+ return placement;
61
+ }
62
+ /**
63
+ * Returns the bounds of the popover in overlay coordinate space.
64
+ */
65
+ getPlacementBounds() {
66
+ const codeRect = this.#getCodeRect();
67
+ if (codeRect === void 0) return;
68
+ const scrollContainerRect = this.#getScrollContainerRect();
69
+ let topScreen;
70
+ let bottomScreen;
71
+ if (scrollContainerRect !== void 0) {
72
+ topScreen = scrollContainerRect.top;
73
+ bottomScreen = scrollContainerRect.bottom;
74
+ } else {
75
+ topScreen = 0;
76
+ bottomScreen = window.innerHeight;
77
+ }
78
+ if (bottomScreen <= topScreen) return;
79
+ return {
80
+ top: topScreen - codeRect.top,
81
+ bottom: bottomScreen - codeRect.top
82
+ };
83
+ }
84
+ #getScrollContainer() {
85
+ const fileContainer = this.#fileContainer;
86
+ if (fileContainer === void 0) return;
87
+ if (this.#scrollContainerSource === fileContainer) {
88
+ if (this.#scrollContainer === void 0 || this.#scrollContainer.isConnected) return this.#scrollContainer;
89
+ }
90
+ let element = fileContainer.parentElement;
91
+ while (element !== null) {
92
+ const overflowY = getComputedStyle(element).overflowY;
93
+ if (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") {
94
+ this.#scrollContainer = element;
95
+ this.#scrollContainerSource = fileContainer;
96
+ return element;
97
+ }
98
+ element = element.parentElement;
99
+ }
100
+ this.#scrollContainer = void 0;
101
+ this.#scrollContainerSource = fileContainer;
102
+ }
103
+ #refreshViewportRectsIfNeeded() {
104
+ if (!this.#viewportRectsDirty) return;
105
+ this.#ensureViewportRectListeners();
106
+ this.#cachedCodeRect = this.#codeElement?.getBoundingClientRect();
107
+ this.#cachedScrollContainerRect = this.#getScrollContainer()?.getBoundingClientRect();
108
+ this.#viewportRectsDirty = false;
109
+ }
110
+ #getCodeRect() {
111
+ this.#refreshViewportRectsIfNeeded();
112
+ return this.#cachedCodeRect;
113
+ }
114
+ #getScrollContainerRect() {
115
+ this.#refreshViewportRectsIfNeeded();
116
+ return this.#cachedScrollContainerRect;
117
+ }
118
+ #ensureViewportRectListeners() {
119
+ if (this.#viewportRectListenersDisposes !== void 0) return;
120
+ let repositionRafId;
121
+ const markDirty = () => {
122
+ this.#viewportRectsDirty = true;
123
+ if (!this.#hasActivePopover() || repositionRafId !== void 0) return;
124
+ repositionRafId = requestAnimationFrame(() => {
125
+ repositionRafId = void 0;
126
+ this.#updateActivePopover();
127
+ });
128
+ };
129
+ const scroller = this.#getScrollContainer();
130
+ this.#viewportRectListenersDisposes = [
131
+ scroller !== void 0 ? addEventListener(scroller, "scroll", markDirty, { passive: true }) : addEventListener(window, "scroll", markDirty, { passive: true }),
132
+ addEventListener(window, "resize", markDirty, { passive: true }),
133
+ () => {
134
+ if (repositionRafId !== void 0) {
135
+ cancelAnimationFrame(repositionRafId);
136
+ repositionRafId = void 0;
137
+ }
138
+ }
139
+ ];
140
+ }
141
+ };
142
+ //#endregion
143
+ export { POPOVER_BOUNDARY_LINES, POPOVER_FLIP_HYSTERESIS_PX, PopoverManager };
144
+
145
+ //# sourceMappingURL=popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.js","names":["#hasActivePopover","#updateActivePopover","#fileContainer","#codeElement","#viewportRectsDirty","#scrollContainer","#scrollContainerSource","#cachedCodeRect","#cachedScrollContainerRect","#viewportRectListenersDisposes","#placement","#getCodeRect","#getScrollContainerRect","#ensureViewportRectListeners","#getScrollContainer","#refreshViewportRectsIfNeeded"],"sources":["../../src/editor/popover.ts"],"sourcesContent":["import { addEventListener } from './utils';\n\n// Lines from the document edge at which the no-viewport fallback heuristic\n// (see `atDocumentEdge` below) flips placement.\nexport const POPOVER_BOUNDARY_LINES = 3;\n\n// Extra clearance (px) `preferred` must regain before a popover that flipped\n// to `fallback` flips back, so an anchor sitting right at the flip boundary\n// doesn't flicker between sides on every recompute.\nexport const POPOVER_FLIP_HYSTERESIS_PX = 4;\n\n/**\n * The visible scroll viewport expressed in the overlay's coordinate space (the\n * same space --popover-y uses), or undefined when no usable layout geometry\n * exists (e.g. a detached unit-test DOM, where every rect is zero).\n */\nexport interface PopoverPlacementBounds {\n /** The top of the popover in overlay coordinate space. */\n top: number;\n /** The bottom of the popover in overlay coordinate space. */\n bottom: number;\n}\n\nexport interface PopoverManagerOptions {\n hasActivePopover: () => boolean;\n updateActivePopover: () => void;\n}\n\n/**\n * Shared placement logic for the editor's overlay popovers: each anchors below\n * or above a document position, flipping to the opposite side only when the\n * preferred side would be clipped by the visible scrollport.\n */\nexport class PopoverManager {\n #fileContainer?: HTMLElement;\n #codeElement?: HTMLElement;\n #scrollContainer?: HTMLElement;\n #scrollContainerSource?: HTMLElement;\n #cachedCodeRect?: DOMRect;\n #cachedScrollContainerRect?: DOMRect;\n #viewportRectsDirty = true;\n #viewportRectListenersDisposes?: (() => void)[];\n #placement?: 'preferred' | 'fallback';\n\n readonly #hasActivePopover: () => boolean;\n readonly #updateActivePopover: () => void;\n\n constructor(options: PopoverManagerOptions) {\n this.#hasActivePopover = options.hasActivePopover;\n this.#updateActivePopover = options.updateActivePopover;\n }\n\n setViewportElements(\n fileContainer: HTMLElement,\n codeElement: HTMLElement\n ): void {\n this.#fileContainer = fileContainer;\n if (this.#codeElement === codeElement) {\n return;\n }\n this.#codeElement = codeElement;\n // A new [data-code] element invalidates the cached viewport rects (e.g. a\n // full re-render loaded a different file, possibly at a different on-screen\n // position) even without an intervening scroll/resize event.\n this.#viewportRectsDirty = true;\n }\n\n cleanUp(): void {\n this.#codeElement = undefined;\n this.#fileContainer = undefined;\n this.#scrollContainer = undefined;\n this.#scrollContainerSource = undefined;\n this.#cachedCodeRect = undefined;\n this.#cachedScrollContainerRect = undefined;\n this.#viewportRectsDirty = true;\n this.#viewportRectListenersDisposes?.forEach((dispose) => dispose());\n this.#viewportRectListenersDisposes = undefined;\n this.#placement = undefined;\n }\n\n resetPlacement(): void {\n this.#placement = undefined;\n }\n\n setPlacement(placement: 'preferred' | 'fallback'): void {\n this.#placement = placement;\n }\n\n // Flips to the opposite side only when the preferred side would be clipped by\n // the real scrollport and the fallback side fits; without viewport geometry,\n // falls back to the document-edge signal so the first/last rows still flip.\n choosePlacement(input: {\n /** Bounds for the anchor's preferred side (e.g. above for backward, below for forward). */\n preferred: PopoverPlacementBounds;\n /** Bounds for the opposite edge we flip to when the preferred side has no room. */\n fallback: PopoverPlacementBounds;\n /** The visible scrollport in overlay coordinate space, or undefined without layout geometry (e.g. a detached test DOM). */\n viewport: PopoverPlacementBounds | undefined;\n /** The popover's measured height; 0 before it has laid out. */\n popoverHeight: number;\n /** Whether the anchor is within the document's first/last rows; only used as a fallback signal when `viewport` is unavailable. */\n atDocumentEdge: boolean;\n }): 'preferred' | 'fallback' {\n const { preferred, fallback, viewport, popoverHeight, atDocumentEdge } =\n input;\n let placement: 'preferred' | 'fallback';\n if (viewport !== undefined && popoverHeight > 0) {\n const fits = (bounds: PopoverPlacementBounds, margin = 0): boolean =>\n bounds.top >= viewport.top + margin &&\n bounds.bottom <= viewport.bottom - margin;\n if (\n this.#placement === 'fallback' &&\n fits(fallback) &&\n !fits(preferred, POPOVER_FLIP_HYSTERESIS_PX)\n ) {\n placement = 'fallback';\n } else if (!fits(preferred) && fits(fallback)) {\n placement = 'fallback';\n } else {\n placement = 'preferred';\n }\n } else {\n placement = atDocumentEdge ? 'fallback' : 'preferred';\n }\n this.#placement = placement;\n return placement;\n }\n\n /**\n * Returns the bounds of the popover in overlay coordinate space.\n */\n getPlacementBounds(): PopoverPlacementBounds | undefined {\n const codeRect = this.#getCodeRect();\n if (codeRect === undefined) {\n return undefined;\n }\n const scrollContainerRect = this.#getScrollContainerRect();\n let topScreen: number;\n let bottomScreen: number;\n if (scrollContainerRect !== undefined) {\n topScreen = scrollContainerRect.top;\n bottomScreen = scrollContainerRect.bottom;\n } else {\n // No scrollable ancestor: the page itself scrolls, so the window is the\n // clip region.\n topScreen = 0;\n bottomScreen = window.innerHeight;\n }\n if (bottomScreen <= topScreen) {\n return undefined;\n }\n return {\n top: topScreen - codeRect.top,\n bottom: bottomScreen - codeRect.top,\n };\n }\n\n // The nearest scrollable ancestor of the file container -- the element whose\n // overflow clips overlay widgets. Walks the light-DOM ancestor chain (the\n // overlay lives in the file container's shadow root) and caches the result per\n // file container so it is not re-resolved on every render.\n //\n // Known limitations: this only inspects light-DOM ancestors outside the\n // shadow root, so it cannot see a clip boundary inside it (e.g. `[data-code]`\n // itself uses `overflow-y: clip`, which also is not one of the values matched\n // below) and cannot cross into an outer shadow root if the file container is\n // itself nested in another web component. It also only considers the\n // *nearest* scrollable ancestor, not the intersection of every clipping\n // ancestor, so a popover could still be clipped by an outer scroll context\n // even when it fits within this nearer one.\n #getScrollContainer(): HTMLElement | undefined {\n const fileContainer = this.#fileContainer;\n if (fileContainer === undefined) {\n return undefined;\n }\n if (this.#scrollContainerSource === fileContainer) {\n // A cached element can detach across re-mounts; re-resolve in that case\n // but otherwise trust the cache (including a cached \"no scroller\" result).\n if (\n this.#scrollContainer === undefined ||\n this.#scrollContainer.isConnected\n ) {\n return this.#scrollContainer;\n }\n }\n let element: HTMLElement | null = fileContainer.parentElement;\n while (element !== null) {\n const overflowY = getComputedStyle(element).overflowY;\n if (\n overflowY === 'auto' ||\n overflowY === 'scroll' ||\n overflowY === 'overlay'\n ) {\n this.#scrollContainer = element;\n this.#scrollContainerSource = fileContainer;\n return element;\n }\n element = element.parentElement;\n }\n this.#scrollContainer = undefined;\n this.#scrollContainerSource = fileContainer;\n return undefined;\n }\n\n // Lazily measures and caches the screen-space rects getOverlayViewport needs,\n // refreshing only when a scroll or resize event marks them dirty rather than\n // on every call. Selection-action placement can run once per keystroke while a\n // ranged selection stays open, so re-measuring via getBoundingClientRect on\n // every call would force a synchronous layout reflow on that hot path.\n #refreshViewportRectsIfNeeded(): void {\n if (!this.#viewportRectsDirty) {\n return;\n }\n this.#ensureViewportRectListeners();\n this.#cachedCodeRect = this.#codeElement?.getBoundingClientRect();\n this.#cachedScrollContainerRect =\n this.#getScrollContainer()?.getBoundingClientRect();\n this.#viewportRectsDirty = false;\n }\n\n #getCodeRect(): DOMRect | undefined {\n this.#refreshViewportRectsIfNeeded();\n return this.#cachedCodeRect;\n }\n\n #getScrollContainerRect(): DOMRect | undefined {\n this.#refreshViewportRectsIfNeeded();\n return this.#cachedScrollContainerRect;\n }\n\n // Subscribes once to events that can move or resize the cached rects: a scroll\n // on the resolved scroll container (or the window, when the page itself\n // scrolls) and a window resize. Scrolling/resizing can change whether the open\n // popover still fits, so this re-runs placement rAF-throttled.\n #ensureViewportRectListeners(): void {\n if (this.#viewportRectListenersDisposes !== undefined) {\n return;\n }\n let repositionRafId: number | undefined;\n const markDirty = () => {\n this.#viewportRectsDirty = true;\n if (!this.#hasActivePopover() || repositionRafId !== undefined) {\n return;\n }\n repositionRafId = requestAnimationFrame(() => {\n repositionRafId = undefined;\n this.#updateActivePopover();\n });\n };\n const scroller = this.#getScrollContainer();\n this.#viewportRectListenersDisposes = [\n scroller !== undefined\n ? addEventListener(scroller, 'scroll', markDirty, { passive: true })\n : addEventListener(window, 'scroll', markDirty, { passive: true }),\n addEventListener(window, 'resize', markDirty, { passive: true }),\n () => {\n if (repositionRafId !== undefined) {\n cancelAnimationFrame(repositionRafId);\n repositionRafId = undefined;\n }\n },\n ];\n }\n}\n"],"mappings":"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"}
@@ -141,6 +141,18 @@ declare function extendSelections(selections: EditorSelection[], target: EditorS
141
141
  * Merges overlapping selections.
142
142
  */
143
143
  declare function mergeOverlappingSelections(selections: EditorSelection[]): EditorSelection[];
144
+ /**
145
+ * Converts selections into merged line blocks for line-based commands.
146
+ */
147
+ declare function getSelectedLineBlocks(selections: EditorSelection[]): {
148
+ startLine: number;
149
+ endLine: number;
150
+ }[];
151
+ /**
152
+ * Moves a selection's line positions after its lines are shifted, clamping to
153
+ * the target document bounds.
154
+ */
155
+ declare function shiftSelectionLines(selection: EditorSelection, direction: -1 | 1, lineCount: number, getLineLength: (line: number) => number): EditorSelection;
144
156
  /**
145
157
  * Finds the next matching word and updates the selections.
146
158
  */
@@ -152,7 +164,7 @@ declare function getDocumentFullSelection(textDocument: TextDocument<unknown>):
152
164
  /**
153
165
  * Get the boundary selection of the document.
154
166
  */
155
- declare function getDocumentBoundarySelection(textDocument: TextDocument<unknown>, atEnd: boolean): EditorSelection;
167
+ declare function getDocumentBoundarySelection(textDocument: TextDocument<unknown>, atEnd: boolean, trimmedEndNewLine?: boolean): EditorSelection;
156
168
  /**
157
169
  * Get the clipboard text of the selections for the given text document. Used by
158
170
  * both copy and cut so the two stay in sync. Overlapping regions (e.g. several
@@ -175,5 +187,5 @@ declare function getSelectionAnchor(lineElement: HTMLElement, character: number)
175
187
  */
176
188
  declare function expandCollapsedSelectionToWord(textDocument: TextDocument<unknown>, selection: EditorSelection): EditorSelection;
177
189
  //#endregion
178
- export { AutoSurround, DirectionBackward, DirectionForward, DirectionNone, EditorSelection, SelectionDirection, 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, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects };
190
+ export { AutoSurround, DirectionBackward, DirectionForward, DirectionNone, EditorSelection, SelectionDirection, 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, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines };
179
191
  //# sourceMappingURL=selection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection.d.ts","names":[],"sources":["../../src/editor/selection.ts"],"mappings":";;;;cAYa,iBAAA;AAAA,cACA,aAAA;AAAA,cACA,gBAAA;AAAA,KAED,kBAAA,UACD,iBAAA,UACA,aAAA,UACA,gBAAA;AAAA,UAEM,eAAA,SAAwB,KAAK;EAC5C,SAAA,EAAW,kBAAA;AAAA;AATb;;;AAAA,iBAegB,gBAAA,CACd,KAAA,EAAO,WAAA,EACP,SAAA,GAAW,kBAAA,GACV,eAAA;AAlBuB;AAC1B;;AAD0B,iBAkCV,kBAAA,CACd,YAAA,EAAc,YAAA,WACd,SAAA,EAAW,eAAA,EACX,OAAA,UACA,OAAA,aACE,KAAA,EAAO,QAAA,IAAY,aAAA,EAAe,eAAA;;AAtCT;AAE7B;iBAqGgB,aAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,EAAY,eAAA,IACZ,QAAA,qEACC,eAAA;;;;iBAoEa,iBAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,EAAY,eAAA,IACZ,QAAA,qEACC,eAAA;;;;iBAwBa,2BAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,IAAA,EAAM,gBAAA,EACN,eAAA,GAAkB,kBAAA,CAAmB,WAAA,KACrC,OAAA,WACA,YAAA;EAEA,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;AA7MX;;;AAAA,iBAwYgB,4BAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,KAAA,YACA,eAAA,GAAkB,kBAAA,CAAmB,WAAA,KACrC,YAAA;EAEA,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;AAAA,KAiLC,YAAA;AAhkBmB;AAM/B;;;AAN+B,iBA2lBf,+BAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,IAAA,UACA,YAAA,GAAe,YAAA;;;;;iBAuBD,0BAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAErC,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;;;AAnnBO;AAgBlB;;iBAwuBgB,sCAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAErC,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;;;;;iBAsBK,uCAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,gBAAA,IAAoB,IAAA,UAAc,SAAA,qBAClC,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAErC,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;;;;;iBA8CK,mCAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAErC,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;;;;;iBAyBK,2BAAA,CACd,YAAA,EAAc,YAAA,WACd,SAAA,EAAW,eAAA,EACX,OAAA,aACE,KAAA,EAAO,QAAA,EAAU,GAAA,EAAK,QAAA;;;;iBAiEV,gCAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,UAAA,EAAY,eAAA,IACZ,OAAA,WACA,eAAA,GAAkB,kBAAA,CAAmB,WAAA,KACrC,OAAA;EAEA,cAAA,EAAgB,eAAA;EAChB,MAAA,GAAS,kBAAA;AAAA;;;AA31BO;iBAo4BF,oBAAA,CACd,SAAA,EAAW,eAAA,GAAkB,KAAK;;;;iBAWpB,gBAAA,CAAiB,SAAA,EAAW,eAAA,GAAkB,QAAQ;;;;iBAQtD,cAAA,CAAe,QAAgB;;;;iBAW/B,mBAAA,CACd,CAAA,EAAG,eAAA,GAAkB,KAAA,EACrB,CAAA,EAAG,eAAA,GAAkB,KAAA;;;AA71BL;iBAw3BF,eAAA,CAAgB,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,QAAQ;;;;iBAUxC,wCAAA,CACd,YAAA,EAAc,YAAA,WACd,YAAA,UACA,WAAA,WACC,eAAe;;;;;;;;;;iBAmDF,yBAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,WAAqB,eAAA,IACrB,gBAAA,EAAkB,aAAA,6BAClB,KAAA,WAAgB,gBAAA,KACf,eAAA;;;;iBAoBa,mBAAA,CACd,eAAA,EAAiB,eAAA,EACjB,cAAA,EAAgB,eAAA,GACf,eAAA;;;;;iBAqCa,eAAA,CACd,QAAA,EAAU,eAAA,EACV,MAAA,EAAQ,eAAA,GACP,eAAA;;;;iBA4Ca,gBAAA,CACd,UAAA,EAAY,eAAA,IACZ,MAAA,EAAQ,eAAA,GACP,eAAA;;;;iBAUa,0BAAA,CACd,UAAA,EAAY,eAAA,KACX,eAAe;AA51BlB;;;AAAA,iBAy4BgB,YAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,EAAY,eAAA,KACX,eAAA;;;;iBAkDa,wBAAA,CACd,YAAA,EAAc,YAAA,YACb,eAAe;;;;iBAaF,4BAAA,CACd,YAAA,EAAc,YAAA,WACd,KAAA,YACC,eAAe;;;;;;;;iBAyDF,gBAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,EAAY,eAAe;AAAA,iBA2Ib,mBAAA,CACd,YAAA,EAAc,YAAA,WACd,UAAA,EAAY,eAAA;EAEZ,IAAA;EACA,KAAA,EAAO,gBAAA;EACP,oBAAA;AAAA;;;;iBA4Bc,kBAAA,CACd,WAAA,EAAa,WAAA,EACb,SAAA,YACE,IAAI;AAlgCR;;;AAAA,iBAikCgB,8BAAA,CACd,YAAA,EAAc,YAAA,WACd,SAAA,EAAW,eAAA,GACV,eAAA"}
1
+ {"version":3,"file":"selection.d.ts","names":[],"sources":["../../src/editor/selection.ts"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { endsWithLineBreak } from "./utils.js";
1
+ import { createSegmenter, endsWithLineBreak, getGraphemeSegmenter } from "./utils.js";
2
2
  import { applyDocumentChangeToLineAnnotations } from "./lineAnnotations.js";
3
3
  //#region src/editor/selection.ts
4
4
  const DirectionBackward = -1;
@@ -81,12 +81,13 @@ function mapCursorMove(textDocument, selections, shortcut) {
81
81
  return selections.map((selection) => {
82
82
  let { line, character } = shortcut === "up" || shortcut === "left" ? selection.start : selection.end;
83
83
  if (shortcut === "textStart" || shortcut === "start" || shortcut === "end") {
84
+ const caret = getCaretPosition(selection);
85
+ line = caret.line;
86
+ character = caret.character;
84
87
  if (shortcut === "textStart") {
85
88
  const indent = getLeadingSpaces(textDocument.getLineText(line));
86
89
  character = character === indent ? 0 : indent;
87
90
  } else character = shortcut === "start" ? 0 : textDocument.getLineLength(line);
88
- if (selection.direction === -1) line = selection.start.line;
89
- else line = selection.end.line;
90
91
  } else if (shortcut === "up") line = Math.max(0, line - 1);
91
92
  else if (shortcut === "down") line = Math.min(Math.max(lineCount - 1, 0), line + 1);
92
93
  else if (isCollapsedSelection(selection)) {
@@ -221,7 +222,7 @@ function getNextSelectionOffsetPairAfterReplace(textDocument, entry, offsetDelta
221
222
  const insertStart = entry.start + offsetDelta;
222
223
  const insertEnd = insertStart + newText.length;
223
224
  if (entry.end - entry.start > 0) {
224
- const originalText = textDocument.getText().slice(entry.start, entry.end);
225
+ const originalText = textDocument.getTextSlice(entry.start, entry.end);
225
226
  const preservedOffset = newText.indexOf(originalText);
226
227
  if (preservedOffset !== -1 && preservedOffset + originalText.length <= newText.length) {
227
228
  const rangeStart = insertStart + preservedOffset;
@@ -375,7 +376,6 @@ function getAutoSurroundReplacementTexts(textDocument, selections, char, autoSur
375
376
  * insertTranspose (Ctrl+T) behavior.
376
377
  */
377
378
  function applyTransposeToSelections(textDocument, selections, lineAnnotations) {
378
- const text = textDocument.getText();
379
379
  const edits = [];
380
380
  const nextOffsetPairs = [];
381
381
  for (const selection of selections) {
@@ -420,7 +420,7 @@ function applyTransposeToSelections(textDocument, selections, lineAnnotations) {
420
420
  const prevGraphemeStart = prevLength > 0 ? findClusterBreak(prevLineText, prevLength, false, getLineGraphemeStarts(prevLineText)) : prevLength;
421
421
  const firstEnd = findClusterBreak(lineText, 0, true, graphemeStarts);
422
422
  const prevStart = prevEnd - (prevLength - prevGraphemeStart);
423
- const newText = lineText.slice(0, firstEnd) + text.slice(prevEnd, offset) + prevLineText.slice(prevGraphemeStart, prevLength);
423
+ const newText = lineText.slice(0, firstEnd) + textDocument.getTextSlice(prevEnd, offset) + prevLineText.slice(prevGraphemeStart, prevLength);
424
424
  edit = {
425
425
  start: prevStart,
426
426
  end: offset + firstEnd,
@@ -738,30 +738,91 @@ function extendSelections(selections, target) {
738
738
  */
739
739
  function mergeOverlappingSelections(selections) {
740
740
  if (selections.length <= 1) return selections;
741
- const selected = /* @__PURE__ */ new Set();
742
- const accepted = [];
743
- for (let i = selections.length - 1; i >= 0; i--) {
744
- const selection = selections[i];
745
- if (selection === void 0) continue;
746
- let left = 0;
747
- let right = accepted.length;
748
- while (left < right) {
749
- const mid = Math.floor((left + right) / 2);
750
- const candidate = accepted[mid]?.selection;
751
- if (candidate === void 0) break;
752
- if (comparePosition(candidate.start, selection.start) < 0) left = mid + 1;
753
- else right = mid;
741
+ const ordered = selections.map((selection, index) => ({
742
+ index,
743
+ selection
744
+ })).sort((a, b) => {
745
+ const startOrder = comparePosition(a.selection.start, b.selection.start);
746
+ if (startOrder !== 0) return startOrder;
747
+ const endOrder = comparePosition(a.selection.end, b.selection.end);
748
+ return endOrder !== 0 ? endOrder : a.index - b.index;
749
+ });
750
+ const merged = [];
751
+ let current = ordered[0];
752
+ for (const entry of ordered.slice(1)) {
753
+ if (selectionIntersects(current.selection, entry.selection)) {
754
+ const latest = entry.index > current.index ? entry : current;
755
+ const start = comparePosition(entry.selection.start, current.selection.start) < 0 ? entry.selection.start : current.selection.start;
756
+ const end = comparePosition(entry.selection.end, current.selection.end) > 0 ? entry.selection.end : current.selection.end;
757
+ let direction = latest.selection.direction;
758
+ if (direction === 0 && comparePosition(start, end) !== 0) direction = comparePosition(latest.selection.start, start) === 0 ? -1 : 1;
759
+ current = {
760
+ index: latest.index,
761
+ selection: {
762
+ direction,
763
+ end,
764
+ start
765
+ }
766
+ };
767
+ continue;
754
768
  }
755
- const previous = accepted[left - 1]?.selection;
756
- const next = accepted[left]?.selection;
757
- if (previous !== void 0 && selectionIntersects(previous, selection) || next !== void 0 && selectionIntersects(next, selection)) continue;
758
- accepted.splice(left, 0, {
759
- index: i,
760
- selection
761
- });
762
- selected.add(i);
769
+ merged.push(current);
770
+ current = entry;
763
771
  }
764
- return selections.filter((_, index) => selected.has(index));
772
+ merged.push(current);
773
+ return merged.sort((a, b) => a.index - b.index).map(({ selection }) => selection);
774
+ }
775
+ /**
776
+ * Converts selections into merged line blocks for line-based commands.
777
+ */
778
+ function getSelectedLineBlocks(selections) {
779
+ const blocks = selections.map((selection) => {
780
+ let endLine = selection.end.line;
781
+ if (selection.end.character === 0 && comparePosition(selection.start, selection.end) !== 0) endLine--;
782
+ return {
783
+ startLine: selection.start.line,
784
+ endLine: Math.max(selection.start.line, endLine)
785
+ };
786
+ }).sort((a, b) => {
787
+ const startOrder = a.startLine - b.startLine;
788
+ return startOrder !== 0 ? startOrder : a.endLine - b.endLine;
789
+ });
790
+ const merged = [];
791
+ for (const block of blocks) {
792
+ const previous = merged.at(-1);
793
+ if (previous !== void 0 && block.startLine <= previous.endLine + 1) previous.endLine = Math.max(previous.endLine, block.endLine);
794
+ else merged.push({ ...block });
795
+ }
796
+ return merged;
797
+ }
798
+ /**
799
+ * Moves a selection's line positions after its lines are shifted, clamping to
800
+ * the target document bounds.
801
+ */
802
+ function shiftSelectionLines(selection, direction, lineCount, getLineLength) {
803
+ const shiftPosition = (position) => {
804
+ const line = position.line + direction;
805
+ if (line >= lineCount) {
806
+ const lastLine = Math.max(0, lineCount - 1);
807
+ return {
808
+ line: lastLine,
809
+ character: getLineLength(lastLine)
810
+ };
811
+ }
812
+ if (line < 0) return {
813
+ line: 0,
814
+ character: 0
815
+ };
816
+ return {
817
+ line,
818
+ character: position.character
819
+ };
820
+ };
821
+ return {
822
+ start: shiftPosition(selection.start),
823
+ end: shiftPosition(selection.end),
824
+ direction: selection.direction
825
+ };
765
826
  }
766
827
  /**
767
828
  * Finds the next matching word and updates the selections.
@@ -801,11 +862,16 @@ function getDocumentFullSelection(textDocument) {
801
862
  /**
802
863
  * Get the boundary selection of the document.
803
864
  */
804
- function getDocumentBoundarySelection(textDocument, atEnd) {
805
- const line = atEnd ? textDocument.lineCount - 1 : 0;
865
+ function getDocumentBoundarySelection(textDocument, atEnd, trimmedEndNewLine) {
866
+ let line = 0;
867
+ if (atEnd) {
868
+ const lastLine = textDocument.lineCount - 1;
869
+ line = trimmedEndNewLine === true && lastLine > 0 && textDocument.getLineLength(lastLine) === 0 ? lastLine - 1 : lastLine;
870
+ }
871
+ const character = atEnd ? textDocument.getLineLength(line) : 0;
806
872
  const start = {
807
873
  line,
808
- character: atEnd ? textDocument.getLineLength(line) : 0
874
+ character
809
875
  };
810
876
  return {
811
877
  start,
@@ -1021,11 +1087,24 @@ function expandCollapsedSelectionToWord(textDocument, selection) {
1021
1087
  };
1022
1088
  }
1023
1089
  function expandCollapsedLineWord(lineText, character) {
1024
- const segmenter = new Intl.Segmenter(void 0, { granularity: "word" });
1025
- for (const seg of segmenter.segment(lineText)) {
1026
- if (seg.isWordLike !== true) continue;
1027
- const lo = seg.index;
1028
- const hi = lo + seg.segment.length;
1090
+ const segmenter = createSegmenter({ granularity: "word" });
1091
+ if (segmenter !== void 0) {
1092
+ for (const seg of segmenter.segment(lineText)) {
1093
+ if (seg.isWordLike !== true) continue;
1094
+ const lo = seg.index;
1095
+ const hi = lo + seg.segment.length;
1096
+ if (character >= lo && character <= hi) return {
1097
+ start: lo,
1098
+ end: hi
1099
+ };
1100
+ }
1101
+ return;
1102
+ }
1103
+ const wordRe = /[\p{Alphabetic}\p{Number}_]+/gu;
1104
+ let match;
1105
+ while ((match = wordRe.exec(lineText)) !== null) {
1106
+ const lo = match.index;
1107
+ const hi = lo + match[0].length;
1029
1108
  if (character >= lo && character <= hi) return {
1030
1109
  start: lo,
1031
1110
  end: hi
@@ -1078,10 +1157,18 @@ function findClusterBreak(text, pos, forward, graphemeStarts) {
1078
1157
  }
1079
1158
  return 0;
1080
1159
  }
1081
- const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1082
1160
  function getLineGraphemeStarts(lineText) {
1083
1161
  const graphemeStarts = [0];
1084
- for (const segment of graphemeSegmenter.segment(lineText)) if (segment.index > 0) graphemeStarts.push(segment.index);
1162
+ const segmenter = getGraphemeSegmenter();
1163
+ if (segmenter !== void 0) {
1164
+ for (const segment of segmenter.segment(lineText)) if (segment.index > 0) graphemeStarts.push(segment.index);
1165
+ return graphemeStarts;
1166
+ }
1167
+ let index = 0;
1168
+ for (const codePoint of lineText) {
1169
+ if (index > 0) graphemeStarts.push(index);
1170
+ index += codePoint.length;
1171
+ }
1085
1172
  return graphemeStarts;
1086
1173
  }
1087
1174
  function stepCharacterByGrapheme(textDocument, line, character, forward) {
@@ -1093,7 +1180,12 @@ function stepCharacterByGrapheme(textDocument, line, character, forward) {
1093
1180
  character: 0
1094
1181
  });
1095
1182
  const suffix = textDocument.getTextSlice(lineStart + character, lineStart + lineLength);
1096
- for (const segment of graphemeSegmenter.segment(suffix)) return character + segment.segment.length;
1183
+ const segmenter = getGraphemeSegmenter();
1184
+ if (segmenter !== void 0) {
1185
+ for (const segment of segmenter.segment(suffix)) return character + segment.segment.length;
1186
+ return lineLength;
1187
+ }
1188
+ for (const codePoint of suffix) return character + codePoint.length;
1097
1189
  return lineLength;
1098
1190
  }
1099
1191
  if (character <= 0) return 0;
@@ -1103,7 +1195,16 @@ function stepCharacterByGrapheme(textDocument, line, character, forward) {
1103
1195
  });
1104
1196
  const prefix = textDocument.getTextSlice(lineStart, lineStart + character);
1105
1197
  let prevStart = 0;
1106
- for (const segment of graphemeSegmenter.segment(prefix)) prevStart = segment.index;
1198
+ const segmenter = getGraphemeSegmenter();
1199
+ if (segmenter !== void 0) {
1200
+ for (const segment of segmenter.segment(prefix)) prevStart = segment.index;
1201
+ return prevStart;
1202
+ }
1203
+ let index = 0;
1204
+ for (const codePoint of prefix) {
1205
+ prevStart = index;
1206
+ index += codePoint.length;
1207
+ }
1107
1208
  return prevStart;
1108
1209
  }
1109
1210
  function getSelectionAnchorAndFocusOffsets(textDocument, selection) {
@@ -1392,6 +1493,6 @@ function getTextOffset(text, offset) {
1392
1493
  return Math.min(offset, lineBreakIndex === -1 ? value.length : lineBreakIndex);
1393
1494
  }
1394
1495
  //#endregion
1395
- export { DirectionBackward, DirectionForward, DirectionNone, 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, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects };
1496
+ export { DirectionBackward, DirectionForward, DirectionNone, 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, resolveDeleteCharacterRange, resolveIndentEdits, resolveSelectionCut, selectionIntersects, shiftSelectionLines };
1396
1497
 
1397
1498
  //# sourceMappingURL=selection.js.map