@pierre/diffs 1.3.0-beta.9 → 1.3.0-rc.2

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 (241) hide show
  1. package/README.md +9 -0
  2. package/dist/components/CodeView.d.ts +12 -13
  3. package/dist/components/CodeView.d.ts.map +1 -1
  4. package/dist/components/CodeView.js +50 -58
  5. package/dist/components/CodeView.js.map +1 -1
  6. package/dist/components/File.d.ts +10 -3
  7. package/dist/components/File.d.ts.map +1 -1
  8. package/dist/components/File.js +41 -10
  9. package/dist/components/File.js.map +1 -1
  10. package/dist/components/FileDiff.d.ts +37 -6
  11. package/dist/components/FileDiff.d.ts.map +1 -1
  12. package/dist/components/FileDiff.js +202 -19
  13. package/dist/components/FileDiff.js.map +1 -1
  14. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  15. package/dist/components/UnresolvedFile.js +7 -2
  16. package/dist/components/UnresolvedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFile.d.ts +2 -1
  18. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  19. package/dist/components/VirtualizedFile.js +9 -5
  20. package/dist/components/VirtualizedFile.js.map +1 -1
  21. package/dist/components/VirtualizedFileDiff.d.ts +14 -0
  22. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  23. package/dist/components/VirtualizedFileDiff.js +61 -3
  24. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  25. package/dist/components/Virtualizer.d.ts +14 -2
  26. package/dist/components/Virtualizer.d.ts.map +1 -1
  27. package/dist/components/Virtualizer.js +27 -2
  28. package/dist/components/Virtualizer.js.map +1 -1
  29. package/dist/edit/index.d.ts +5 -0
  30. package/dist/edit/index.js +3 -0
  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 +11 -0
  34. package/dist/editor/command.js.map +1 -1
  35. package/dist/editor/editStack.d.ts +6 -2
  36. package/dist/editor/editStack.d.ts.map +1 -1
  37. package/dist/editor/editStack.js +53 -12
  38. package/dist/editor/editStack.js.map +1 -1
  39. package/dist/editor/editor.d.ts +51 -23
  40. package/dist/editor/editor.d.ts.map +1 -1
  41. package/dist/editor/editor.js +977 -241
  42. package/dist/editor/editor.js.map +1 -1
  43. package/dist/editor/editor2.js +1 -1
  44. package/dist/editor/languages.d.ts +26 -0
  45. package/dist/editor/languages.d.ts.map +1 -0
  46. package/dist/editor/languages.js +363 -0
  47. package/dist/editor/languages.js.map +1 -0
  48. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  49. package/dist/editor/lineAnnotations.js +25 -26
  50. package/dist/editor/lineAnnotations.js.map +1 -1
  51. package/dist/editor/marker.d.ts +5 -3
  52. package/dist/editor/marker.d.ts.map +1 -1
  53. package/dist/editor/marker.js +111 -91
  54. package/dist/editor/marker.js.map +1 -1
  55. package/dist/editor/matchBrackets.d.ts +3 -2
  56. package/dist/editor/matchBrackets.d.ts.map +1 -1
  57. package/dist/editor/matchBrackets.js +2 -10
  58. package/dist/editor/matchBrackets.js.map +1 -1
  59. package/dist/editor/pieceTable.d.ts +2 -1
  60. package/dist/editor/pieceTable.d.ts.map +1 -1
  61. package/dist/editor/pieceTable.js +103 -39
  62. package/dist/editor/pieceTable.js.map +1 -1
  63. package/dist/editor/platform.js +2 -0
  64. package/dist/editor/platform.js.map +1 -1
  65. package/dist/editor/popover.d.ts +25 -5
  66. package/dist/editor/popover.d.ts.map +1 -1
  67. package/dist/editor/popover.js +60 -14
  68. package/dist/editor/popover.js.map +1 -1
  69. package/dist/editor/searchPanel.d.ts.map +1 -1
  70. package/dist/editor/searchPanel.js +9 -2
  71. package/dist/editor/searchPanel.js.map +1 -1
  72. package/dist/editor/selection.d.ts +31 -11
  73. package/dist/editor/selection.d.ts.map +1 -1
  74. package/dist/editor/selection.js +198 -70
  75. package/dist/editor/selection.js.map +1 -1
  76. package/dist/editor/selectionAction.d.ts +4 -3
  77. package/dist/editor/selectionAction.d.ts.map +1 -1
  78. package/dist/editor/selectionAction.js +9 -5
  79. package/dist/editor/selectionAction.js.map +1 -1
  80. package/dist/editor/stateStorage.d.ts +15 -0
  81. package/dist/editor/stateStorage.d.ts.map +1 -0
  82. package/dist/editor/stateStorage.js +65 -0
  83. package/dist/editor/stateStorage.js.map +1 -0
  84. package/dist/editor/textDocument.d.ts +12 -77
  85. package/dist/editor/textDocument.d.ts.map +1 -1
  86. package/dist/editor/textDocument.js +76 -34
  87. package/dist/editor/textDocument.js.map +1 -1
  88. package/dist/editor/textMeasure.d.ts +7 -12
  89. package/dist/editor/textMeasure.d.ts.map +1 -1
  90. package/dist/editor/textMeasure.js +40 -32
  91. package/dist/editor/textMeasure.js.map +1 -1
  92. package/dist/editor/{tokenzier.d.ts → tokenizer.d.ts} +3 -3
  93. package/dist/editor/tokenizer.d.ts.map +1 -0
  94. package/dist/editor/{tokenzier.js → tokenizer.js} +170 -50
  95. package/dist/editor/tokenizer.js.map +1 -0
  96. package/dist/editor/utils.js +1 -1
  97. package/dist/editor/utils.js.map +1 -1
  98. package/dist/index.d.ts +4 -3
  99. package/dist/index.js +6 -5
  100. package/dist/managers/InteractionManager.d.ts +18 -7
  101. package/dist/managers/InteractionManager.d.ts.map +1 -1
  102. package/dist/managers/InteractionManager.js +154 -99
  103. package/dist/managers/InteractionManager.js.map +1 -1
  104. package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
  105. package/dist/managers/UniversalRenderingManager.js +1 -2
  106. package/dist/managers/UniversalRenderingManager.js.map +1 -1
  107. package/dist/react/CodeView.d.ts +13 -13
  108. package/dist/react/CodeView.d.ts.map +1 -1
  109. package/dist/react/CodeView.js +37 -28
  110. package/dist/react/CodeView.js.map +1 -1
  111. package/dist/react/EditContext.d.ts +20 -0
  112. package/dist/react/EditContext.d.ts.map +1 -0
  113. package/dist/react/EditContext.js +20 -0
  114. package/dist/react/EditContext.js.map +1 -0
  115. package/dist/react/File.d.ts +2 -1
  116. package/dist/react/File.d.ts.map +1 -1
  117. package/dist/react/File.js +3 -2
  118. package/dist/react/File.js.map +1 -1
  119. package/dist/react/FileDiff.d.ts +2 -2
  120. package/dist/react/FileDiff.d.ts.map +1 -1
  121. package/dist/react/FileDiff.js +3 -2
  122. package/dist/react/FileDiff.js.map +1 -1
  123. package/dist/react/MultiFileDiff.d.ts +2 -2
  124. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  125. package/dist/react/MultiFileDiff.js +3 -2
  126. package/dist/react/MultiFileDiff.js.map +1 -1
  127. package/dist/react/PatchDiff.d.ts +2 -2
  128. package/dist/react/PatchDiff.d.ts.map +1 -1
  129. package/dist/react/PatchDiff.js +3 -2
  130. package/dist/react/PatchDiff.js.map +1 -1
  131. package/dist/react/UnresolvedFile.d.ts +1 -1
  132. package/dist/react/UnresolvedFile.js.map +1 -1
  133. package/dist/react/index.d.ts +4 -4
  134. package/dist/react/index.js +3 -3
  135. package/dist/react/types.d.ts +9 -1
  136. package/dist/react/types.d.ts.map +1 -1
  137. package/dist/react/utils/useFileDiffInstance.d.ts +5 -3
  138. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  139. package/dist/react/utils/useFileDiffInstance.js +24 -34
  140. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  141. package/dist/react/utils/useFileInstance.d.ts +5 -3
  142. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  143. package/dist/react/utils/useFileInstance.js +23 -33
  144. package/dist/react/utils/useFileInstance.js.map +1 -1
  145. package/dist/react/utils/useUnresolvedFileInstance.js +4 -4
  146. package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
  147. package/dist/renderers/DiffHunksRenderer.d.ts +41 -1
  148. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  149. package/dist/renderers/DiffHunksRenderer.js +167 -29
  150. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  151. package/dist/renderers/FileRenderer.d.ts +20 -1
  152. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  153. package/dist/renderers/FileRenderer.js +119 -29
  154. package/dist/renderers/FileRenderer.js.map +1 -1
  155. package/dist/ssr/index.d.ts +2 -2
  156. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  157. package/dist/ssr/preloadDiffs.js +3 -1
  158. package/dist/ssr/preloadDiffs.js.map +1 -1
  159. package/dist/ssr/preloadFile.d.ts.map +1 -1
  160. package/dist/ssr/preloadFile.js +2 -0
  161. package/dist/ssr/preloadFile.js.map +1 -1
  162. package/dist/style.js +1 -1
  163. package/dist/types.d.ts +192 -42
  164. package/dist/types.d.ts.map +1 -1
  165. package/dist/utils/annotationHelpers.d.ts +20 -0
  166. package/dist/utils/annotationHelpers.d.ts.map +1 -0
  167. package/dist/utils/annotationHelpers.js +29 -0
  168. package/dist/utils/annotationHelpers.js.map +1 -0
  169. package/dist/utils/computeFileOffsets.d.ts.map +1 -1
  170. package/dist/utils/computeFileOffsets.js +34 -5
  171. package/dist/utils/computeFileOffsets.js.map +1 -1
  172. package/dist/utils/editSessionHunks.d.ts +66 -0
  173. package/dist/utils/editSessionHunks.d.ts.map +1 -0
  174. package/dist/utils/editSessionHunks.js +437 -0
  175. package/dist/utils/editSessionHunks.js.map +1 -0
  176. package/dist/utils/getDiffHunksRendererOptions.d.ts.map +1 -1
  177. package/dist/utils/getDiffHunksRendererOptions.js +2 -1
  178. package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
  179. package/dist/utils/getFileRendererOptions.d.ts.map +1 -1
  180. package/dist/utils/getFileRendererOptions.js +2 -1
  181. package/dist/utils/getFileRendererOptions.js.map +1 -1
  182. package/dist/utils/getHunkSideBoundaries.d.ts +7 -0
  183. package/dist/utils/getHunkSideBoundaries.d.ts.map +1 -0
  184. package/dist/utils/getHunkSideBoundaries.js +12 -0
  185. package/dist/utils/getHunkSideBoundaries.js.map +1 -0
  186. package/dist/utils/getTotalLineCountFromHunks.d.ts.map +1 -1
  187. package/dist/utils/getTotalLineCountFromHunks.js +2 -1
  188. package/dist/utils/getTotalLineCountFromHunks.js.map +1 -1
  189. package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
  190. package/dist/utils/hydratePartialDiff.js +4 -3
  191. package/dist/utils/hydratePartialDiff.js.map +1 -1
  192. package/dist/utils/isGutterUtilityPath.d.ts +5 -0
  193. package/dist/utils/isGutterUtilityPath.d.ts.map +1 -0
  194. package/dist/utils/isGutterUtilityPath.js +12 -0
  195. package/dist/utils/isGutterUtilityPath.js.map +1 -0
  196. package/dist/utils/iterateOverDiff.d.ts.map +1 -1
  197. package/dist/utils/iterateOverDiff.js +17 -12
  198. package/dist/utils/iterateOverDiff.js.map +1 -1
  199. package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -1
  200. package/dist/utils/parseMergeConflictDiffFromFile.js +9 -6
  201. package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
  202. package/dist/utils/parsePatchFiles.d.ts.map +1 -1
  203. package/dist/utils/parsePatchFiles.js +6 -3
  204. package/dist/utils/parsePatchFiles.js.map +1 -1
  205. package/dist/utils/realignChangeContent.d.ts +33 -0
  206. package/dist/utils/realignChangeContent.d.ts.map +1 -0
  207. package/dist/utils/realignChangeContent.js +148 -0
  208. package/dist/utils/realignChangeContent.js.map +1 -0
  209. package/dist/utils/renderDiffWithHighlighter.js +1 -1
  210. package/dist/utils/resolveRegion.d.ts.map +1 -1
  211. package/dist/utils/resolveRegion.js +18 -2
  212. package/dist/utils/resolveRegion.js.map +1 -1
  213. package/dist/utils/shouldUseTokenTransformer.d.ts +7 -3
  214. package/dist/utils/shouldUseTokenTransformer.d.ts.map +1 -1
  215. package/dist/utils/shouldUseTokenTransformer.js +1 -1
  216. package/dist/utils/shouldUseTokenTransformer.js.map +1 -1
  217. package/dist/utils/updateDiffHunks.d.ts +8 -2
  218. package/dist/utils/updateDiffHunks.d.ts.map +1 -1
  219. package/dist/utils/updateDiffHunks.js +9 -7
  220. package/dist/utils/updateDiffHunks.js.map +1 -1
  221. package/dist/utils/virtualDiffLayout.d.ts +42 -2
  222. package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
  223. package/dist/utils/virtualDiffLayout.js +101 -7
  224. package/dist/utils/virtualDiffLayout.js.map +1 -1
  225. package/dist/worker/WorkerPoolManager.d.ts +2 -1
  226. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  227. package/dist/worker/WorkerPoolManager.js +3 -0
  228. package/dist/worker/WorkerPoolManager.js.map +1 -1
  229. package/dist/worker/worker-portable.js +60 -20
  230. package/dist/worker/worker-portable.js.map +1 -1
  231. package/dist/worker/worker.js +61 -19
  232. package/dist/worker/worker.js.map +1 -1
  233. package/package.json +10 -7
  234. package/dist/editor/index.d.ts +0 -3
  235. package/dist/editor/index.js +0 -3
  236. package/dist/editor/tokenzier.d.ts.map +0 -1
  237. package/dist/editor/tokenzier.js.map +0 -1
  238. package/dist/react/EditorContext.d.ts +0 -15
  239. package/dist/react/EditorContext.d.ts.map +0 -1
  240. package/dist/react/EditorContext.js +0 -23
  241. package/dist/react/EditorContext.js.map +0 -1
@@ -0,0 +1,29 @@
1
+ //#region src/utils/annotationHelpers.ts
2
+ /** Narrow an annotation to the side-tagged diff shape. */
3
+ function isDiffAnnotation(annotation) {
4
+ return "side" in annotation;
5
+ }
6
+ /** Narrow an annotation to the side-less file shape. */
7
+ function isFileAnnotation(annotation) {
8
+ return !isDiffAnnotation(annotation);
9
+ }
10
+ /**
11
+ * Narrow a homogeneous editor annotation collection to diff annotations.
12
+ * Empty collections return true because they are valid for either shape.
13
+ */
14
+ function isDiffAnnotationCollection(annotations) {
15
+ const first = annotations[0];
16
+ return first == null || isDiffAnnotation(first);
17
+ }
18
+ /**
19
+ * Narrow a homogeneous editor annotation collection to file annotations.
20
+ * Empty collections return true because they are valid for either shape.
21
+ */
22
+ function isFileAnnotationCollection(annotations) {
23
+ const first = annotations[0];
24
+ return first == null || isFileAnnotation(first);
25
+ }
26
+ //#endregion
27
+ export { isDiffAnnotation, isDiffAnnotationCollection, isFileAnnotation, isFileAnnotationCollection };
28
+
29
+ //# sourceMappingURL=annotationHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotationHelpers.js","names":[],"sources":["../../src/utils/annotationHelpers.ts"],"sourcesContent":["import type { DiffLineAnnotation, LineAnnotation } from '../types';\n\n/** Narrow an annotation to the side-tagged diff shape. */\nexport function isDiffAnnotation<LAnnotation = undefined>(\n annotation: LineAnnotation<LAnnotation> | DiffLineAnnotation<LAnnotation>\n): annotation is DiffLineAnnotation<LAnnotation> {\n return 'side' in annotation;\n}\n\n/** Narrow an annotation to the side-less file shape. */\nexport function isFileAnnotation<LAnnotation = undefined>(\n annotation: LineAnnotation<LAnnotation> | DiffLineAnnotation<LAnnotation>\n): annotation is LineAnnotation<LAnnotation> {\n return !isDiffAnnotation(annotation);\n}\n\n/**\n * Narrow a homogeneous editor annotation collection to diff annotations.\n * Empty collections return true because they are valid for either shape.\n */\nexport function isDiffAnnotationCollection<LAnnotation = undefined>(\n annotations: LineAnnotation<LAnnotation>[] | DiffLineAnnotation<LAnnotation>[]\n): annotations is DiffLineAnnotation<LAnnotation>[] {\n const first = annotations[0];\n return first == null || isDiffAnnotation(first);\n}\n\n/**\n * Narrow a homogeneous editor annotation collection to file annotations.\n * Empty collections return true because they are valid for either shape.\n */\nexport function isFileAnnotationCollection<LAnnotation = undefined>(\n annotations: LineAnnotation<LAnnotation>[] | DiffLineAnnotation<LAnnotation>[]\n): annotations is LineAnnotation<LAnnotation>[] {\n const first = annotations[0];\n return first == null || isFileAnnotation(first);\n}\n"],"mappings":";;AAGA,SAAgB,iBACd,YAC+C;CAC/C,OAAO,UAAU;AACnB;;AAGA,SAAgB,iBACd,YAC2C;CAC3C,OAAO,CAAC,iBAAiB,UAAU;AACrC;;;;;AAMA,SAAgB,2BACd,aACkD;CAClD,MAAM,QAAQ,YAAY;CAC1B,OAAO,SAAS,QAAQ,iBAAiB,KAAK;AAChD;;;;;AAMA,SAAgB,2BACd,aAC8C;CAC9C,MAAM,QAAQ,YAAY;CAC1B,OAAO,SAAS,QAAQ,iBAAiB,KAAK;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"computeFileOffsets.d.ts","names":[],"sources":["../../src/utils/computeFileOffsets.ts"],"mappings":";;AAMA;;iBAAgB,kBAAA,CAAmB,QAAgB;;AAAA;AAyBnD;;;;AAAgD;iBAAhC,eAAA,CAAgB,QAAgB;;;;AAuBM;iBAAtC,qBAAA,CAAsB,QAAgB"}
1
+ {"version":3,"file":"computeFileOffsets.d.ts","names":[],"sources":["../../src/utils/computeFileOffsets.ts"],"mappings":";;AAQA;;iBAAgB,kBAAA,CAAmB,QAAgB;;AAAA;AA2DnD;;;;AAAgD;iBAAhC,eAAA,CAAgB,QAAgB;;;;AAuBM;iBAAtC,qBAAA,CAAsB,QAAgB"}
@@ -1,16 +1,45 @@
1
1
  //#region src/utils/computeFileOffsets.ts
2
2
  const LINE_FEED = 10;
3
3
  const CARRIAGE_RETURN = 13;
4
+ const DENSE_LINE_BREAK_BLOCK_SIZE = 4;
5
+ const DENSE_LINE_BREAK_BLOCK_SPAN = 40;
4
6
  /**
5
7
  * Computes line start offsets for a string.
6
8
  */
7
9
  function computeLineOffsets(contents) {
8
10
  const offsets = [0];
9
- for (let i = 0; i < contents.length; i++) {
10
- const char = contents.charCodeAt(i);
11
- if (char === LINE_FEED || char === CARRIAGE_RETURN) {
12
- if (char === CARRIAGE_RETURN && i + 1 < contents.length && contents.charCodeAt(i + 1) === LINE_FEED) i++;
13
- offsets.push(i + 1);
11
+ let carriageReturn = contents.indexOf("\r");
12
+ let lineFeed = contents.indexOf("\n");
13
+ let blockStart = 0;
14
+ let blockBreaks = 0;
15
+ while (carriageReturn !== -1 || lineFeed !== -1) {
16
+ let nextOffset;
17
+ if (carriageReturn !== -1 && (lineFeed === -1 || carriageReturn < lineFeed)) if (lineFeed === carriageReturn + 1) {
18
+ nextOffset = lineFeed + 1;
19
+ carriageReturn = contents.indexOf("\r", nextOffset);
20
+ lineFeed = contents.indexOf("\n", nextOffset);
21
+ } else {
22
+ nextOffset = carriageReturn + 1;
23
+ carriageReturn = contents.indexOf("\r", nextOffset);
24
+ }
25
+ else {
26
+ nextOffset = lineFeed + 1;
27
+ lineFeed = contents.indexOf("\n", nextOffset);
28
+ }
29
+ offsets.push(nextOffset);
30
+ if (++blockBreaks === DENSE_LINE_BREAK_BLOCK_SIZE) {
31
+ if (nextOffset - blockStart <= DENSE_LINE_BREAK_BLOCK_SPAN) {
32
+ for (let i = nextOffset; i < contents.length; i++) {
33
+ const char = contents.charCodeAt(i);
34
+ if (char === LINE_FEED || char === CARRIAGE_RETURN) {
35
+ if (char === CARRIAGE_RETURN && i + 1 < contents.length && contents.charCodeAt(i + 1) === LINE_FEED) i++;
36
+ offsets.push(i + 1);
37
+ }
38
+ }
39
+ return offsets;
40
+ }
41
+ blockStart = nextOffset;
42
+ blockBreaks = 0;
14
43
  }
15
44
  }
16
45
  return offsets;
@@ -1 +1 @@
1
- {"version":3,"file":"computeFileOffsets.js","names":[],"sources":["../../src/utils/computeFileOffsets.ts"],"sourcesContent":["const LINE_FEED = 10; // \\n\nconst CARRIAGE_RETURN = 13; // \\r\n\n/**\n * Computes line start offsets for a string.\n */\nexport function computeLineOffsets(contents: string): number[] {\n const offsets: number[] = [0];\n for (let i = 0; i < contents.length; i++) {\n const char = contents.charCodeAt(i);\n if (char === LINE_FEED || char === CARRIAGE_RETURN) {\n if (\n char === CARRIAGE_RETURN &&\n i + 1 < contents.length &&\n contents.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n offsets.push(i + 1);\n }\n }\n return offsets;\n}\n\n/**\n * Counts line breaks in a string, treating `\\n`, `\\r`, and `\\r\\n` the same way\n * {@link computeLineOffsets} does (a `\\r\\n` pair is one break). Mirrors that\n * scan but counts in a single pass instead of building and discarding an\n * offsets array, so sizing the changed-line range for large edits stays cheap.\n * A unit test asserts it stays in lockstep with `computeLineOffsets`.\n */\nexport function countLineBreaks(contents: string): number {\n let count = 0;\n for (let i = 0; i < contents.length; i++) {\n const char = contents.charCodeAt(i);\n if (char === LINE_FEED || char === CARRIAGE_RETURN) {\n // Skip the `\\n` of a `\\r\\n` pair so it counts as one break, not two.\n if (\n char === CARRIAGE_RETURN &&\n i + 1 < contents.length &&\n contents.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n count++;\n }\n }\n return count;\n}\n\n/**\n * Splits file contents into lines aligned with {@link computeLineOffsets}.\n * Unlike splitFileContents, a trailing newline produces a final empty line.\n */\nexport function linesFromFileContents(contents: string): string[] {\n const offsets = computeLineOffsets(contents);\n const lines = Array.from({ length: offsets.length }, (_, i) => {\n const start = offsets[i];\n const end = offsets[i + 1] ?? contents.length;\n return contents.slice(start, end);\n });\n return lines;\n}\n"],"mappings":";AAAA,MAAM,YAAY;AAClB,MAAM,kBAAkB;;;;AAKxB,SAAgB,mBAAmB,UAA4B;CAC7D,MAAM,UAAoB,CAAC,CAAC;CAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,OAAO,SAAS,WAAW,CAAC;EAClC,IAAI,SAAS,aAAa,SAAS,iBAAiB;GAClD,IACE,SAAS,mBACT,IAAI,IAAI,SAAS,UACjB,SAAS,WAAW,IAAI,CAAC,MAAM,WAE/B;GAEF,QAAQ,KAAK,IAAI,CAAC;EACpB;CACF;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,gBAAgB,UAA0B;CACxD,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,OAAO,SAAS,WAAW,CAAC;EAClC,IAAI,SAAS,aAAa,SAAS,iBAAiB;GAElD,IACE,SAAS,mBACT,IAAI,IAAI,SAAS,UACjB,SAAS,WAAW,IAAI,CAAC,MAAM,WAE/B;GAEF;EACF;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,sBAAsB,UAA4B;CAChE,MAAM,UAAU,mBAAmB,QAAQ;CAM3C,OALc,MAAM,KAAK,EAAE,QAAQ,QAAQ,OAAO,IAAI,GAAG,MAAM;EAC7D,MAAM,QAAQ,QAAQ;EACtB,MAAM,MAAM,QAAQ,IAAI,MAAM,SAAS;EACvC,OAAO,SAAS,MAAM,OAAO,GAAG;CAClC,CACW;AACb"}
1
+ {"version":3,"file":"computeFileOffsets.js","names":[],"sources":["../../src/utils/computeFileOffsets.ts"],"sourcesContent":["const LINE_FEED = 10; // \\n\nconst CARRIAGE_RETURN = 13; // \\r\nconst DENSE_LINE_BREAK_BLOCK_SIZE = 4;\nconst DENSE_LINE_BREAK_BLOCK_SPAN = 40;\n\n/**\n * Computes line start offsets for a string.\n */\nexport function computeLineOffsets(contents: string): number[] {\n const offsets: number[] = [0];\n let carriageReturn = contents.indexOf('\\r');\n let lineFeed = contents.indexOf('\\n');\n let blockStart = 0;\n let blockBreaks = 0;\n while (carriageReturn !== -1 || lineFeed !== -1) {\n let nextOffset: number;\n if (\n carriageReturn !== -1 &&\n (lineFeed === -1 || carriageReturn < lineFeed)\n ) {\n if (lineFeed === carriageReturn + 1) {\n nextOffset = lineFeed + 1;\n carriageReturn = contents.indexOf('\\r', nextOffset);\n lineFeed = contents.indexOf('\\n', nextOffset);\n } else {\n nextOffset = carriageReturn + 1;\n carriageReturn = contents.indexOf('\\r', nextOffset);\n }\n } else {\n nextOffset = lineFeed + 1;\n lineFeed = contents.indexOf('\\n', nextOffset);\n }\n offsets.push(nextOffset);\n\n if (++blockBreaks === DENSE_LINE_BREAK_BLOCK_SIZE) {\n if (nextOffset - blockStart <= DENSE_LINE_BREAK_BLOCK_SPAN) {\n // Native searches win on source-like lines; a character scan is faster\n // once line breaks become unusually dense.\n for (let i = nextOffset; i < contents.length; i++) {\n const char = contents.charCodeAt(i);\n if (char === LINE_FEED || char === CARRIAGE_RETURN) {\n if (\n char === CARRIAGE_RETURN &&\n i + 1 < contents.length &&\n contents.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n offsets.push(i + 1);\n }\n }\n return offsets;\n }\n blockStart = nextOffset;\n blockBreaks = 0;\n }\n }\n return offsets;\n}\n\n/**\n * Counts line breaks in a string, treating `\\n`, `\\r`, and `\\r\\n` the same way\n * {@link computeLineOffsets} does (a `\\r\\n` pair is one break). Mirrors that\n * scan but counts in a single pass instead of building and discarding an\n * offsets array, so sizing the changed-line range for large edits stays cheap.\n * A unit test asserts it stays in lockstep with `computeLineOffsets`.\n */\nexport function countLineBreaks(contents: string): number {\n let count = 0;\n for (let i = 0; i < contents.length; i++) {\n const char = contents.charCodeAt(i);\n if (char === LINE_FEED || char === CARRIAGE_RETURN) {\n // Skip the `\\n` of a `\\r\\n` pair so it counts as one break, not two.\n if (\n char === CARRIAGE_RETURN &&\n i + 1 < contents.length &&\n contents.charCodeAt(i + 1) === LINE_FEED\n ) {\n i++;\n }\n count++;\n }\n }\n return count;\n}\n\n/**\n * Splits file contents into lines aligned with {@link computeLineOffsets}.\n * Unlike splitFileContents, a trailing newline produces a final empty line.\n */\nexport function linesFromFileContents(contents: string): string[] {\n const offsets = computeLineOffsets(contents);\n const lines = Array.from({ length: offsets.length }, (_, i) => {\n const start = offsets[i];\n const end = offsets[i + 1] ?? contents.length;\n return contents.slice(start, end);\n });\n return lines;\n}\n"],"mappings":";AAAA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;;;;AAKpC,SAAgB,mBAAmB,UAA4B;CAC7D,MAAM,UAAoB,CAAC,CAAC;CAC5B,IAAI,iBAAiB,SAAS,QAAQ,IAAI;CAC1C,IAAI,WAAW,SAAS,QAAQ,IAAI;CACpC,IAAI,aAAa;CACjB,IAAI,cAAc;CAClB,OAAO,mBAAmB,MAAM,aAAa,IAAI;EAC/C,IAAI;EACJ,IACE,mBAAmB,OAClB,aAAa,MAAM,iBAAiB,WAErC,IAAI,aAAa,iBAAiB,GAAG;GACnC,aAAa,WAAW;GACxB,iBAAiB,SAAS,QAAQ,MAAM,UAAU;GAClD,WAAW,SAAS,QAAQ,MAAM,UAAU;EAC9C,OAAO;GACL,aAAa,iBAAiB;GAC9B,iBAAiB,SAAS,QAAQ,MAAM,UAAU;EACpD;OACK;GACL,aAAa,WAAW;GACxB,WAAW,SAAS,QAAQ,MAAM,UAAU;EAC9C;EACA,QAAQ,KAAK,UAAU;EAEvB,IAAI,EAAE,gBAAgB,6BAA6B;GACjD,IAAI,aAAa,cAAc,6BAA6B;IAG1D,KAAK,IAAI,IAAI,YAAY,IAAI,SAAS,QAAQ,KAAK;KACjD,MAAM,OAAO,SAAS,WAAW,CAAC;KAClC,IAAI,SAAS,aAAa,SAAS,iBAAiB;MAClD,IACE,SAAS,mBACT,IAAI,IAAI,SAAS,UACjB,SAAS,WAAW,IAAI,CAAC,MAAM,WAE/B;MAEF,QAAQ,KAAK,IAAI,CAAC;KACpB;IACF;IACA,OAAO;GACT;GACA,aAAa;GACb,cAAc;EAChB;CACF;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,gBAAgB,UAA0B;CACxD,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,OAAO,SAAS,WAAW,CAAC;EAClC,IAAI,SAAS,aAAa,SAAS,iBAAiB;GAElD,IACE,SAAS,mBACT,IAAI,IAAI,SAAS,UACjB,SAAS,WAAW,IAAI,CAAC,MAAM,WAE/B;GAEF;EACF;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,sBAAsB,UAA4B;CAChE,MAAM,UAAU,mBAAmB,QAAQ;CAM3C,OALc,MAAM,KAAK,EAAE,QAAQ,QAAQ,OAAO,IAAI,GAAG,MAAM;EAC7D,MAAM,QAAQ,QAAQ;EACtB,MAAM,MAAM,QAAQ,IAAI,MAAM,SAAS;EACvC,OAAO,SAAS,MAAM,OAAO,GAAG;CAClC,CACW;AACb"}
@@ -0,0 +1,66 @@
1
+ import { FileDiffMetadata, HunkExpansionRegion } from "../types.js";
2
+ import { CreatePatchOptionsNonabortable } from "diff";
3
+
4
+ //#region src/utils/editSessionHunks.d.ts
5
+ interface DivergenceCore {
6
+ start: number;
7
+ deletionEnd: number;
8
+ additionEnd: number;
9
+ }
10
+ interface PreviousRegionSpan {
11
+ firstIndex: number;
12
+ lastIndex: number;
13
+ }
14
+ /** Maps rebuilt regions back to the previous skeleton for expansion remapping. */
15
+ interface SessionRegionChange {
16
+ regions: Array<PreviousRegionSpan | undefined>;
17
+ }
18
+ /**
19
+ * Drops the editor document's phantom trailing empty line (a document ending
20
+ * in a newline exposes one extra empty line the parsed diff never contains)
21
+ * so session line arrays compare like parse-derived ones.
22
+ */
23
+ declare function normalizeEditorLines(lines: string[]): string[];
24
+ /**
25
+ * Find the complete old/current divergence core. The old side is immutable
26
+ * during an edit session, so this result needs no prior-pass snapshot.
27
+ */
28
+ declare function findDivergenceCore(deletionLines: string[], additionLines: string[]): DivergenceCore | undefined;
29
+ /**
30
+ * Rebuild the session skeleton as a pure function of the immutable old lines,
31
+ * current new lines, and old-side ranges of the previous regions. One
32
+ * canonical parse supplies the same change blocks that session exit will use.
33
+ */
34
+ declare function rebuildSessionHunks(diff: FileDiffMetadata, parseDiffOptions?: CreatePatchOptionsNonabortable): SessionRegionChange | undefined;
35
+ /**
36
+ * Keep a cheap content-only path when a same-line-count pass cannot alter the
37
+ * canonical blocks. Gap, ambiguous, or multi-region edits rebuild statelessly.
38
+ */
39
+ declare function applySessionChangedLines(diff: FileDiffMetadata, changedAdditionLineIndexes: Iterable<number>, parseDiffOptions?: CreatePatchOptionsNonabortable, previousAdditionLines?: ReadonlyMap<number, string>): SessionRegionChange | undefined;
40
+ /** Preserve expansion at the surviving outer edges of rebuilt old-side gaps. */
41
+ declare function remapExpandedHunksForRegionChange(expandedHunks: Map<number, HunkExpansionRegion>, change: SessionRegionChange): Map<number, HunkExpansionRegion>;
42
+ /**
43
+ * An expanded gap-edge slice in old-side (deletion-line) coordinates as a
44
+ * `[start, end)` range. Old-side coordinates survive the exit recompute
45
+ * unchanged — edits only touch the new side — so these anchor best-effort
46
+ * expansion preservation across the recompute.
47
+ */
48
+ type ExpansionAnchorRange = [start: number, end: number];
49
+ /** Snapshot the expanded gap-edge slices before the exit recompute. */
50
+ declare function captureExpansionAnchors(diff: FileDiffMetadata, expandedHunks: Map<number, HunkExpansionRegion>, collapsedContextThreshold: number): ExpansionAnchorRange[];
51
+ /**
52
+ * Rebuild gap expansion state against the recomputed hunks: for each new
53
+ * gap, an anchor touching the gap's start edge restores `fromStart`, one
54
+ * touching its end edge restores `fromEnd`, and anchors for gaps that no
55
+ * longer exist drop.
56
+ */
57
+ declare function rebuildExpansionFromAnchors(diff: FileDiffMetadata, anchors: ExpansionAnchorRange[]): Map<number, HunkExpansionRegion>;
58
+ /**
59
+ * Genuine session exit: when session passes reshaped the hunks, run the real
60
+ * full recompute so exit state matches a non-session edit pipeline, and clear
61
+ * the marker. Returns true when a recompute ran.
62
+ */
63
+ declare function finishEditSessionForDiff(diff: FileDiffMetadata, parseDiffOptions?: CreatePatchOptionsNonabortable): boolean;
64
+ //#endregion
65
+ export { DivergenceCore, ExpansionAnchorRange, SessionRegionChange, applySessionChangedLines, captureExpansionAnchors, findDivergenceCore, finishEditSessionForDiff, normalizeEditorLines, rebuildExpansionFromAnchors, rebuildSessionHunks, remapExpandedHunksForRegionChange };
66
+ //# sourceMappingURL=editSessionHunks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editSessionHunks.d.ts","names":[],"sources":["../../src/utils/editSessionHunks.ts"],"mappings":";;;;UA6BiB,cAAA;EACf,KAAA;EACA,WAAA;EACA,WAAA;AAAA;AAAA,UAGQ,kBAAA;EACR,UAAA;EACA,SAAS;AAAA;;UAIM,mBAAA;EACf,OAAA,EAAS,KAAK,CAAC,kBAAA;AAAA;;;AALN;AAIX;;iBA4BgB,oBAAA,CAAqB,KAAe;;;;;iBAWpC,kBAAA,CACd,aAAA,YACA,aAAA,aACC,cAAc;AAzCkB;AA2BnC;;;;AA3BmC,iBAoEnB,mBAAA,CACd,IAAA,EAAM,gBAAA,EACN,gBAAA,GAAmB,8BAAA,GAClB,mBAAA;AAjCH;;;;AAAA,iBAoEgB,wBAAA,CACd,IAAA,EAAM,gBAAA,EACN,0BAAA,EAA4B,QAAA,UAC5B,gBAAA,GAAmB,8BAAA,EACnB,qBAAA,GAAwB,WAAA,mBACvB,mBAAA;;iBAwHa,iCAAA,CACd,aAAA,EAAe,GAAA,SAAY,mBAAA,GAC3B,MAAA,EAAQ,mBAAA,GACP,GAAA,SAAY,mBAAA;;AAjME;AA2BjB;;;;KAmMY,oBAAA,IAAwB,KAAA,UAAe,GAAG;;iBAGtC,uBAAA,CACd,IAAA,EAAM,gBAAA,EACN,aAAA,EAAe,GAAA,SAAY,mBAAA,GAC3B,yBAAA,WACC,oBAAA;;;;;;;iBAoDa,2BAAA,CACd,IAAA,EAAM,gBAAA,EACN,OAAA,EAAS,oBAAA,KACR,GAAA,SAAY,mBAAA;AA9PO;AAmCtB;;;;AAnCsB,iBA4SN,wBAAA,CACd,IAAA,EAAM,gBAAA,EACN,gBAAA,GAAmB,8BAA8B"}
@@ -0,0 +1,437 @@
1
+ import { getHunkSideStartBoundary } from "./getHunkSideBoundaries.js";
2
+ import { parseDiffFromFile } from "./parseDiffFromFile.js";
3
+ import { getExpandedRegion, getTrailingExpandedRegion } from "./virtualDiffLayout.js";
4
+ import { offsetHunkContent, preserveTrailingEditorBlankLine, recomputeDiffHunksForEdit, recomputeDiffRenderLineCounts, recomputeHunkRenderLineCounts, syncHunkNoEOFCRFromFullFile } from "./updateDiffHunks.js";
5
+ //#region src/utils/editSessionHunks.ts
6
+ const deletionLineSetCache = /* @__PURE__ */ new WeakMap();
7
+ /**
8
+ * Drops the editor document's phantom trailing empty line (a document ending
9
+ * in a newline exposes one extra empty line the parsed diff never contains)
10
+ * so session line arrays compare like parse-derived ones.
11
+ */
12
+ function normalizeEditorLines(lines) {
13
+ if (lines.length > 1 && lines[lines.length - 1] === "") return lines.slice(0, -1);
14
+ return lines;
15
+ }
16
+ /**
17
+ * Find the complete old/current divergence core. The old side is immutable
18
+ * during an edit session, so this result needs no prior-pass snapshot.
19
+ */
20
+ function findDivergenceCore(deletionLines, additionLines) {
21
+ const maxStart = Math.min(deletionLines.length, additionLines.length);
22
+ let start = 0;
23
+ while (start < maxStart && deletionLines[start] === additionLines[start]) start++;
24
+ let deletionEnd = deletionLines.length;
25
+ let additionEnd = additionLines.length;
26
+ while (deletionEnd > start && additionEnd > start && deletionLines[deletionEnd - 1] === additionLines[additionEnd - 1]) {
27
+ deletionEnd--;
28
+ additionEnd--;
29
+ }
30
+ if (start === deletionEnd && start === additionEnd) return;
31
+ return {
32
+ start,
33
+ deletionEnd,
34
+ additionEnd
35
+ };
36
+ }
37
+ /**
38
+ * Rebuild the session skeleton as a pure function of the immutable old lines,
39
+ * current new lines, and old-side ranges of the previous regions. One
40
+ * canonical parse supplies the same change blocks that session exit will use.
41
+ */
42
+ function rebuildSessionHunks(diff, parseDiffOptions) {
43
+ const previousHunks = diff.hunks;
44
+ const editorAdditionLines = diff.additionLines;
45
+ const canonicalAdditionLines = normalizeEditorLines(editorAdditionLines);
46
+ const canonicalDiff = canonicalAdditionLines === editorAdditionLines ? diff : {
47
+ ...diff,
48
+ additionLines: canonicalAdditionLines
49
+ };
50
+ const plans = buildRegionPlans(previousHunks, parseCanonicalChangeBlocks(canonicalDiff, parseDiffOptions), diff.deletionLines.length);
51
+ const nextHunks = buildRegionHunks(canonicalDiff, plans);
52
+ diff.additionLines = canonicalAdditionLines;
53
+ diff.hunks = nextHunks;
54
+ diff.editSessionDirty = true;
55
+ finalizeSessionHunks(diff);
56
+ preserveTrailingEditorBlankLine(diff, editorAdditionLines);
57
+ if (!hasRegionOrSplitLayoutChanged(previousHunks, diff.hunks)) return;
58
+ return { regions: plans.map((plan) => plan.previousSpan) };
59
+ }
60
+ /**
61
+ * Keep a cheap content-only path when a same-line-count pass cannot alter the
62
+ * canonical blocks. Gap, ambiguous, or multi-region edits rebuild statelessly.
63
+ */
64
+ function applySessionChangedLines(diff, changedAdditionLineIndexes, parseDiffOptions, previousAdditionLines) {
65
+ const lines = Array.from(new Set(changedAdditionLineIndexes)).filter((line) => line >= 0 && line < diff.additionLines.length).sort((a, b) => a - b);
66
+ if (lines.length === 0) return;
67
+ const { hunks } = diff;
68
+ let regionIndex;
69
+ let hunkIndex = 0;
70
+ for (const line of lines) {
71
+ while (hunkIndex < hunks.length) {
72
+ const hunk = hunks[hunkIndex];
73
+ if (line < getHunkAdditionStart(hunk) + hunk.additionCount) break;
74
+ hunkIndex++;
75
+ }
76
+ const hunk = hunks[hunkIndex];
77
+ const start = hunk == null ? void 0 : getHunkAdditionStart(hunk);
78
+ if (start == null || line < start || regionIndex != null && regionIndex !== hunkIndex) return rebuildSessionHunks(diff, parseDiffOptions);
79
+ regionIndex = hunkIndex;
80
+ }
81
+ if (regionIndex == null) return;
82
+ if (canRetainCanonicalBlocks(diff, lines, regionIndex, previousAdditionLines, parseDiffOptions)) {
83
+ diff.editSessionDirty = true;
84
+ return;
85
+ }
86
+ return rebuildSessionHunks(diff, parseDiffOptions);
87
+ }
88
+ function canRetainCanonicalBlocks(diff, changedLines, regionIndex, previousAdditionLines, parseDiffOptions) {
89
+ if (previousAdditionLines == null || parseDiffOptions?.ignoreWhitespace === true || parseDiffOptions?.stripTrailingCr === true) return false;
90
+ const deletionLineSet = getDeletionLineSet(diff);
91
+ const hunk = diff.hunks[regionIndex];
92
+ if (hunk.hunkContent.some(isPureChange)) return false;
93
+ for (const line of changedLines) {
94
+ const previousLine = previousAdditionLines.get(line);
95
+ const additionLine = diff.additionLines[line];
96
+ if (previousLine == null || additionLine == null || deletionLineSet.has(previousLine) || deletionLineSet.has(additionLine)) return false;
97
+ let insideBalancedChange = false;
98
+ for (const content of hunk.hunkContent) if (content.type === "change" && content.additions === content.deletions && line >= content.additionLineIndex && line < content.additionLineIndex + content.additions) {
99
+ insideBalancedChange = true;
100
+ break;
101
+ }
102
+ if (!insideBalancedChange) return false;
103
+ }
104
+ return true;
105
+ }
106
+ function getDeletionLineSet(diff) {
107
+ const cached = deletionLineSetCache.get(diff);
108
+ if (cached?.lines === diff.deletionLines) return cached.set;
109
+ const set = new Set(diff.deletionLines);
110
+ deletionLineSetCache.set(diff, {
111
+ lines: diff.deletionLines,
112
+ set
113
+ });
114
+ return set;
115
+ }
116
+ function isPureChange(content) {
117
+ return content?.type === "change" && (content.additions === 0 || content.deletions === 0);
118
+ }
119
+ /** Preserve expansion at the surviving outer edges of rebuilt old-side gaps. */
120
+ function remapExpandedHunksForRegionChange(expandedHunks, change) {
121
+ const remapped = /* @__PURE__ */ new Map();
122
+ const { regions } = change;
123
+ for (let key = 0; key <= regions.length; key++) {
124
+ const previous = regions[key - 1];
125
+ const next = regions[key];
126
+ const fromStartSource = key === 0 ? expandedHunks.get(0) : previous == null ? void 0 : expandedHunks.get(previous.lastIndex + 1);
127
+ const fromEndSource = next == null ? void 0 : expandedHunks.get(next.firstIndex);
128
+ const fromStart = fromStartSource?.fromStart ?? 0;
129
+ const fromEnd = fromEndSource?.fromEnd ?? 0;
130
+ if (fromStart > 0 || fromEnd > 0) remapped.set(key, {
131
+ fromStart,
132
+ fromEnd
133
+ });
134
+ }
135
+ return remapped;
136
+ }
137
+ /** Snapshot the expanded gap-edge slices before the exit recompute. */
138
+ function captureExpansionAnchors(diff, expandedHunks, collapsedContextThreshold) {
139
+ const anchors = [];
140
+ if (diff.isPartial) return anchors;
141
+ for (const [hunkIndex, hunk] of diff.hunks.entries()) {
142
+ const region = getExpandedRegion({
143
+ isPartial: diff.isPartial,
144
+ rangeSize: hunk.collapsedBefore,
145
+ expandedHunks,
146
+ hunkIndex,
147
+ collapsedContextThreshold
148
+ });
149
+ if (region.rangeSize <= collapsedContextThreshold) continue;
150
+ const gapEnd = getHunkDeletionStart(hunk);
151
+ const gapStart = gapEnd - region.rangeSize;
152
+ if (region.fromStart > 0) anchors.push([gapStart, gapStart + region.fromStart]);
153
+ if (region.fromEnd > 0) anchors.push([gapEnd - region.fromEnd, gapEnd]);
154
+ }
155
+ const trailingRegion = getTrailingExpandedRegion({
156
+ fileDiff: diff,
157
+ hunkIndex: diff.hunks.length - 1,
158
+ expandedHunks,
159
+ collapsedContextThreshold,
160
+ errorPrefix: "captureExpansionAnchors"
161
+ });
162
+ if (trailingRegion != null && trailingRegion.fromStart > 0 && trailingRegion.rangeSize > collapsedContextThreshold) {
163
+ const lastHunk = diff.hunks[diff.hunks.length - 1];
164
+ const gapStart = getHunkDeletionStart(lastHunk) + lastHunk.deletionCount;
165
+ anchors.push([gapStart, gapStart + trailingRegion.fromStart]);
166
+ }
167
+ return anchors;
168
+ }
169
+ /**
170
+ * Rebuild gap expansion state against the recomputed hunks: for each new
171
+ * gap, an anchor touching the gap's start edge restores `fromStart`, one
172
+ * touching its end edge restores `fromEnd`, and anchors for gaps that no
173
+ * longer exist drop.
174
+ */
175
+ function rebuildExpansionFromAnchors(diff, anchors) {
176
+ const rebuilt = /* @__PURE__ */ new Map();
177
+ if (anchors.length === 0) return rebuilt;
178
+ const applyGap = (key, gapStart, gapEnd) => {
179
+ if (gapEnd <= gapStart) return;
180
+ let fromStart = 0;
181
+ let fromEnd = 0;
182
+ for (const [start, end] of anchors) {
183
+ if (end <= gapStart || start >= gapEnd) continue;
184
+ if (start <= gapStart) fromStart = Math.max(fromStart, Math.min(end, gapEnd) - gapStart);
185
+ if (end >= gapEnd) fromEnd = Math.max(fromEnd, gapEnd - Math.max(start, gapStart));
186
+ }
187
+ if (fromStart > 0 || fromEnd > 0) rebuilt.set(key, {
188
+ fromStart,
189
+ fromEnd
190
+ });
191
+ };
192
+ for (const [hunkIndex, hunk] of diff.hunks.entries()) {
193
+ const gapEnd = getHunkDeletionStart(hunk);
194
+ applyGap(hunkIndex, gapEnd - Math.max(hunk.collapsedBefore, 0), gapEnd);
195
+ }
196
+ const lastHunk = diff.hunks[diff.hunks.length - 1];
197
+ if (lastHunk != null && !diff.isPartial && diff.deletionLines.length > 0) applyGap(diff.hunks.length, getHunkDeletionStart(lastHunk) + lastHunk.deletionCount, diff.deletionLines.length);
198
+ return rebuilt;
199
+ }
200
+ /**
201
+ * Genuine session exit: when session passes reshaped the hunks, run the real
202
+ * full recompute so exit state matches a non-session edit pipeline, and clear
203
+ * the marker. Returns true when a recompute ran.
204
+ */
205
+ function finishEditSessionForDiff(diff, parseDiffOptions) {
206
+ if (diff.editSessionDirty !== true) return false;
207
+ diff.editSessionDirty = void 0;
208
+ Object.assign(diff, recomputeDiffHunksForEdit(diff, parseDiffOptions));
209
+ return true;
210
+ }
211
+ function parseCanonicalChangeBlocks(diff, parseDiffOptions) {
212
+ if (findDivergenceCore(diff.deletionLines, diff.additionLines) == null) return [];
213
+ const parsed = parseDiffFromFile({
214
+ name: diff.prevName ?? diff.name,
215
+ contents: diff.deletionLines.join("")
216
+ }, {
217
+ name: diff.name,
218
+ contents: diff.additionLines.join(""),
219
+ lang: diff.lang
220
+ }, parseDiffOptions);
221
+ const blocks = [];
222
+ let coveredAdditions = 0;
223
+ let coveredDeletions = 0;
224
+ for (const hunk of parsed.hunks) {
225
+ const contextLines = hunk.additionCount > 0 ? hunk.additionLineIndex - coveredAdditions : hunk.deletionLineIndex - coveredDeletions;
226
+ coveredAdditions += contextLines;
227
+ coveredDeletions += contextLines;
228
+ for (const content of hunk.hunkContent) {
229
+ if (content.type === "context") {
230
+ coveredAdditions += content.lines;
231
+ coveredDeletions += content.lines;
232
+ continue;
233
+ }
234
+ const block = offsetHunkContent(content, 0, 0);
235
+ if (block.additions === 0) block.additionLineIndex = coveredAdditions;
236
+ if (block.deletions === 0) block.deletionLineIndex = coveredDeletions;
237
+ blocks.push(block);
238
+ coveredAdditions += block.additions;
239
+ coveredDeletions += block.deletions;
240
+ }
241
+ }
242
+ return blocks;
243
+ }
244
+ function buildRegionPlans(previousHunks, blocks, deletionLineCount) {
245
+ const previousPlans = previousHunks.map((hunk, index) => {
246
+ const deletionStart = getHunkDeletionStart(hunk);
247
+ return {
248
+ deletionStart,
249
+ deletionEnd: deletionStart + hunk.deletionCount,
250
+ blocks: [],
251
+ previousSpan: {
252
+ firstIndex: index,
253
+ lastIndex: index
254
+ }
255
+ };
256
+ });
257
+ const plans = [];
258
+ let previousIndex = 0;
259
+ for (const block of blocks) {
260
+ const blockStart = block.deletionLineIndex;
261
+ const blockEnd = blockStart + block.deletions;
262
+ while (previousIndex < previousPlans.length && previousPlans[previousIndex].deletionEnd < blockStart) {
263
+ plans.push(previousPlans[previousIndex]);
264
+ previousIndex++;
265
+ }
266
+ let plan = plans.length > 0 && blockTouchesRegion(blockStart, blockEnd, plans[plans.length - 1]) ? plans.pop() : void 0;
267
+ while (previousIndex < previousPlans.length && previousPlans[previousIndex].deletionStart <= blockEnd) {
268
+ plan = mergeRegionPlans(plan, previousPlans[previousIndex]);
269
+ previousIndex++;
270
+ }
271
+ if (plan == null) {
272
+ let deletionStart = blockStart;
273
+ let deletionEnd = blockEnd;
274
+ if ((block.deletions === 0 || block.additions === 0) && deletionLineCount > block.deletions) {
275
+ const previousEnd = plans[plans.length - 1]?.deletionEnd ?? 0;
276
+ const nextStart = previousPlans[previousIndex]?.deletionStart ?? deletionLineCount;
277
+ if (blockStart > previousEnd) deletionStart--;
278
+ else if (blockEnd < nextStart) deletionEnd++;
279
+ }
280
+ plan = {
281
+ deletionStart,
282
+ deletionEnd,
283
+ blocks: [],
284
+ previousSpan: void 0
285
+ };
286
+ }
287
+ plan.deletionStart = Math.min(plan.deletionStart, blockStart);
288
+ plan.deletionEnd = Math.max(plan.deletionEnd, blockEnd);
289
+ plan.blocks.push(block);
290
+ plans.push(plan);
291
+ }
292
+ while (previousIndex < previousPlans.length) {
293
+ plans.push(previousPlans[previousIndex]);
294
+ previousIndex++;
295
+ }
296
+ return plans;
297
+ }
298
+ function blockTouchesRegion(blockStart, blockEnd, region) {
299
+ return blockStart <= region.deletionEnd && blockEnd >= region.deletionStart;
300
+ }
301
+ function mergeRegionPlans(target, source) {
302
+ if (target == null) return source;
303
+ target.deletionStart = Math.min(target.deletionStart, source.deletionStart);
304
+ target.deletionEnd = Math.max(target.deletionEnd, source.deletionEnd);
305
+ target.blocks.push(...source.blocks);
306
+ if (source.previousSpan != null) {
307
+ target.previousSpan ??= { ...source.previousSpan };
308
+ target.previousSpan.firstIndex = Math.min(target.previousSpan.firstIndex, source.previousSpan.firstIndex);
309
+ target.previousSpan.lastIndex = Math.max(target.previousSpan.lastIndex, source.previousSpan.lastIndex);
310
+ }
311
+ return target;
312
+ }
313
+ function buildRegionHunks(diff, plans) {
314
+ const hunks = [];
315
+ let deletionCursor = 0;
316
+ let additionCursor = 0;
317
+ for (const plan of plans) {
318
+ const contextBefore = plan.deletionStart - deletionCursor;
319
+ if (contextBefore < 0) throw new Error("buildRegionHunks: overlapping old-side regions");
320
+ deletionCursor += contextBefore;
321
+ additionCursor += contextBefore;
322
+ const additionStart = additionCursor;
323
+ const hunkContent = [];
324
+ for (const canonicalBlock of plan.blocks) {
325
+ const deletionContext = canonicalBlock.deletionLineIndex - deletionCursor;
326
+ const additionContext = canonicalBlock.additionLineIndex - additionCursor;
327
+ if (deletionContext < 0 || deletionContext !== additionContext) throw new Error("buildRegionHunks: canonical block context mismatch");
328
+ pushContext(hunkContent, deletionContext, additionCursor, deletionCursor);
329
+ deletionCursor += deletionContext;
330
+ additionCursor += additionContext;
331
+ hunkContent.push({ ...canonicalBlock });
332
+ deletionCursor += canonicalBlock.deletions;
333
+ additionCursor += canonicalBlock.additions;
334
+ }
335
+ const trailingContext = plan.deletionEnd - deletionCursor;
336
+ if (trailingContext < 0) throw new Error("buildRegionHunks: block exceeds its old-side region");
337
+ pushContext(hunkContent, trailingContext, additionCursor, deletionCursor);
338
+ deletionCursor += trailingContext;
339
+ additionCursor += trailingContext;
340
+ hunks.push(createRegionHunk(diff, {
341
+ additionStart,
342
+ additionEnd: additionCursor,
343
+ deletionStart: plan.deletionStart,
344
+ deletionEnd: plan.deletionEnd
345
+ }, hunkContent));
346
+ }
347
+ if (diff.deletionLines.length - deletionCursor !== diff.additionLines.length - additionCursor) throw new Error("buildRegionHunks: trailing context mismatch");
348
+ return hunks;
349
+ }
350
+ function createRegionHunk(diff, bounds, hunkContent) {
351
+ const additionCount = bounds.additionEnd - bounds.additionStart;
352
+ const deletionCount = bounds.deletionEnd - bounds.deletionStart;
353
+ let additionLines = 0;
354
+ let deletionLines = 0;
355
+ for (const content of hunkContent) if (content.type === "change") {
356
+ additionLines += content.additions;
357
+ deletionLines += content.deletions;
358
+ }
359
+ const hunk = {
360
+ collapsedBefore: 0,
361
+ additionStart: getUnifiedStart(bounds.additionStart, additionCount),
362
+ additionCount,
363
+ additionLines,
364
+ additionLineIndex: getUnifiedLineIndex(bounds.additionStart, additionCount),
365
+ deletionStart: getUnifiedStart(bounds.deletionStart, deletionCount),
366
+ deletionCount,
367
+ deletionLines,
368
+ deletionLineIndex: getUnifiedLineIndex(bounds.deletionStart, deletionCount),
369
+ hunkContent,
370
+ hunkSpecs: `@@ -${getUnifiedStart(bounds.deletionStart, deletionCount)},${deletionCount} +${getUnifiedStart(bounds.additionStart, additionCount)},${additionCount} @@`,
371
+ splitLineStart: 0,
372
+ splitLineCount: 0,
373
+ unifiedLineStart: 0,
374
+ unifiedLineCount: 0,
375
+ noEOFCRAdditions: false,
376
+ noEOFCRDeletions: false
377
+ };
378
+ recomputeHunkRenderLineCounts(hunk);
379
+ return hunk;
380
+ }
381
+ function pushContext(hunkContent, lines, additionLineIndex, deletionLineIndex) {
382
+ if (lines > 0) hunkContent.push({
383
+ type: "context",
384
+ lines,
385
+ additionLineIndex,
386
+ deletionLineIndex
387
+ });
388
+ }
389
+ function hasRegionOrSplitLayoutChanged(previous, next) {
390
+ if (previous.length !== next.length) return true;
391
+ for (let index = 0; index < previous.length; index++) {
392
+ const previousHunk = previous[index];
393
+ const nextHunk = next[index];
394
+ if (getHunkDeletionStart(previousHunk) !== getHunkDeletionStart(nextHunk) || previousHunk.deletionCount !== nextHunk.deletionCount || getHunkAdditionStart(previousHunk) !== getHunkAdditionStart(nextHunk) || previousHunk.additionCount !== nextHunk.additionCount || previousHunk.splitLineCount !== nextHunk.splitLineCount || !haveSameSplitRowMapping(previousHunk, nextHunk)) return true;
395
+ }
396
+ return false;
397
+ }
398
+ function haveSameSplitRowMapping(previous, next) {
399
+ const previousRows = iterateSplitRowMapping(previous);
400
+ const nextRows = iterateSplitRowMapping(next);
401
+ while (true) {
402
+ const previousRow = previousRows.next();
403
+ const nextRow = nextRows.next();
404
+ if (previousRow.done === true || nextRow.done === true) return previousRow.done === nextRow.done;
405
+ if (previousRow.value[0] !== nextRow.value[0] || previousRow.value[1] !== nextRow.value[1]) return false;
406
+ }
407
+ }
408
+ function* iterateSplitRowMapping(hunk) {
409
+ for (const content of hunk.hunkContent) {
410
+ if (content.type === "context") {
411
+ for (let offset = 0; offset < content.lines; offset++) yield [content.deletionLineIndex + offset, content.additionLineIndex + offset];
412
+ continue;
413
+ }
414
+ const rowCount = Math.max(content.deletions, content.additions);
415
+ for (let offset = 0; offset < rowCount; offset++) yield [offset < content.deletions ? content.deletionLineIndex + offset : void 0, offset < content.additions ? content.additionLineIndex + offset : void 0];
416
+ }
417
+ }
418
+ function getHunkAdditionStart(hunk) {
419
+ return getHunkSideStartBoundary(hunk.additionStart, hunk.additionCount);
420
+ }
421
+ function getHunkDeletionStart(hunk) {
422
+ return getHunkSideStartBoundary(hunk.deletionStart, hunk.deletionCount);
423
+ }
424
+ function getUnifiedStart(lineIndex, count) {
425
+ return count === 0 ? lineIndex : lineIndex + 1;
426
+ }
427
+ function getUnifiedLineIndex(lineIndex, count) {
428
+ return count === 0 ? lineIndex - 1 : lineIndex;
429
+ }
430
+ function finalizeSessionHunks(diff) {
431
+ recomputeDiffRenderLineCounts(diff);
432
+ for (let index = 0; index < diff.hunks.length; index++) syncHunkNoEOFCRFromFullFile(diff, index);
433
+ }
434
+ //#endregion
435
+ export { applySessionChangedLines, captureExpansionAnchors, findDivergenceCore, finishEditSessionForDiff, normalizeEditorLines, rebuildExpansionFromAnchors, rebuildSessionHunks, remapExpandedHunksForRegionChange };
436
+
437
+ //# sourceMappingURL=editSessionHunks.js.map