@pierre/diffs 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/dist/components/CodeView.d.ts +113 -68
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +153 -58
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +75 -15
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +223 -62
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +97 -21
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +400 -100
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts +2 -2
  14. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  15. package/dist/components/UnresolvedFile.js +2 -2
  16. package/dist/components/UnresolvedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFile.d.ts +16 -7
  18. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  19. package/dist/components/VirtualizedFile.js +140 -51
  20. package/dist/components/VirtualizedFile.js.map +1 -1
  21. package/dist/components/VirtualizedFileDiff.d.ts +14 -8
  22. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  23. package/dist/components/VirtualizedFileDiff.js +183 -93
  24. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  25. package/dist/components/VirtulizerDevelopment.d.ts +1 -1
  26. package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
  27. package/dist/edit/index.d.ts +7 -4
  28. package/dist/edit/index.js +5 -1
  29. package/dist/editor/EditStateManager.d.ts +56 -0
  30. package/dist/editor/EditStateManager.d.ts.map +1 -0
  31. package/dist/editor/EditStateManager.js +135 -0
  32. package/dist/editor/EditStateManager.js.map +1 -0
  33. package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts +7 -0
  34. package/dist/editor/cloneRetainedDiffSessionSnapshot.d.ts.map +1 -0
  35. package/dist/editor/cloneRetainedDiffSessionSnapshot.js +16 -0
  36. package/dist/editor/cloneRetainedDiffSessionSnapshot.js.map +1 -0
  37. package/dist/editor/editPrediction.d.ts +76 -0
  38. package/dist/editor/editPrediction.d.ts.map +1 -0
  39. package/dist/editor/editPrediction.js +365 -0
  40. package/dist/editor/editPrediction.js.map +1 -0
  41. package/dist/editor/editStack.d.ts +19 -40
  42. package/dist/editor/editStack.d.ts.map +1 -1
  43. package/dist/editor/editStack.js +67 -4
  44. package/dist/editor/editStack.js.map +1 -1
  45. package/dist/editor/editor.d.ts +118 -29
  46. package/dist/editor/editor.d.ts.map +1 -1
  47. package/dist/editor/editor.js +1196 -403
  48. package/dist/editor/editor.js.map +1 -1
  49. package/dist/editor/editor2.js +1 -1
  50. package/dist/editor/languages.d.ts +3 -3
  51. package/dist/editor/languages.d.ts.map +1 -1
  52. package/dist/editor/languages.js.map +1 -1
  53. package/dist/editor/lineAnnotations.d.ts +3 -3
  54. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  55. package/dist/editor/lineAnnotations.js +16 -7
  56. package/dist/editor/lineAnnotations.js.map +1 -1
  57. package/dist/editor/marker.d.ts +2 -2
  58. package/dist/editor/marker.d.ts.map +1 -1
  59. package/dist/editor/marker.js.map +1 -1
  60. package/dist/editor/matchBrackets.d.ts +2 -2
  61. package/dist/editor/matchBrackets.d.ts.map +1 -1
  62. package/dist/editor/matchBrackets.js.map +1 -1
  63. package/dist/editor/pieceTable.d.ts +1 -1
  64. package/dist/editor/pieceTable.js.map +1 -1
  65. package/dist/editor/searchPanel.d.ts +2 -2
  66. package/dist/editor/searchPanel.d.ts.map +1 -1
  67. package/dist/editor/searchPanel.js +3 -2
  68. package/dist/editor/searchPanel.js.map +1 -1
  69. package/dist/editor/selection.d.ts +33 -23
  70. package/dist/editor/selection.d.ts.map +1 -1
  71. package/dist/editor/selection.js +3 -3
  72. package/dist/editor/selection.js.map +1 -1
  73. package/dist/editor/selectionAction.d.ts +3 -3
  74. package/dist/editor/selectionAction.d.ts.map +1 -1
  75. package/dist/editor/selectionAction.js.map +1 -1
  76. package/dist/editor/textDocument.d.ts +11 -8
  77. package/dist/editor/textDocument.d.ts.map +1 -1
  78. package/dist/editor/textDocument.js +28 -5
  79. package/dist/editor/textDocument.js.map +1 -1
  80. package/dist/editor/textDocumentChangeTransaction.d.ts +15 -0
  81. package/dist/editor/textDocumentChangeTransaction.d.ts.map +1 -0
  82. package/dist/editor/textDocumentChangeTransaction.js +12 -0
  83. package/dist/editor/textDocumentChangeTransaction.js.map +1 -0
  84. package/dist/editor/textMeasure.d.ts +5 -0
  85. package/dist/editor/textMeasure.d.ts.map +1 -1
  86. package/dist/editor/textMeasure.js +10 -0
  87. package/dist/editor/textMeasure.js.map +1 -1
  88. package/dist/editor/tokenizer.d.ts +1 -1
  89. package/dist/editor/tokenizer.d.ts.map +1 -1
  90. package/dist/editor/tokenizer.js +6 -3
  91. package/dist/editor/tokenizer.js.map +1 -1
  92. package/dist/editor/types.d.ts +267 -0
  93. package/dist/editor/types.d.ts.map +1 -0
  94. package/dist/editor/types.js +0 -0
  95. package/dist/editor/utils.d.ts +2 -1
  96. package/dist/editor/utils.d.ts.map +1 -1
  97. package/dist/editor/utils.js +4 -1
  98. package/dist/editor/utils.js.map +1 -1
  99. package/dist/index.d.ts +8 -6
  100. package/dist/index.js +9 -8
  101. package/dist/managers/InteractionManager.d.ts.map +1 -1
  102. package/dist/managers/InteractionManager.js +7 -0
  103. package/dist/managers/InteractionManager.js.map +1 -1
  104. package/dist/react/CodeView.d.ts +36 -25
  105. package/dist/react/CodeView.d.ts.map +1 -1
  106. package/dist/react/CodeView.js +15 -14
  107. package/dist/react/CodeView.js.map +1 -1
  108. package/dist/react/EditContext.d.ts +8 -11
  109. package/dist/react/EditContext.d.ts.map +1 -1
  110. package/dist/react/EditContext.js +2 -9
  111. package/dist/react/EditContext.js.map +1 -1
  112. package/dist/react/File.d.ts +10 -6
  113. package/dist/react/File.d.ts.map +1 -1
  114. package/dist/react/File.js +8 -4
  115. package/dist/react/File.js.map +1 -1
  116. package/dist/react/FileDiff.d.ts +10 -5
  117. package/dist/react/FileDiff.d.ts.map +1 -1
  118. package/dist/react/FileDiff.js +8 -4
  119. package/dist/react/FileDiff.js.map +1 -1
  120. package/dist/react/MultiFileDiff.d.ts +8 -5
  121. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  122. package/dist/react/MultiFileDiff.js +8 -12
  123. package/dist/react/MultiFileDiff.js.map +1 -1
  124. package/dist/react/PatchDiff.d.ts +7 -4
  125. package/dist/react/PatchDiff.d.ts.map +1 -1
  126. package/dist/react/PatchDiff.js +8 -4
  127. package/dist/react/PatchDiff.js.map +1 -1
  128. package/dist/react/UnresolvedFile.d.ts +3 -3
  129. package/dist/react/UnresolvedFile.d.ts.map +1 -1
  130. package/dist/react/UnresolvedFile.js.map +1 -1
  131. package/dist/react/index.d.ts +8 -5
  132. package/dist/react/types.d.ts +46 -9
  133. package/dist/react/types.d.ts.map +1 -1
  134. package/dist/react/utils/renderDiffChildren.d.ts +14 -12
  135. package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
  136. package/dist/react/utils/renderDiffChildren.js +2 -2
  137. package/dist/react/utils/renderDiffChildren.js.map +1 -1
  138. package/dist/react/utils/renderFileChildren.d.ts +14 -12
  139. package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
  140. package/dist/react/utils/renderFileChildren.js +2 -2
  141. package/dist/react/utils/renderFileChildren.js.map +1 -1
  142. package/dist/react/utils/useFileDiffInstance.d.ts +22 -10
  143. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  144. package/dist/react/utils/useFileDiffInstance.js +112 -22
  145. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  146. package/dist/react/utils/useFileInstance.d.ts +15 -9
  147. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  148. package/dist/react/utils/useFileInstance.js +84 -25
  149. package/dist/react/utils/useFileInstance.js.map +1 -1
  150. package/dist/renderers/DiffHunksRenderer.d.ts +30 -15
  151. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  152. package/dist/renderers/DiffHunksRenderer.js +172 -108
  153. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  154. package/dist/renderers/FileRenderer.d.ts +27 -11
  155. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  156. package/dist/renderers/FileRenderer.js +138 -64
  157. package/dist/renderers/FileRenderer.js.map +1 -1
  158. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +2 -2
  159. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -1
  160. package/dist/renderers/UnresolvedFileHunksRenderer.js +2 -2
  161. package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -1
  162. package/dist/shiki-stream/tokenizer.d.ts.map +1 -1
  163. package/dist/shiki-stream/tokenizer.js +2 -1
  164. package/dist/shiki-stream/tokenizer.js.map +1 -1
  165. package/dist/ssr/index.d.ts +2 -2
  166. package/dist/ssr/preloadDiffs.d.ts +21 -21
  167. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  168. package/dist/ssr/preloadDiffs.js +1 -1
  169. package/dist/ssr/preloadDiffs.js.map +1 -1
  170. package/dist/ssr/preloadFile.d.ts +6 -6
  171. package/dist/ssr/preloadFile.d.ts.map +1 -1
  172. package/dist/ssr/preloadFile.js.map +1 -1
  173. package/dist/ssr/preloadPatchFile.d.ts +4 -4
  174. package/dist/ssr/preloadPatchFile.d.ts.map +1 -1
  175. package/dist/ssr/preloadPatchFile.js.map +1 -1
  176. package/dist/style.js +1 -1
  177. package/dist/types.d.ts +13 -263
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/utils/appendItems.d.ts +5 -0
  180. package/dist/utils/appendItems.d.ts.map +1 -0
  181. package/dist/utils/appendItems.js +8 -0
  182. package/dist/utils/appendItems.js.map +1 -0
  183. package/dist/utils/areDiffTargetsEqual.js +1 -1
  184. package/dist/utils/areDiffTargetsEqual.js.map +1 -1
  185. package/dist/utils/areFileTargetsEqual.d.ts +7 -0
  186. package/dist/utils/areFileTargetsEqual.d.ts.map +1 -0
  187. package/dist/utils/areFileTargetsEqual.js +8 -0
  188. package/dist/utils/areFileTargetsEqual.js.map +1 -0
  189. package/dist/utils/areManagedSnapshotsEqual.d.ts +1 -1
  190. package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
  191. package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
  192. package/dist/utils/areOptionsEqual.d.ts +3 -3
  193. package/dist/utils/areOptionsEqual.d.ts.map +1 -1
  194. package/dist/utils/areOptionsEqual.js.map +1 -1
  195. package/dist/utils/cloneFileDiffMetadata.d.ts +3 -2
  196. package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -1
  197. package/dist/utils/cloneFileDiffMetadata.js +8 -5
  198. package/dist/utils/cloneFileDiffMetadata.js.map +1 -1
  199. package/dist/utils/composeCacheKey.d.ts +6 -0
  200. package/dist/utils/composeCacheKey.d.ts.map +1 -0
  201. package/dist/utils/composeCacheKey.js +10 -0
  202. package/dist/utils/composeCacheKey.js.map +1 -0
  203. package/dist/utils/editSessionAnnotations.d.ts +69 -0
  204. package/dist/utils/editSessionAnnotations.d.ts.map +1 -0
  205. package/dist/utils/editSessionAnnotations.js +64 -0
  206. package/dist/utils/editSessionAnnotations.js.map +1 -0
  207. package/dist/utils/editSessionHunks.d.ts +5 -3
  208. package/dist/utils/editSessionHunks.d.ts.map +1 -1
  209. package/dist/utils/editSessionHunks.js +7 -5
  210. package/dist/utils/editSessionHunks.js.map +1 -1
  211. package/dist/utils/getDiffHunksRendererOptions.d.ts +1 -1
  212. package/dist/utils/getDiffHunksRendererOptions.d.ts.map +1 -1
  213. package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
  214. package/dist/utils/getFileRendererOptions.d.ts +1 -1
  215. package/dist/utils/getFileRendererOptions.d.ts.map +1 -1
  216. package/dist/utils/getFileRendererOptions.js.map +1 -1
  217. package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
  218. package/dist/utils/hydratePartialDiff.js +2 -1
  219. package/dist/utils/hydratePartialDiff.js.map +1 -1
  220. package/dist/utils/lineAnnotationIdentity.d.ts +16 -0
  221. package/dist/utils/lineAnnotationIdentity.d.ts.map +1 -0
  222. package/dist/utils/lineAnnotationIdentity.js +17 -0
  223. package/dist/utils/lineAnnotationIdentity.js.map +1 -0
  224. package/dist/utils/parseDiffFromFile.d.ts +2 -2
  225. package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
  226. package/dist/utils/parseDiffFromFile.js +6 -6
  227. package/dist/utils/parseDiffFromFile.js.map +1 -1
  228. package/dist/utils/parsePatchFiles.d.ts +6 -3
  229. package/dist/utils/parsePatchFiles.d.ts.map +1 -1
  230. package/dist/utils/parsePatchFiles.js +34 -9
  231. package/dist/utils/parsePatchFiles.js.map +1 -1
  232. package/dist/utils/renderDiffWithHighlighter.d.ts.map +1 -1
  233. package/dist/utils/renderDiffWithHighlighter.js +6 -5
  234. package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
  235. package/dist/utils/renderFileWithHighlighter.d.ts.map +1 -1
  236. package/dist/utils/renderFileWithHighlighter.js +7 -4
  237. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  238. package/dist/utils/trimPatchContext.d.ts.map +1 -1
  239. package/dist/utils/trimPatchContext.js +3 -2
  240. package/dist/utils/trimPatchContext.js.map +1 -1
  241. package/dist/utils/updateDiffHunks.js +1 -1
  242. package/dist/utils/virtualDiffLayout.js +1 -1
  243. package/dist/worker/WorkerPoolManager.d.ts +4 -3
  244. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  245. package/dist/worker/WorkerPoolManager.js +33 -4
  246. package/dist/worker/WorkerPoolManager.js.map +1 -1
  247. package/dist/worker/types.d.ts +5 -0
  248. package/dist/worker/types.d.ts.map +1 -1
  249. package/dist/worker/worker-portable.js +14 -6
  250. package/dist/worker/worker-portable.js.map +1 -1
  251. package/dist/worker/worker.js +14 -6
  252. package/dist/worker/worker.js.map +1 -1
  253. package/package.json +9 -1
  254. package/dist/editor/stateStorage.d.ts +0 -15
  255. package/dist/editor/stateStorage.d.ts.map +0 -1
  256. package/dist/editor/stateStorage.js +0 -65
  257. package/dist/editor/stateStorage.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { composeCacheKey } from "./composeCacheKey.js";
1
2
  import { processFile } from "./parsePatchFiles.js";
2
3
  import { createTwoFilesPatch } from "diff";
3
4
  //#region src/utils/parseDiffFromFile.ts
@@ -5,8 +6,8 @@ const MISSING_FILE_NAME = "/dev/null";
5
6
  /**
6
7
  * Parses a diff from two file contents objects.
7
8
  *
8
- * If both `oldFile` and `newFile` have a `cacheKey`, the resulting diff will
9
- * automatically get a combined cache key in the format `oldKey:newKey`.
9
+ * If both `oldFile` and `newFile` have a `cacheKey`, the resulting diff gets a
10
+ * collision-safe key derived from both values.
10
11
  */
11
12
  function parseDiffFromFile(oldFile, newFile, options, throwOnError = false) {
12
13
  if (oldFile === null && newFile === null) throw new Error("parseDiffFromFile: You must pass oldFile, newFile, or both");
@@ -14,10 +15,9 @@ function parseDiffFromFile(oldFile, newFile, options, throwOnError = false) {
14
15
  const resolvedNewFile = newFile ?? createMissingFile();
15
16
  const fileData = processFile(createTwoFilesPatch(resolvedOldFile.name, resolvedNewFile.name, resolvedOldFile.contents, resolvedNewFile.contents, resolvedOldFile.header, resolvedNewFile.header, options), {
16
17
  cacheKey: (() => {
17
- const oldCacheKey = oldFile?.cacheKey ?? oldFile?.name;
18
- const newCacheKey = newFile?.cacheKey ?? newFile?.name;
19
- if (oldCacheKey != null && newCacheKey != null) return oldCacheKey + ":" + newCacheKey;
20
- return oldCacheKey ?? newCacheKey;
18
+ const oldCacheKey = oldFile?.cacheKey;
19
+ const newCacheKey = newFile?.cacheKey;
20
+ return oldCacheKey != null && newCacheKey != null ? composeCacheKey("diff", oldCacheKey, newCacheKey) : void 0;
21
21
  })(),
22
22
  oldFile: resolvedOldFile,
23
23
  newFile: resolvedNewFile,
@@ -1 +1 @@
1
- {"version":3,"file":"parseDiffFromFile.js","names":[],"sources":["../../src/utils/parseDiffFromFile.ts"],"sourcesContent":["import { type CreatePatchOptionsNonabortable, createTwoFilesPatch } from 'diff';\n\nimport type { FileContents, FileDiffMetadata } from '../types';\nimport { processFile } from './parsePatchFiles';\n\nconst MISSING_FILE_NAME = '/dev/null';\n\n/**\n * Parses a diff from two file contents objects.\n *\n * If both `oldFile` and `newFile` have a `cacheKey`, the resulting diff will\n * automatically get a combined cache key in the format `oldKey:newKey`.\n */\nexport function parseDiffFromFile(\n oldFile: FileContents | null,\n newFile: FileContents | null,\n options?: CreatePatchOptionsNonabortable,\n throwOnError = false\n): FileDiffMetadata {\n if (oldFile === null && newFile === null) {\n throw new Error(\n 'parseDiffFromFile: You must pass oldFile, newFile, or both'\n );\n }\n\n const resolvedOldFile = oldFile ?? createMissingFile();\n const resolvedNewFile = newFile ?? createMissingFile();\n const patch = createTwoFilesPatch(\n resolvedOldFile.name,\n resolvedNewFile.name,\n resolvedOldFile.contents,\n resolvedNewFile.contents,\n resolvedOldFile.header,\n resolvedNewFile.header,\n options\n );\n\n const fileData = processFile(patch, {\n cacheKey: (() => {\n const oldCacheKey = oldFile?.cacheKey ?? oldFile?.name;\n const newCacheKey = newFile?.cacheKey ?? newFile?.name;\n if (oldCacheKey != null && newCacheKey != null) {\n return oldCacheKey + ':' + newCacheKey;\n }\n return oldCacheKey ?? newCacheKey;\n })(),\n oldFile: resolvedOldFile,\n newFile: resolvedNewFile,\n throwOnError,\n });\n if (fileData == null) {\n throw new Error(\n 'parseDiffFrom: FileInvalid diff -- probably need to fix something -- if the files are the same maybe?'\n );\n }\n if (oldFile === null) {\n fileData.type = 'new';\n fileData.prevName = undefined;\n } else if (newFile === null) {\n fileData.type = 'deleted';\n fileData.prevName = undefined;\n }\n // If we've been provided an override for language in the newFile, let’s pass\n // it through to FileDiffMetadata.\n const language =\n newFile?.lang ?? (newFile === null ? oldFile?.lang : undefined);\n if (language != null) {\n fileData.lang = language;\n }\n return fileData;\n}\n\nfunction createMissingFile(): FileContents {\n return { name: MISSING_FILE_NAME, contents: '' };\n}\n"],"mappings":";;;AAKA,MAAM,oBAAoB;;;;;;;AAQ1B,SAAgB,kBACd,SACA,SACA,SACA,eAAe,OACG;CAClB,IAAI,YAAY,QAAQ,YAAY,MAClC,MAAM,IAAI,MACR,4DACF;CAGF,MAAM,kBAAkB,WAAW,kBAAkB;CACrD,MAAM,kBAAkB,WAAW,kBAAkB;CAWrD,MAAM,WAAW,YAVH,oBACZ,gBAAgB,MAChB,gBAAgB,MAChB,gBAAgB,UAChB,gBAAgB,UAChB,gBAAgB,QAChB,gBAAgB,QAChB,OAG+B,GAAG;EAClC,iBAAiB;GACf,MAAM,cAAc,SAAS,YAAY,SAAS;GAClD,MAAM,cAAc,SAAS,YAAY,SAAS;GAClD,IAAI,eAAe,QAAQ,eAAe,MACxC,OAAO,cAAc,MAAM;GAE7B,OAAO,eAAe;EACxB,EAAA,CAAG;EACH,SAAS;EACT,SAAS;EACT;CACF,CAAC;CACD,IAAI,YAAY,MACd,MAAM,IAAI,MACR,uGACF;CAEF,IAAI,YAAY,MAAM;EACpB,SAAS,OAAO;EAChB,SAAS,WAAW,KAAA;CACtB,OAAO,IAAI,YAAY,MAAM;EAC3B,SAAS,OAAO;EAChB,SAAS,WAAW,KAAA;CACtB;CAGA,MAAM,WACJ,SAAS,SAAS,YAAY,OAAO,SAAS,OAAO,KAAA;CACvD,IAAI,YAAY,MACd,SAAS,OAAO;CAElB,OAAO;AACT;AAEA,SAAS,oBAAkC;CACzC,OAAO;EAAE,MAAM;EAAmB,UAAU;CAAG;AACjD"}
1
+ {"version":3,"file":"parseDiffFromFile.js","names":[],"sources":["../../src/utils/parseDiffFromFile.ts"],"sourcesContent":["import { type CreatePatchOptionsNonabortable, createTwoFilesPatch } from 'diff';\n\nimport type { FileContents, FileDiffMetadata } from '../types';\nimport { composeCacheKey } from './composeCacheKey';\nimport { processFile } from './parsePatchFiles';\n\nconst MISSING_FILE_NAME = '/dev/null';\n\n/**\n * Parses a diff from two file contents objects.\n *\n * If both `oldFile` and `newFile` have a `cacheKey`, the resulting diff gets a\n * collision-safe key derived from both values.\n */\nexport function parseDiffFromFile(\n oldFile: FileContents | null,\n newFile: FileContents | null,\n options?: CreatePatchOptionsNonabortable,\n throwOnError = false\n): FileDiffMetadata {\n if (oldFile === null && newFile === null) {\n throw new Error(\n 'parseDiffFromFile: You must pass oldFile, newFile, or both'\n );\n }\n\n const resolvedOldFile = oldFile ?? createMissingFile();\n const resolvedNewFile = newFile ?? createMissingFile();\n const patch = createTwoFilesPatch(\n resolvedOldFile.name,\n resolvedNewFile.name,\n resolvedOldFile.contents,\n resolvedNewFile.contents,\n resolvedOldFile.header,\n resolvedNewFile.header,\n options\n );\n\n const fileData = processFile(patch, {\n cacheKey: (() => {\n const oldCacheKey = oldFile?.cacheKey;\n const newCacheKey = newFile?.cacheKey;\n return oldCacheKey != null && newCacheKey != null\n ? composeCacheKey('diff', oldCacheKey, newCacheKey)\n : undefined;\n })(),\n oldFile: resolvedOldFile,\n newFile: resolvedNewFile,\n throwOnError,\n });\n if (fileData == null) {\n throw new Error(\n 'parseDiffFrom: FileInvalid diff -- probably need to fix something -- if the files are the same maybe?'\n );\n }\n if (oldFile === null) {\n fileData.type = 'new';\n fileData.prevName = undefined;\n } else if (newFile === null) {\n fileData.type = 'deleted';\n fileData.prevName = undefined;\n }\n // If we've been provided an override for language in the newFile, let’s pass\n // it through to FileDiffMetadata.\n const language =\n newFile?.lang ?? (newFile === null ? oldFile?.lang : undefined);\n if (language != null) {\n fileData.lang = language;\n }\n return fileData;\n}\n\nfunction createMissingFile(): FileContents {\n return { name: MISSING_FILE_NAME, contents: '' };\n}\n"],"mappings":";;;;AAMA,MAAM,oBAAoB;;;;;;;AAQ1B,SAAgB,kBACd,SACA,SACA,SACA,eAAe,OACG;CAClB,IAAI,YAAY,QAAQ,YAAY,MAClC,MAAM,IAAI,MACR,4DACF;CAGF,MAAM,kBAAkB,WAAW,kBAAkB;CACrD,MAAM,kBAAkB,WAAW,kBAAkB;CAWrD,MAAM,WAAW,YAVH,oBACZ,gBAAgB,MAChB,gBAAgB,MAChB,gBAAgB,UAChB,gBAAgB,UAChB,gBAAgB,QAChB,gBAAgB,QAChB,OAG+B,GAAG;EAClC,iBAAiB;GACf,MAAM,cAAc,SAAS;GAC7B,MAAM,cAAc,SAAS;GAC7B,OAAO,eAAe,QAAQ,eAAe,OACzC,gBAAgB,QAAQ,aAAa,WAAW,IAChD,KAAA;EACN,EAAA,CAAG;EACH,SAAS;EACT,SAAS;EACT;CACF,CAAC;CACD,IAAI,YAAY,MACd,MAAM,IAAI,MACR,uGACF;CAEF,IAAI,YAAY,MAAM;EACpB,SAAS,OAAO;EAChB,SAAS,WAAW,KAAA;CACtB,OAAO,IAAI,YAAY,MAAM;EAC3B,SAAS,OAAO;EAChB,SAAS,WAAW,KAAA;CACtB;CAGA,MAAM,WACJ,SAAS,SAAS,YAAY,OAAO,SAAS,OAAO,KAAA;CACvD,IAAI,YAAY,MACd,SAAS,OAAO;CAElB,OAAO;AACT;AAEA,SAAS,oBAAkC;CACzC,OAAO;EAAE,MAAM;EAAmB,UAAU;CAAG;AACjD"}
@@ -14,9 +14,12 @@ declare function processFile(fileDiffString: string, options?: ProcessFileOption
14
14
  * Parses a patch file string into an array of parsed patches.
15
15
  *
16
16
  * @param data - The raw patch file content (supports multi-commit patches)
17
- * @param cacheKeyPrefix - Optional prefix for generating cache keys. When provided,
18
- * each file in the patch will get a cache key in the format `prefix-patchIndex-fileIndex`.
19
- * This enables caching of rendered diff results in the worker pool.
17
+ * @param cacheKeyPrefix - Optional prefix for collision-safe cache keys derived
18
+ * from the prefix, patch index, and file index. This enables caching of
19
+ * rendered diff results in the worker pool.
20
+ * @param throwOnError - When true, invalid data throws. When false, invalid data
21
+ * is reported with `console.error` and the parser attempts to recover when
22
+ * possible. Recovery is best-effort and does not guarantee valid output.
20
23
  */
21
24
  declare function parsePatchFiles(data: string, cacheKeyPrefix?: string, throwOnError?: boolean): ParsedPatch[];
22
25
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"parsePatchFiles.d.ts","names":[],"sources":["../../src/utils/parsePatchFiles.ts"],"mappings":";;;iBAiCgB,YAAA,CACd,IAAA,UACA,cAAA,WACA,YAAA,aACC,WAAW;AAAA,UAqEJ,kBAAA;EACR,QAAA;EACA,SAAA;EACA,OAAA,GAAU,YAAA;EACV,OAAA,GAAU,YAAY;EACtB,YAAA;AAAA;AAAA,iBAGc,WAAA,CACd,cAAA,UACA,OAAA,GAAU,kBAAA,GACT,gBAAgB;;;AAhFL;AAMb;;;;;iBAmjBe,eAAA,CACd,IAAA,UACA,cAAA,WACA,YAAA,aACC,WAAW"}
1
+ {"version":3,"file":"parsePatchFiles.d.ts","names":[],"sources":["../../src/utils/parsePatchFiles.ts"],"mappings":";;;iBAkCgB,YAAA,CACd,IAAA,UACA,cAAA,WACA,YAAA,aACC,WAAW;AAAA,UAiFJ,kBAAA;EACR,QAAA;EACA,SAAA;EACA,OAAA,GAAU,YAAA;EACV,OAAA,GAAU,YAAY;EACtB,YAAA;AAAA;AAAA,iBAGc,WAAA,CACd,cAAA,UACA,OAAA,GAAU,kBAAA,GACT,gBAAgB;;;AA5FL;AAMb;;;;;;;;iBAsmBe,eAAA,CACd,IAAA,UACA,cAAA,WACA,YAAA,aACC,WAAW"}
@@ -1,5 +1,6 @@
1
1
  import { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, GIT_DIFF_FILE_BREAK_REGEX, INDEX_LINE_METADATA } from "../constants.js";
2
2
  import { getHunkSideEndBoundary, getHunkSideStartBoundary } from "./getHunkSideBoundaries.js";
3
+ import { composeCacheKey } from "./composeCacheKey.js";
3
4
  import { cleanLastNewline } from "./cleanLastNewline.js";
4
5
  import { detachString, releaseStringDetachBuffer } from "./detachString.js";
5
6
  import { realignChangeContentBySimilarity } from "./realignChangeContent.js";
@@ -11,7 +12,7 @@ function processPatch(data, cacheKeyPrefix, throwOnError) {
11
12
  releaseStringDetachBuffer();
12
13
  }
13
14
  }
14
- function _processPatch(data, cacheKeyPrefix, throwOnError = false) {
15
+ function _processPatch(data, cacheKeyPrefix, throwOnError = false, patchIndex) {
15
16
  const isGitDiff = isGitDiffPatch(data);
16
17
  const rawFiles = isGitDiff ? splitGitDiffFiles(data) : splitUnifiedDiffFiles(data);
17
18
  let patchMetadata;
@@ -29,7 +30,7 @@ function _processPatch(data, cacheKeyPrefix, throwOnError = false) {
29
30
  continue;
30
31
  }
31
32
  const currentFile = _processFile(fileOrPatchMetadata, {
32
- cacheKey: cacheKeyPrefix != null ? `${cacheKeyPrefix}-${files.length}` : void 0,
33
+ cacheKey: cacheKeyPrefix != null ? patchIndex == null ? composeCacheKey("patch-file", cacheKeyPrefix, String(files.length)) : composeCacheKey("patch-file", cacheKeyPrefix, String(patchIndex), String(files.length)) : void 0,
33
34
  isGitDiff,
34
35
  throwOnError
35
36
  });
@@ -162,8 +163,8 @@ function _processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREA
162
163
  for (let lineIndex = 1; lineIndex < lines.length; lineIndex++) {
163
164
  const rawLine = lines[lineIndex];
164
165
  if (parsedAdditionLines >= hunkData.additionCount && parsedDeletionLines >= hunkData.deletionCount && !rawLine.startsWith("\\")) {
165
- if (throwOnError && isHunkBodyLine(rawLine) && !isFormatPatchVersionSeparator(rawLine)) throw Error("parsePatchContent: hunk has more lines than expected");
166
- break;
166
+ if (!(isHunkBodyLine(rawLine) && !isFormatPatchVersionSeparator(rawLine))) break;
167
+ if (throwOnError) throw Error("parsePatchContent: hunk has more lines than expected");
167
168
  }
168
169
  const firstChar = rawLine[0];
169
170
  if (firstChar !== "+" && firstChar !== "-" && firstChar !== " " && firstChar !== "\\") {
@@ -232,7 +233,26 @@ function _processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREA
232
233
  }
233
234
  }
234
235
  }
235
- if (throwOnError && (parsedAdditionLines !== hunkData.additionCount || parsedDeletionLines !== hunkData.deletionCount)) throw Error("parsePatchContent: hunk line count mismatch");
236
+ if (parsedAdditionLines !== hunkData.additionCount || parsedDeletionLines !== hunkData.deletionCount) {
237
+ if (throwOnError) throw Error("parsePatchContent: hunk line count mismatch");
238
+ console.error(`parsePatchContent: hunk line count mismatch: "${firstLine.trimEnd()}", declared old/new ${hunkData.deletionCount}/${hunkData.additionCount}, parsed old/new ${parsedDeletionLines}/${parsedAdditionLines}`);
239
+ const repairedAdditionStart = getHunkSideStartBoundary(hunkData.additionStart, hunkData.additionCount) + (parsedAdditionLines === 0 ? 0 : 1);
240
+ const repairedDeletionStart = getHunkSideStartBoundary(hunkData.deletionStart, hunkData.deletionCount) + (parsedDeletionLines === 0 ? 0 : 1);
241
+ if (!isPartial) {
242
+ const additionStartDelta = repairedAdditionStart - hunkData.additionStart;
243
+ const deletionStartDelta = repairedDeletionStart - hunkData.deletionStart;
244
+ hunkData.additionLineIndex += additionStartDelta;
245
+ hunkData.deletionLineIndex += deletionStartDelta;
246
+ for (const content of hunkData.hunkContent) {
247
+ content.additionLineIndex += additionStartDelta;
248
+ content.deletionLineIndex += deletionStartDelta;
249
+ }
250
+ }
251
+ hunkData.additionStart = repairedAdditionStart;
252
+ hunkData.deletionStart = repairedDeletionStart;
253
+ hunkData.additionCount = parsedAdditionLines;
254
+ hunkData.deletionCount = parsedDeletionLines;
255
+ }
236
256
  hunkData.additionLines = additionLines;
237
257
  hunkData.deletionLines = deletionLines;
238
258
  hunkData.collapsedBefore = Math.max(getHunkSideStartBoundary(hunkData.additionStart, hunkData.additionCount) - lastHunkEnd, 0);
@@ -274,18 +294,23 @@ function _processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREA
274
294
  * Parses a patch file string into an array of parsed patches.
275
295
  *
276
296
  * @param data - The raw patch file content (supports multi-commit patches)
277
- * @param cacheKeyPrefix - Optional prefix for generating cache keys. When provided,
278
- * each file in the patch will get a cache key in the format `prefix-patchIndex-fileIndex`.
279
- * This enables caching of rendered diff results in the worker pool.
297
+ * @param cacheKeyPrefix - Optional prefix for collision-safe cache keys derived
298
+ * from the prefix, patch index, and file index. This enables caching of
299
+ * rendered diff results in the worker pool.
300
+ * @param throwOnError - When true, invalid data throws. When false, invalid data
301
+ * is reported with `console.error` and the parser attempts to recover when
302
+ * possible. Recovery is best-effort and does not guarantee valid output.
280
303
  */
281
304
  function parsePatchFiles(data, cacheKeyPrefix, throwOnError = false) {
282
305
  const patches = [];
283
306
  const rawPatches = hasCommitMetadataBoundary(data) ? data.split(COMMIT_METADATA_SPLIT) : [data];
284
307
  for (const patch of rawPatches) try {
285
- patches.push(processPatch(patch, cacheKeyPrefix != null ? `${cacheKeyPrefix}-${patches.length}` : void 0, throwOnError));
308
+ patches.push(_processPatch(patch, cacheKeyPrefix, throwOnError, cacheKeyPrefix != null ? patches.length : void 0));
286
309
  } catch (error) {
287
310
  if (throwOnError) throw error;
288
311
  else console.error(error);
312
+ } finally {
313
+ releaseStringDetachBuffer();
289
314
  }
290
315
  return patches;
291
316
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parsePatchFiles.js","names":[],"sources":["../../src/utils/parsePatchFiles.ts"],"sourcesContent":["import {\n ALTERNATE_FILE_NAMES_GIT,\n COMMIT_METADATA_SPLIT,\n FILENAME_HEADER_REGEX,\n FILENAME_HEADER_REGEX_GIT,\n GIT_DIFF_FILE_BREAK_REGEX,\n INDEX_LINE_METADATA,\n} from '../constants';\nimport type {\n ChangeContent,\n ContextContent,\n FileContents,\n FileDiffMetadata,\n Hunk,\n HunkLineType,\n ParsedPatch,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { detachString, releaseStringDetachBuffer } from './detachString';\nimport {\n getHunkSideEndBoundary,\n getHunkSideStartBoundary,\n} from './getHunkSideBoundaries';\nimport { realignChangeContentBySimilarity } from './realignChangeContent';\n\ninterface ParsedHunkHeader {\n additionCount: number;\n additionStart: number;\n deletionCount: number;\n deletionStart: number;\n hunkContext?: string;\n}\n\nexport function processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError?: boolean\n): ParsedPatch {\n try {\n return _processPatch(data, cacheKeyPrefix, throwOnError);\n } finally {\n releaseStringDetachBuffer();\n }\n}\n\nfunction _processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch {\n const isGitDiff = isGitDiffPatch(data);\n const rawFiles = isGitDiff\n ? splitGitDiffFiles(data)\n : splitUnifiedDiffFiles(data);\n let patchMetadata: string | undefined;\n const files: FileDiffMetadata[] = [];\n for (const fileOrPatchMetadata of rawFiles) {\n if (isGitDiff && !GIT_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {\n if (patchMetadata == null) {\n patchMetadata = detachString(fileOrPatchMetadata);\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n // If we get in here, it's most likely the introductory metadata from the\n // patch, or something is fucked with the diff format\n continue;\n } else if (\n !isGitDiff &&\n !startsWithUnifiedDiffFileHeader(fileOrPatchMetadata)\n ) {\n if (patchMetadata == null) {\n patchMetadata = detachString(fileOrPatchMetadata);\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n continue;\n }\n const currentFile = _processFile(fileOrPatchMetadata, {\n cacheKey:\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${files.length}`\n : undefined,\n isGitDiff,\n throwOnError,\n });\n if (currentFile != null) {\n files.push(currentFile);\n }\n }\n return { patchMetadata, files };\n}\n\ninterface ProcessFileOptions {\n cacheKey?: string;\n isGitDiff?: boolean;\n oldFile?: FileContents;\n newFile?: FileContents;\n throwOnError?: boolean;\n}\n\nexport function processFile(\n fileDiffString: string,\n options?: ProcessFileOptions\n): FileDiffMetadata | undefined {\n try {\n return _processFile(fileDiffString, options);\n } finally {\n releaseStringDetachBuffer();\n }\n}\n\nfunction _processFile(\n fileDiffString: string,\n {\n cacheKey,\n isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(fileDiffString),\n oldFile,\n newFile,\n throwOnError = false,\n }: ProcessFileOptions = {}\n): FileDiffMetadata | undefined {\n let lastHunkEnd = 0;\n const hunks = splitAtLinePrefix(fileDiffString, '@@ ');\n let currentFile: FileDiffMetadata | undefined;\n const isPartial = oldFile == null || newFile == null;\n let deletionLineIndex = 0;\n let additionLineIndex = 0;\n for (const hunk of hunks) {\n const lines = splitWithNewlines(hunk);\n const firstLine = lines[0];\n if (firstLine == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk');\n } else {\n console.error('parsePatchContent: invalid hunk', hunk);\n }\n continue;\n }\n const fileHeader = parseHunkHeader(firstLine);\n let additionLines = 0;\n let deletionLines = 0;\n // Setup currentFile, this should be the first iteration of our hunks, and\n // technically not a hunk\n if (fileHeader == null || currentFile == null) {\n if (currentFile != null) {\n if (throwOnError) {\n throw Error('parsePatchContent: Invalid hunk');\n } else {\n console.error('parsePatchContent: Invalid hunk', hunk);\n }\n continue;\n }\n currentFile = {\n name: '',\n type: 'change',\n hunks: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n isPartial,\n additionLines:\n !isPartial && oldFile != null && newFile != null\n ? splitFileContents(newFile.contents)\n : [],\n deletionLines:\n !isPartial && oldFile != null && newFile != null\n ? splitFileContents(oldFile.contents)\n : [],\n cacheKey: maybeDetachOptionalString(cacheKey),\n };\n // If either file is technically empty, then we should empty the\n // arrays respectively\n if (currentFile.additionLines.length === 1 && newFile?.contents === '') {\n currentFile.additionLines.length = 0;\n }\n if (currentFile.deletionLines.length === 1 && oldFile?.contents === '') {\n currentFile.deletionLines.length = 0;\n }\n\n for (const line of lines) {\n if (line.startsWith('diff --git')) {\n const filenameMatch = line.trim().match(ALTERNATE_FILE_NAMES_GIT);\n const prevName = filenameMatch?.[1] ?? filenameMatch?.[2];\n const name = filenameMatch?.[3] ?? filenameMatch?.[4];\n if (prevName == null || name == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid git diff header');\n } else {\n console.error('parsePatchContent: invalid git diff header', line);\n }\n continue;\n }\n currentFile.name = detachString(name.trim());\n if (prevName !== name) {\n currentFile.prevName = detachString(prevName.trim());\n }\n continue;\n }\n\n const filenameMatch =\n line.startsWith('---') || line.startsWith('+++')\n ? line.match(\n isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX\n )\n : null;\n if (filenameMatch != null) {\n const [, type, fileName] = filenameMatch;\n if (type === '---' && fileName !== '/dev/null') {\n const detachedFileName = detachString(fileName.trim());\n currentFile.prevName = detachedFileName;\n currentFile.name = detachedFileName;\n } else if (type === '+++' && fileName !== '/dev/null') {\n currentFile.name = detachString(fileName.trim());\n }\n }\n // Git diffs have a bunch of additional metadata we can pull from\n else if (isGitDiff) {\n if (line.startsWith('new mode ')) {\n currentFile.mode = detachString(\n line.slice('new mode'.length).trim()\n );\n }\n if (line.startsWith('old mode ')) {\n currentFile.prevMode = detachString(\n line.slice('old mode'.length).trim()\n );\n }\n if (line.startsWith('new file mode')) {\n currentFile.type = 'new';\n currentFile.mode = detachString(\n line.slice('new file mode'.length).trim()\n );\n }\n if (line.startsWith('deleted file mode')) {\n currentFile.type = 'deleted';\n currentFile.mode = detachString(\n line.slice('deleted file mode'.length).trim()\n );\n }\n if (line.startsWith('similarity index')) {\n if (line.startsWith('similarity index 100%')) {\n currentFile.type = 'rename-pure';\n } else {\n currentFile.type = 'rename-changed';\n }\n }\n if (line.startsWith('index ')) {\n const [, prevObjectId, newObjectId, mode] =\n line.trim().match(INDEX_LINE_METADATA) ?? [];\n if (prevObjectId != null) {\n currentFile.prevObjectId = detachString(prevObjectId);\n }\n if (newObjectId != null) {\n currentFile.newObjectId = detachString(newObjectId);\n }\n if (mode != null) {\n currentFile.mode = detachString(mode);\n }\n }\n // We have to handle these for pure renames because there won't be\n // --- and +++ lines\n if (line.startsWith('rename from ')) {\n currentFile.prevName = detachString(\n line.slice('rename from '.length).trim()\n );\n }\n if (line.startsWith('rename to ')) {\n currentFile.name = detachString(\n line.slice('rename to '.length).trim()\n );\n }\n }\n }\n continue;\n }\n\n // Otherwise, time to start parsing out the hunk\n let currentContent: ContextContent | ChangeContent | undefined;\n let lastLineType: 'context' | 'addition' | 'deletion' | undefined;\n\n // Strip trailing bare newlines (format-patch separators between commits)\n // if needed\n while (\n lines.length > 0 &&\n (lines[lines.length - 1] === '\\n' ||\n lines[lines.length - 1] === '\\r' ||\n lines[lines.length - 1] === '\\r\\n' ||\n lines[lines.length - 1] === '')\n ) {\n lines.pop();\n }\n\n const { additionStart, deletionStart } = fileHeader;\n deletionLineIndex = isPartial ? deletionLineIndex : deletionStart - 1;\n additionLineIndex = isPartial ? additionLineIndex : additionStart - 1;\n\n const hunkData: Hunk = {\n collapsedBefore: 0,\n\n splitLineCount: 0,\n splitLineStart: 0,\n\n unifiedLineCount: 0,\n unifiedLineStart: 0,\n\n additionCount: fileHeader.additionCount,\n additionStart,\n additionLines,\n\n deletionCount: fileHeader.deletionCount,\n deletionStart,\n deletionLines,\n\n deletionLineIndex,\n additionLineIndex,\n\n hunkContent: [],\n hunkContext: maybeDetachOptionalString(fileHeader.hunkContext),\n hunkSpecs: detachString(firstLine),\n\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n // Now we process each line of the hunk\n let parsedAdditionLines = 0;\n let parsedDeletionLines = 0;\n for (let lineIndex = 1; lineIndex < lines.length; lineIndex++) {\n const rawLine = lines[lineIndex];\n if (\n parsedAdditionLines >= hunkData.additionCount &&\n parsedDeletionLines >= hunkData.deletionCount &&\n !rawLine.startsWith('\\\\')\n ) {\n if (\n throwOnError &&\n isHunkBodyLine(rawLine) &&\n !isFormatPatchVersionSeparator(rawLine)\n ) {\n throw Error('parsePatchContent: hunk has more lines than expected');\n }\n break;\n }\n\n const firstChar = rawLine[0];\n // If we can't properly process the line, well, lets just try to salvage\n // things and continue... It's possible an AI generated diff might have\n // some stray blank lines or something in there\n if (\n firstChar !== '+' &&\n firstChar !== '-' &&\n firstChar !== ' ' &&\n firstChar !== '\\\\'\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk line');\n }\n console.error(\n `parseLineType: Invalid firstChar: \"${firstChar}\", full line: \"${rawLine}\"`\n );\n console.error('processFile: invalid rawLine:', rawLine);\n continue;\n }\n\n const type = parseRawLineType(firstChar);\n if (type === 'addition') {\n if (throwOnError && parsedAdditionLines >= hunkData.additionCount) {\n throw Error('parsePatchContent: hunk has too many addition lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n parsedAdditionLines++;\n if (isPartial) {\n currentFile.additionLines.push(line);\n }\n currentContent.additions++;\n additionLines++;\n lastLineType = 'addition';\n } else if (type === 'deletion') {\n if (throwOnError && parsedDeletionLines >= hunkData.deletionCount) {\n throw Error('parsePatchContent: hunk has too many deletion lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n deletionLineIndex++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n }\n currentContent.deletions++;\n deletionLines++;\n lastLineType = 'deletion';\n } else if (type === 'context') {\n if (\n throwOnError &&\n (parsedDeletionLines >= hunkData.deletionCount ||\n parsedAdditionLines >= hunkData.additionCount)\n ) {\n throw Error('parsePatchContent: hunk has too many context lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'context') {\n currentContent = createContentGroup(\n 'context',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n deletionLineIndex++;\n parsedAdditionLines++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n currentFile.additionLines.push(line);\n }\n currentContent.lines++;\n lastLineType = 'context';\n } else if (type === 'metadata' && currentContent != null) {\n if (currentContent.type === 'context') {\n hunkData.noEOFCRAdditions = true;\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'deletion') {\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'addition') {\n hunkData.noEOFCRAdditions = true;\n }\n // If we're dealing with partial content from a diff, we need to strip\n // newlines manually from the content\n if (\n isPartial &&\n (lastLineType === 'addition' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.additionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.additionLines[lastIndex] = cleanLastNewline(\n currentFile.additionLines[lastIndex]\n );\n }\n }\n if (\n isPartial &&\n (lastLineType === 'deletion' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.deletionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.deletionLines[lastIndex] = cleanLastNewline(\n currentFile.deletionLines[lastIndex]\n );\n }\n }\n }\n }\n\n if (\n throwOnError &&\n (parsedAdditionLines !== hunkData.additionCount ||\n parsedDeletionLines !== hunkData.deletionCount)\n ) {\n throw Error('parsePatchContent: hunk line count mismatch');\n }\n\n hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n getHunkSideStartBoundary(hunkData.additionStart, hunkData.additionCount) -\n lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = getHunkSideEndBoundary(\n hunkData.additionStart,\n hunkData.additionCount\n );\n for (const content of hunkData.hunkContent) {\n if (content.type === 'context') {\n hunkData.splitLineCount += content.lines;\n hunkData.unifiedLineCount += content.lines;\n } else {\n hunkData.splitLineCount += Math.max(\n content.additions,\n content.deletions\n );\n hunkData.unifiedLineCount += content.deletions + content.additions;\n }\n }\n hunkData.splitLineStart =\n currentFile.splitLineCount + hunkData.collapsedBefore;\n hunkData.unifiedLineStart =\n currentFile.unifiedLineCount + hunkData.collapsedBefore;\n\n currentFile.splitLineCount +=\n hunkData.collapsedBefore + hunkData.splitLineCount;\n currentFile.unifiedLineCount +=\n hunkData.collapsedBefore + hunkData.unifiedLineCount;\n }\n if (currentFile == null) {\n return undefined;\n }\n if (\n throwOnError &&\n isPartial &&\n !isGitDiff &&\n currentFile.hunks.length === 0\n ) {\n throw Error('parsePatchContent: unified file has no hunks');\n }\n\n // Account for collapsed lines after the final hunk and increment the\n // split/unified counts properly\n if (\n currentFile.hunks.length > 0 &&\n !isPartial &&\n currentFile.additionLines.length > 0 &&\n currentFile.deletionLines.length > 0\n ) {\n const lastHunk = currentFile.hunks[currentFile.hunks.length - 1];\n const lastHunkEnd = getHunkSideEndBoundary(\n lastHunk.additionStart,\n lastHunk.additionCount\n );\n const totalFileLines = currentFile.additionLines.length;\n const collapsedAfter = Math.max(totalFileLines - lastHunkEnd, 0);\n currentFile.splitLineCount += collapsedAfter;\n currentFile.unifiedLineCount += collapsedAfter;\n }\n\n // If this isn't a git diff style patch, then we'll need to sus out some\n // additional metadata manually\n if (!isGitDiff) {\n if (\n currentFile.prevName != null &&\n currentFile.name !== currentFile.prevName\n ) {\n if (currentFile.hunks.length > 0) {\n currentFile.type = 'rename-changed';\n } else {\n currentFile.type = 'rename-pure';\n }\n }\n // Sort of a hack for detecting deleted/added files...\n else if (\n (oldFile == null || oldFile.contents === '') &&\n newFile != null &&\n newFile.contents !== ''\n ) {\n currentFile.type = 'new';\n } else if (\n oldFile != null &&\n oldFile.contents !== '' &&\n (newFile == null || newFile.contents === '')\n ) {\n currentFile.type = 'deleted';\n }\n }\n if (\n currentFile.type !== 'rename-pure' &&\n currentFile.type !== 'rename-changed'\n ) {\n currentFile.prevName = undefined;\n }\n // Pair change-block lines by similarity instead of the patch's positional\n // ordering. Partial diffs work too: their hunk line indexes point into the\n // patch-built line arrays, which hold every line a block references.\n realignChangeContentBySimilarity(currentFile);\n return currentFile;\n}\n\n/**\n * Parses a patch file string into an array of parsed patches.\n *\n * @param data - The raw patch file content (supports multi-commit patches)\n * @param cacheKeyPrefix - Optional prefix for generating cache keys. When provided,\n * each file in the patch will get a cache key in the format `prefix-patchIndex-fileIndex`.\n * This enables caching of rendered diff results in the worker pool.\n */\nexport function parsePatchFiles(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch[] {\n // NOTE(amadeus): This function is pretty forgiving in that it can accept a\n // patch file that includes commit metdata, multiple commits, or not\n const patches: ParsedPatch[] = [];\n const rawPatches = hasCommitMetadataBoundary(data)\n ? data.split(COMMIT_METADATA_SPLIT)\n : [data];\n for (const patch of rawPatches) {\n try {\n patches.push(\n processPatch(\n patch,\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${patches.length}`\n : undefined,\n throwOnError\n )\n );\n } catch (error) {\n if (throwOnError) {\n throw error;\n } else {\n console.error(error);\n }\n }\n }\n return patches;\n}\n\nfunction hasCommitMetadataBoundary(data: string): boolean {\n return data.startsWith('From ') || data.includes('\\nFrom ');\n}\n\nfunction splitFileContents(contents: string): string[] {\n const lines = splitWithNewlines(contents);\n for (let index = 0; index < lines.length; index++) {\n lines[index] = detachString(lines[index]);\n }\n return lines;\n}\n\nfunction splitWithNewlines(contents: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const lines: string[] = [];\n let startIndex = 0;\n for (;;) {\n const newlineIndex = contents.indexOf('\\n', startIndex);\n if (newlineIndex === -1) {\n break;\n }\n\n lines.push(contents.slice(startIndex, newlineIndex + 1));\n startIndex = newlineIndex + 1;\n }\n\n if (startIndex < contents.length) {\n lines.push(contents.slice(startIndex));\n }\n return lines;\n}\n\nfunction splitGitDiffFiles(contents: string): string[] {\n return splitAtLinePrefix(contents, 'diff --git');\n}\n\nfunction splitUnifiedDiffFiles(contents: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const parts: string[] = [];\n let partStartIndex = 0;\n let lineStartIndex = 0;\n let remainingDeletionLines = 0;\n let remainingAdditionLines = 0;\n let hasOpenedUnifiedFile = false;\n\n while (lineStartIndex < contents.length) {\n const nextLineStartIndex = getNextLineStartIndex(contents, lineStartIndex);\n if (remainingDeletionLines <= 0 && remainingAdditionLines <= 0) {\n if (isUnifiedDiffFileHeaderAt(contents, lineStartIndex)) {\n if (lineStartIndex > partStartIndex) {\n parts.push(contents.slice(partStartIndex, lineStartIndex));\n }\n partStartIndex = lineStartIndex;\n hasOpenedUnifiedFile = true;\n lineStartIndex = getNextLineStartIndex(contents, nextLineStartIndex);\n continue;\n }\n\n if (hasOpenedUnifiedFile && contents.startsWith('@@ -', lineStartIndex)) {\n const fileHeader = parseHunkHeader(\n contents.slice(lineStartIndex, nextLineStartIndex)\n );\n if (fileHeader != null) {\n remainingDeletionLines = fileHeader.deletionCount;\n remainingAdditionLines = fileHeader.additionCount;\n }\n }\n lineStartIndex = nextLineStartIndex;\n continue;\n }\n\n const firstChar = contents[lineStartIndex];\n if (firstChar === '\\\\') {\n lineStartIndex = nextLineStartIndex;\n continue;\n }\n\n if (firstChar === ' ') {\n remainingDeletionLines = Math.max(remainingDeletionLines - 1, 0);\n remainingAdditionLines = Math.max(remainingAdditionLines - 1, 0);\n } else if (firstChar === '-') {\n remainingDeletionLines = Math.max(remainingDeletionLines - 1, 0);\n } else if (firstChar === '+') {\n remainingAdditionLines = Math.max(remainingAdditionLines - 1, 0);\n }\n lineStartIndex = nextLineStartIndex;\n }\n\n parts.push(contents.slice(partStartIndex));\n return parts;\n}\n\nfunction startsWithUnifiedDiffFileHeader(contents: string): boolean {\n return isUnifiedDiffFileHeaderAt(contents, 0);\n}\n\nfunction isUnifiedDiffFileHeaderAt(contents: string, lineStartIndex: number) {\n const nextLineStartIndex = getNextLineStartIndex(contents, lineStartIndex);\n return (\n isUnifiedDiffHeaderLineAt(contents, lineStartIndex, '---') &&\n isUnifiedDiffHeaderLineAt(contents, nextLineStartIndex, '+++')\n );\n}\n\nfunction isUnifiedDiffHeaderLineAt(\n contents: string,\n lineStartIndex: number,\n prefix: '---' | '+++'\n): boolean {\n if (!contents.startsWith(prefix, lineStartIndex)) {\n return false;\n }\n\n const separator = contents[lineStartIndex + prefix.length];\n if (separator !== ' ' && separator !== '\\t') {\n return false;\n }\n\n for (\n let index = lineStartIndex + prefix.length + 1;\n index < contents.length;\n index++\n ) {\n const char = contents[index];\n if (char === '\\n' || char === '\\r') {\n break;\n }\n if (char !== ' ' && char !== '\\t') {\n return true;\n }\n }\n return false;\n}\n\nfunction getNextLineStartIndex(\n contents: string,\n lineStartIndex: number\n): number {\n const newlineIndex = contents.indexOf('\\n', lineStartIndex);\n return newlineIndex === -1 ? contents.length : newlineIndex + 1;\n}\n\nfunction isHunkBodyLine(line: string): boolean {\n const firstChar = line[0];\n return firstChar === '+' || firstChar === '-' || firstChar === ' ';\n}\n\nfunction isFormatPatchVersionSeparator(line: string): boolean {\n if (!line.startsWith('--')) {\n return false;\n }\n\n for (let index = 2; index < line.length; index++) {\n const char = line[index];\n if (char !== ' ' && char !== '\\t' && char !== '\\n' && char !== '\\r') {\n return false;\n }\n }\n return true;\n}\n\nfunction parseHunkHeader(line: string): ParsedHunkHeader | undefined {\n if (!line.startsWith('@@ -')) {\n return undefined;\n }\n\n let index = 4;\n const deletionStartResult = readPositiveInteger(line, index);\n if (deletionStartResult == null) {\n return undefined;\n }\n const deletionStart = deletionStartResult.value;\n index = deletionStartResult.endIndex;\n\n let deletionCount = 1;\n if (line[index] === ',') {\n const deletionCountResult = readPositiveInteger(line, index + 1);\n if (deletionCountResult == null) {\n return undefined;\n }\n deletionCount = deletionCountResult.value;\n index = deletionCountResult.endIndex;\n }\n\n if (line[index] !== ' ' || line[index + 1] !== '+') {\n return undefined;\n }\n index += 2;\n\n const additionStartResult = readPositiveInteger(line, index);\n if (additionStartResult == null) {\n return undefined;\n }\n const additionStart = additionStartResult.value;\n index = additionStartResult.endIndex;\n\n let additionCount = 1;\n if (line[index] === ',') {\n const additionCountResult = readPositiveInteger(line, index + 1);\n if (additionCountResult == null) {\n return undefined;\n }\n additionCount = additionCountResult.value;\n index = additionCountResult.endIndex;\n }\n\n if (\n line[index] !== ' ' ||\n line[index + 1] !== '@' ||\n line[index + 2] !== '@'\n ) {\n return undefined;\n }\n\n let hunkContext: string | undefined;\n const contextStartIndex = index + 3;\n if (line[contextStartIndex] === ' ') {\n hunkContext = trimLineEnd(line.slice(contextStartIndex + 1));\n }\n\n return {\n additionCount,\n additionStart,\n deletionCount,\n deletionStart,\n hunkContext,\n };\n}\n\nfunction readPositiveInteger(\n value: string,\n startIndex: number\n): { value: number; endIndex: number } | undefined {\n let index = startIndex;\n let parsedValue = 0;\n for (; index < value.length; index++) {\n const digit = value.charCodeAt(index) - 48;\n if (digit < 0 || digit > 9) {\n break;\n }\n parsedValue = parsedValue * 10 + digit;\n }\n\n if (index === startIndex) {\n return undefined;\n }\n return { value: parsedValue, endIndex: index };\n}\n\nfunction trimLineEnd(value: string): string {\n if (value.endsWith('\\r\\n')) {\n return value.slice(0, -2);\n }\n if (value.endsWith('\\n')) {\n return value.slice(0, -1);\n }\n return value;\n}\n\nfunction isGitDiffPatch(data: string): boolean {\n return data.startsWith('diff --git') || data.includes('\\ndiff --git');\n}\n\nfunction splitAtLinePrefix(contents: string, prefix: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const newlinePrefix = `\\n${prefix}`;\n const firstBoundaryIndex = contents.startsWith(prefix)\n ? 0\n : findLinePrefixIndex(contents, newlinePrefix, 0);\n if (firstBoundaryIndex === -1) {\n return [contents];\n }\n\n const parts: string[] = [];\n if (firstBoundaryIndex > 0) {\n parts.push(contents.slice(0, firstBoundaryIndex));\n }\n\n let startIndex = firstBoundaryIndex;\n for (;;) {\n const nextBoundaryIndex = findLinePrefixIndex(\n contents,\n newlinePrefix,\n startIndex + 1\n );\n if (nextBoundaryIndex === -1) {\n break;\n }\n\n parts.push(contents.slice(startIndex, nextBoundaryIndex));\n startIndex = nextBoundaryIndex;\n }\n parts.push(contents.slice(startIndex));\n return parts;\n}\n\nfunction findLinePrefixIndex(\n contents: string,\n newlinePrefix: string,\n fromIndex: number\n): number {\n const index = contents.indexOf(newlinePrefix, fromIndex);\n return index === -1 ? -1 : index + 1;\n}\n\nfunction maybeDetachOptionalString<T extends string | undefined>(value: T): T {\n return (value == null ? value : detachString(value)) as T;\n}\n\nfunction parseRawLineType(\n firstChar: string | undefined\n): Exclude<HunkLineType, 'expanded'> {\n return firstChar === ' '\n ? 'context'\n : firstChar === '\\\\'\n ? 'metadata'\n : firstChar === '+'\n ? 'addition'\n : 'deletion';\n}\n\nfunction getParsedLineContent(rawLine: string): string {\n const processedLine = rawLine.slice(1);\n return detachString(processedLine === '' ? '\\n' : processedLine);\n}\n\nfunction createContentGroup(\n type: 'change',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent;\nfunction createContentGroup(\n type: 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ContextContent;\nfunction createContentGroup(\n type: 'change' | 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent | ContextContent {\n if (type === 'change') {\n return {\n type: 'change',\n additions: 0,\n deletions: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n }\n return {\n type: 'context',\n lines: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parsePatchFiles.js","names":[],"sources":["../../src/utils/parsePatchFiles.ts"],"sourcesContent":["import {\n ALTERNATE_FILE_NAMES_GIT,\n COMMIT_METADATA_SPLIT,\n FILENAME_HEADER_REGEX,\n FILENAME_HEADER_REGEX_GIT,\n GIT_DIFF_FILE_BREAK_REGEX,\n INDEX_LINE_METADATA,\n} from '../constants';\nimport type {\n ChangeContent,\n ContextContent,\n FileContents,\n FileDiffMetadata,\n Hunk,\n HunkLineType,\n ParsedPatch,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { composeCacheKey } from './composeCacheKey';\nimport { detachString, releaseStringDetachBuffer } from './detachString';\nimport {\n getHunkSideEndBoundary,\n getHunkSideStartBoundary,\n} from './getHunkSideBoundaries';\nimport { realignChangeContentBySimilarity } from './realignChangeContent';\n\ninterface ParsedHunkHeader {\n additionCount: number;\n additionStart: number;\n deletionCount: number;\n deletionStart: number;\n hunkContext?: string;\n}\n\nexport function processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError?: boolean\n): ParsedPatch {\n try {\n return _processPatch(data, cacheKeyPrefix, throwOnError);\n } finally {\n releaseStringDetachBuffer();\n }\n}\n\nfunction _processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false,\n patchIndex?: number\n): ParsedPatch {\n const isGitDiff = isGitDiffPatch(data);\n const rawFiles = isGitDiff\n ? splitGitDiffFiles(data)\n : splitUnifiedDiffFiles(data);\n let patchMetadata: string | undefined;\n const files: FileDiffMetadata[] = [];\n for (const fileOrPatchMetadata of rawFiles) {\n if (isGitDiff && !GIT_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {\n if (patchMetadata == null) {\n patchMetadata = detachString(fileOrPatchMetadata);\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n // If we get in here, it's most likely the introductory metadata from the\n // patch, or something is fucked with the diff format\n continue;\n } else if (\n !isGitDiff &&\n !startsWithUnifiedDiffFileHeader(fileOrPatchMetadata)\n ) {\n if (patchMetadata == null) {\n patchMetadata = detachString(fileOrPatchMetadata);\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n continue;\n }\n const currentFile = _processFile(fileOrPatchMetadata, {\n cacheKey:\n cacheKeyPrefix != null\n ? patchIndex == null\n ? composeCacheKey(\n 'patch-file',\n cacheKeyPrefix,\n String(files.length)\n )\n : composeCacheKey(\n 'patch-file',\n cacheKeyPrefix,\n String(patchIndex),\n String(files.length)\n )\n : undefined,\n isGitDiff,\n throwOnError,\n });\n if (currentFile != null) {\n files.push(currentFile);\n }\n }\n return { patchMetadata, files };\n}\n\ninterface ProcessFileOptions {\n cacheKey?: string;\n isGitDiff?: boolean;\n oldFile?: FileContents;\n newFile?: FileContents;\n throwOnError?: boolean;\n}\n\nexport function processFile(\n fileDiffString: string,\n options?: ProcessFileOptions\n): FileDiffMetadata | undefined {\n try {\n return _processFile(fileDiffString, options);\n } finally {\n releaseStringDetachBuffer();\n }\n}\n\nfunction _processFile(\n fileDiffString: string,\n {\n cacheKey,\n isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(fileDiffString),\n oldFile,\n newFile,\n throwOnError = false,\n }: ProcessFileOptions = {}\n): FileDiffMetadata | undefined {\n let lastHunkEnd = 0;\n const hunks = splitAtLinePrefix(fileDiffString, '@@ ');\n let currentFile: FileDiffMetadata | undefined;\n const isPartial = oldFile == null || newFile == null;\n let deletionLineIndex = 0;\n let additionLineIndex = 0;\n for (const hunk of hunks) {\n const lines = splitWithNewlines(hunk);\n const firstLine = lines[0];\n if (firstLine == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk');\n } else {\n console.error('parsePatchContent: invalid hunk', hunk);\n }\n continue;\n }\n const fileHeader = parseHunkHeader(firstLine);\n let additionLines = 0;\n let deletionLines = 0;\n // Setup currentFile, this should be the first iteration of our hunks, and\n // technically not a hunk\n if (fileHeader == null || currentFile == null) {\n if (currentFile != null) {\n if (throwOnError) {\n throw Error('parsePatchContent: Invalid hunk');\n } else {\n console.error('parsePatchContent: Invalid hunk', hunk);\n }\n continue;\n }\n currentFile = {\n name: '',\n type: 'change',\n hunks: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n isPartial,\n additionLines:\n !isPartial && oldFile != null && newFile != null\n ? splitFileContents(newFile.contents)\n : [],\n deletionLines:\n !isPartial && oldFile != null && newFile != null\n ? splitFileContents(oldFile.contents)\n : [],\n cacheKey: maybeDetachOptionalString(cacheKey),\n };\n // If either file is technically empty, then we should empty the\n // arrays respectively\n if (currentFile.additionLines.length === 1 && newFile?.contents === '') {\n currentFile.additionLines.length = 0;\n }\n if (currentFile.deletionLines.length === 1 && oldFile?.contents === '') {\n currentFile.deletionLines.length = 0;\n }\n\n for (const line of lines) {\n if (line.startsWith('diff --git')) {\n const filenameMatch = line.trim().match(ALTERNATE_FILE_NAMES_GIT);\n const prevName = filenameMatch?.[1] ?? filenameMatch?.[2];\n const name = filenameMatch?.[3] ?? filenameMatch?.[4];\n if (prevName == null || name == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid git diff header');\n } else {\n console.error('parsePatchContent: invalid git diff header', line);\n }\n continue;\n }\n currentFile.name = detachString(name.trim());\n if (prevName !== name) {\n currentFile.prevName = detachString(prevName.trim());\n }\n continue;\n }\n\n const filenameMatch =\n line.startsWith('---') || line.startsWith('+++')\n ? line.match(\n isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX\n )\n : null;\n if (filenameMatch != null) {\n const [, type, fileName] = filenameMatch;\n if (type === '---' && fileName !== '/dev/null') {\n const detachedFileName = detachString(fileName.trim());\n currentFile.prevName = detachedFileName;\n currentFile.name = detachedFileName;\n } else if (type === '+++' && fileName !== '/dev/null') {\n currentFile.name = detachString(fileName.trim());\n }\n }\n // Git diffs have a bunch of additional metadata we can pull from\n else if (isGitDiff) {\n if (line.startsWith('new mode ')) {\n currentFile.mode = detachString(\n line.slice('new mode'.length).trim()\n );\n }\n if (line.startsWith('old mode ')) {\n currentFile.prevMode = detachString(\n line.slice('old mode'.length).trim()\n );\n }\n if (line.startsWith('new file mode')) {\n currentFile.type = 'new';\n currentFile.mode = detachString(\n line.slice('new file mode'.length).trim()\n );\n }\n if (line.startsWith('deleted file mode')) {\n currentFile.type = 'deleted';\n currentFile.mode = detachString(\n line.slice('deleted file mode'.length).trim()\n );\n }\n if (line.startsWith('similarity index')) {\n if (line.startsWith('similarity index 100%')) {\n currentFile.type = 'rename-pure';\n } else {\n currentFile.type = 'rename-changed';\n }\n }\n if (line.startsWith('index ')) {\n const [, prevObjectId, newObjectId, mode] =\n line.trim().match(INDEX_LINE_METADATA) ?? [];\n if (prevObjectId != null) {\n currentFile.prevObjectId = detachString(prevObjectId);\n }\n if (newObjectId != null) {\n currentFile.newObjectId = detachString(newObjectId);\n }\n if (mode != null) {\n currentFile.mode = detachString(mode);\n }\n }\n // We have to handle these for pure renames because there won't be\n // --- and +++ lines\n if (line.startsWith('rename from ')) {\n currentFile.prevName = detachString(\n line.slice('rename from '.length).trim()\n );\n }\n if (line.startsWith('rename to ')) {\n currentFile.name = detachString(\n line.slice('rename to '.length).trim()\n );\n }\n }\n }\n continue;\n }\n\n // Otherwise, time to start parsing out the hunk\n let currentContent: ContextContent | ChangeContent | undefined;\n let lastLineType: 'context' | 'addition' | 'deletion' | undefined;\n\n // Strip trailing bare newlines (format-patch separators between commits)\n // if needed\n while (\n lines.length > 0 &&\n (lines[lines.length - 1] === '\\n' ||\n lines[lines.length - 1] === '\\r' ||\n lines[lines.length - 1] === '\\r\\n' ||\n lines[lines.length - 1] === '')\n ) {\n lines.pop();\n }\n\n const { additionStart, deletionStart } = fileHeader;\n deletionLineIndex = isPartial ? deletionLineIndex : deletionStart - 1;\n additionLineIndex = isPartial ? additionLineIndex : additionStart - 1;\n\n const hunkData: Hunk = {\n collapsedBefore: 0,\n\n splitLineCount: 0,\n splitLineStart: 0,\n\n unifiedLineCount: 0,\n unifiedLineStart: 0,\n\n additionCount: fileHeader.additionCount,\n additionStart,\n additionLines,\n\n deletionCount: fileHeader.deletionCount,\n deletionStart,\n deletionLines,\n\n deletionLineIndex,\n additionLineIndex,\n\n hunkContent: [],\n hunkContext: maybeDetachOptionalString(fileHeader.hunkContext),\n hunkSpecs: detachString(firstLine),\n\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n // Now we process each line of the hunk\n let parsedAdditionLines = 0;\n let parsedDeletionLines = 0;\n for (let lineIndex = 1; lineIndex < lines.length; lineIndex++) {\n const rawLine = lines[lineIndex];\n if (\n parsedAdditionLines >= hunkData.additionCount &&\n parsedDeletionLines >= hunkData.deletionCount &&\n !rawLine.startsWith('\\\\')\n ) {\n const isUnexpectedBodyLine =\n isHunkBodyLine(rawLine) && !isFormatPatchVersionSeparator(rawLine);\n if (!isUnexpectedBodyLine) {\n break;\n }\n if (throwOnError) {\n throw Error('parsePatchContent: hunk has more lines than expected');\n }\n }\n\n const firstChar = rawLine[0];\n // If we can't properly process the line, well, lets just try to salvage\n // things and continue... It's possible an AI generated diff might have\n // some stray blank lines or something in there\n if (\n firstChar !== '+' &&\n firstChar !== '-' &&\n firstChar !== ' ' &&\n firstChar !== '\\\\'\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk line');\n }\n console.error(\n `parseLineType: Invalid firstChar: \"${firstChar}\", full line: \"${rawLine}\"`\n );\n console.error('processFile: invalid rawLine:', rawLine);\n continue;\n }\n\n const type = parseRawLineType(firstChar);\n if (type === 'addition') {\n if (throwOnError && parsedAdditionLines >= hunkData.additionCount) {\n throw Error('parsePatchContent: hunk has too many addition lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n parsedAdditionLines++;\n if (isPartial) {\n currentFile.additionLines.push(line);\n }\n currentContent.additions++;\n additionLines++;\n lastLineType = 'addition';\n } else if (type === 'deletion') {\n if (throwOnError && parsedDeletionLines >= hunkData.deletionCount) {\n throw Error('parsePatchContent: hunk has too many deletion lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n deletionLineIndex++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n }\n currentContent.deletions++;\n deletionLines++;\n lastLineType = 'deletion';\n } else if (type === 'context') {\n if (\n throwOnError &&\n (parsedDeletionLines >= hunkData.deletionCount ||\n parsedAdditionLines >= hunkData.additionCount)\n ) {\n throw Error('parsePatchContent: hunk has too many context lines');\n }\n const line = getParsedLineContent(rawLine);\n if (currentContent == null || currentContent.type !== 'context') {\n currentContent = createContentGroup(\n 'context',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n deletionLineIndex++;\n parsedAdditionLines++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n currentFile.additionLines.push(line);\n }\n currentContent.lines++;\n lastLineType = 'context';\n } else if (type === 'metadata' && currentContent != null) {\n if (currentContent.type === 'context') {\n hunkData.noEOFCRAdditions = true;\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'deletion') {\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'addition') {\n hunkData.noEOFCRAdditions = true;\n }\n // If we're dealing with partial content from a diff, we need to strip\n // newlines manually from the content\n if (\n isPartial &&\n (lastLineType === 'addition' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.additionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.additionLines[lastIndex] = cleanLastNewline(\n currentFile.additionLines[lastIndex]\n );\n }\n }\n if (\n isPartial &&\n (lastLineType === 'deletion' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.deletionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.deletionLines[lastIndex] = cleanLastNewline(\n currentFile.deletionLines[lastIndex]\n );\n }\n }\n }\n }\n\n if (\n parsedAdditionLines !== hunkData.additionCount ||\n parsedDeletionLines !== hunkData.deletionCount\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: hunk line count mismatch');\n }\n console.error(\n `parsePatchContent: hunk line count mismatch: \"${firstLine.trimEnd()}\", declared old/new ${hunkData.deletionCount}/${hunkData.additionCount}, parsed old/new ${parsedDeletionLines}/${parsedAdditionLines}`\n );\n // Re-encode each original boundary using the repaired count. Zero-count\n // ranges use the boundary itself as their start; positive ranges use +1.\n const repairedAdditionStart =\n getHunkSideStartBoundary(\n hunkData.additionStart,\n hunkData.additionCount\n ) + (parsedAdditionLines === 0 ? 0 : 1);\n const repairedDeletionStart =\n getHunkSideStartBoundary(\n hunkData.deletionStart,\n hunkData.deletionCount\n ) + (parsedDeletionLines === 0 ? 0 : 1);\n\n // Hydrated hunks index into full-file line arrays, so their indexes must\n // move with repaired starts. Partial hunks index patch-built arrays.\n if (!isPartial) {\n const additionStartDelta =\n repairedAdditionStart - hunkData.additionStart;\n const deletionStartDelta =\n repairedDeletionStart - hunkData.deletionStart;\n hunkData.additionLineIndex += additionStartDelta;\n hunkData.deletionLineIndex += deletionStartDelta;\n for (const content of hunkData.hunkContent) {\n content.additionLineIndex += additionStartDelta;\n content.deletionLineIndex += deletionStartDelta;\n }\n }\n\n hunkData.additionStart = repairedAdditionStart;\n hunkData.deletionStart = repairedDeletionStart;\n hunkData.additionCount = parsedAdditionLines;\n hunkData.deletionCount = parsedDeletionLines;\n }\n\n hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n getHunkSideStartBoundary(hunkData.additionStart, hunkData.additionCount) -\n lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = getHunkSideEndBoundary(\n hunkData.additionStart,\n hunkData.additionCount\n );\n for (const content of hunkData.hunkContent) {\n if (content.type === 'context') {\n hunkData.splitLineCount += content.lines;\n hunkData.unifiedLineCount += content.lines;\n } else {\n hunkData.splitLineCount += Math.max(\n content.additions,\n content.deletions\n );\n hunkData.unifiedLineCount += content.deletions + content.additions;\n }\n }\n hunkData.splitLineStart =\n currentFile.splitLineCount + hunkData.collapsedBefore;\n hunkData.unifiedLineStart =\n currentFile.unifiedLineCount + hunkData.collapsedBefore;\n\n currentFile.splitLineCount +=\n hunkData.collapsedBefore + hunkData.splitLineCount;\n currentFile.unifiedLineCount +=\n hunkData.collapsedBefore + hunkData.unifiedLineCount;\n }\n if (currentFile == null) {\n return undefined;\n }\n if (\n throwOnError &&\n isPartial &&\n !isGitDiff &&\n currentFile.hunks.length === 0\n ) {\n throw Error('parsePatchContent: unified file has no hunks');\n }\n\n // Account for collapsed lines after the final hunk and increment the\n // split/unified counts properly\n if (\n currentFile.hunks.length > 0 &&\n !isPartial &&\n currentFile.additionLines.length > 0 &&\n currentFile.deletionLines.length > 0\n ) {\n const lastHunk = currentFile.hunks[currentFile.hunks.length - 1];\n const lastHunkEnd = getHunkSideEndBoundary(\n lastHunk.additionStart,\n lastHunk.additionCount\n );\n const totalFileLines = currentFile.additionLines.length;\n const collapsedAfter = Math.max(totalFileLines - lastHunkEnd, 0);\n currentFile.splitLineCount += collapsedAfter;\n currentFile.unifiedLineCount += collapsedAfter;\n }\n\n // If this isn't a git diff style patch, then we'll need to sus out some\n // additional metadata manually\n if (!isGitDiff) {\n if (\n currentFile.prevName != null &&\n currentFile.name !== currentFile.prevName\n ) {\n if (currentFile.hunks.length > 0) {\n currentFile.type = 'rename-changed';\n } else {\n currentFile.type = 'rename-pure';\n }\n }\n // Sort of a hack for detecting deleted/added files...\n else if (\n (oldFile == null || oldFile.contents === '') &&\n newFile != null &&\n newFile.contents !== ''\n ) {\n currentFile.type = 'new';\n } else if (\n oldFile != null &&\n oldFile.contents !== '' &&\n (newFile == null || newFile.contents === '')\n ) {\n currentFile.type = 'deleted';\n }\n }\n if (\n currentFile.type !== 'rename-pure' &&\n currentFile.type !== 'rename-changed'\n ) {\n currentFile.prevName = undefined;\n }\n // Pair change-block lines by similarity instead of the patch's positional\n // ordering. Partial diffs work too: their hunk line indexes point into the\n // patch-built line arrays, which hold every line a block references.\n realignChangeContentBySimilarity(currentFile);\n return currentFile;\n}\n\n/**\n * Parses a patch file string into an array of parsed patches.\n *\n * @param data - The raw patch file content (supports multi-commit patches)\n * @param cacheKeyPrefix - Optional prefix for collision-safe cache keys derived\n * from the prefix, patch index, and file index. This enables caching of\n * rendered diff results in the worker pool.\n * @param throwOnError - When true, invalid data throws. When false, invalid data\n * is reported with `console.error` and the parser attempts to recover when\n * possible. Recovery is best-effort and does not guarantee valid output.\n */\nexport function parsePatchFiles(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch[] {\n // NOTE(amadeus): This function is pretty forgiving in that it can accept a\n // patch file that includes commit metdata, multiple commits, or not\n const patches: ParsedPatch[] = [];\n const rawPatches = hasCommitMetadataBoundary(data)\n ? data.split(COMMIT_METADATA_SPLIT)\n : [data];\n for (const patch of rawPatches) {\n try {\n patches.push(\n _processPatch(\n patch,\n cacheKeyPrefix,\n throwOnError,\n cacheKeyPrefix != null ? patches.length : undefined\n )\n );\n } catch (error) {\n if (throwOnError) {\n throw error;\n } else {\n console.error(error);\n }\n } finally {\n releaseStringDetachBuffer();\n }\n }\n return patches;\n}\n\nfunction hasCommitMetadataBoundary(data: string): boolean {\n return data.startsWith('From ') || data.includes('\\nFrom ');\n}\n\nfunction splitFileContents(contents: string): string[] {\n const lines = splitWithNewlines(contents);\n for (let index = 0; index < lines.length; index++) {\n lines[index] = detachString(lines[index]);\n }\n return lines;\n}\n\nfunction splitWithNewlines(contents: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const lines: string[] = [];\n let startIndex = 0;\n for (;;) {\n const newlineIndex = contents.indexOf('\\n', startIndex);\n if (newlineIndex === -1) {\n break;\n }\n\n lines.push(contents.slice(startIndex, newlineIndex + 1));\n startIndex = newlineIndex + 1;\n }\n\n if (startIndex < contents.length) {\n lines.push(contents.slice(startIndex));\n }\n return lines;\n}\n\nfunction splitGitDiffFiles(contents: string): string[] {\n return splitAtLinePrefix(contents, 'diff --git');\n}\n\nfunction splitUnifiedDiffFiles(contents: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const parts: string[] = [];\n let partStartIndex = 0;\n let lineStartIndex = 0;\n let remainingDeletionLines = 0;\n let remainingAdditionLines = 0;\n let hasOpenedUnifiedFile = false;\n\n while (lineStartIndex < contents.length) {\n const nextLineStartIndex = getNextLineStartIndex(contents, lineStartIndex);\n if (remainingDeletionLines <= 0 && remainingAdditionLines <= 0) {\n if (isUnifiedDiffFileHeaderAt(contents, lineStartIndex)) {\n if (lineStartIndex > partStartIndex) {\n parts.push(contents.slice(partStartIndex, lineStartIndex));\n }\n partStartIndex = lineStartIndex;\n hasOpenedUnifiedFile = true;\n lineStartIndex = getNextLineStartIndex(contents, nextLineStartIndex);\n continue;\n }\n\n if (hasOpenedUnifiedFile && contents.startsWith('@@ -', lineStartIndex)) {\n const fileHeader = parseHunkHeader(\n contents.slice(lineStartIndex, nextLineStartIndex)\n );\n if (fileHeader != null) {\n remainingDeletionLines = fileHeader.deletionCount;\n remainingAdditionLines = fileHeader.additionCount;\n }\n }\n lineStartIndex = nextLineStartIndex;\n continue;\n }\n\n const firstChar = contents[lineStartIndex];\n if (firstChar === '\\\\') {\n lineStartIndex = nextLineStartIndex;\n continue;\n }\n\n if (firstChar === ' ') {\n remainingDeletionLines = Math.max(remainingDeletionLines - 1, 0);\n remainingAdditionLines = Math.max(remainingAdditionLines - 1, 0);\n } else if (firstChar === '-') {\n remainingDeletionLines = Math.max(remainingDeletionLines - 1, 0);\n } else if (firstChar === '+') {\n remainingAdditionLines = Math.max(remainingAdditionLines - 1, 0);\n }\n lineStartIndex = nextLineStartIndex;\n }\n\n parts.push(contents.slice(partStartIndex));\n return parts;\n}\n\nfunction startsWithUnifiedDiffFileHeader(contents: string): boolean {\n return isUnifiedDiffFileHeaderAt(contents, 0);\n}\n\nfunction isUnifiedDiffFileHeaderAt(contents: string, lineStartIndex: number) {\n const nextLineStartIndex = getNextLineStartIndex(contents, lineStartIndex);\n return (\n isUnifiedDiffHeaderLineAt(contents, lineStartIndex, '---') &&\n isUnifiedDiffHeaderLineAt(contents, nextLineStartIndex, '+++')\n );\n}\n\nfunction isUnifiedDiffHeaderLineAt(\n contents: string,\n lineStartIndex: number,\n prefix: '---' | '+++'\n): boolean {\n if (!contents.startsWith(prefix, lineStartIndex)) {\n return false;\n }\n\n const separator = contents[lineStartIndex + prefix.length];\n if (separator !== ' ' && separator !== '\\t') {\n return false;\n }\n\n for (\n let index = lineStartIndex + prefix.length + 1;\n index < contents.length;\n index++\n ) {\n const char = contents[index];\n if (char === '\\n' || char === '\\r') {\n break;\n }\n if (char !== ' ' && char !== '\\t') {\n return true;\n }\n }\n return false;\n}\n\nfunction getNextLineStartIndex(\n contents: string,\n lineStartIndex: number\n): number {\n const newlineIndex = contents.indexOf('\\n', lineStartIndex);\n return newlineIndex === -1 ? contents.length : newlineIndex + 1;\n}\n\nfunction isHunkBodyLine(line: string): boolean {\n const firstChar = line[0];\n return firstChar === '+' || firstChar === '-' || firstChar === ' ';\n}\n\nfunction isFormatPatchVersionSeparator(line: string): boolean {\n if (!line.startsWith('--')) {\n return false;\n }\n\n for (let index = 2; index < line.length; index++) {\n const char = line[index];\n if (char !== ' ' && char !== '\\t' && char !== '\\n' && char !== '\\r') {\n return false;\n }\n }\n return true;\n}\n\nfunction parseHunkHeader(line: string): ParsedHunkHeader | undefined {\n if (!line.startsWith('@@ -')) {\n return undefined;\n }\n\n let index = 4;\n const deletionStartResult = readPositiveInteger(line, index);\n if (deletionStartResult == null) {\n return undefined;\n }\n const deletionStart = deletionStartResult.value;\n index = deletionStartResult.endIndex;\n\n let deletionCount = 1;\n if (line[index] === ',') {\n const deletionCountResult = readPositiveInteger(line, index + 1);\n if (deletionCountResult == null) {\n return undefined;\n }\n deletionCount = deletionCountResult.value;\n index = deletionCountResult.endIndex;\n }\n\n if (line[index] !== ' ' || line[index + 1] !== '+') {\n return undefined;\n }\n index += 2;\n\n const additionStartResult = readPositiveInteger(line, index);\n if (additionStartResult == null) {\n return undefined;\n }\n const additionStart = additionStartResult.value;\n index = additionStartResult.endIndex;\n\n let additionCount = 1;\n if (line[index] === ',') {\n const additionCountResult = readPositiveInteger(line, index + 1);\n if (additionCountResult == null) {\n return undefined;\n }\n additionCount = additionCountResult.value;\n index = additionCountResult.endIndex;\n }\n\n if (\n line[index] !== ' ' ||\n line[index + 1] !== '@' ||\n line[index + 2] !== '@'\n ) {\n return undefined;\n }\n\n let hunkContext: string | undefined;\n const contextStartIndex = index + 3;\n if (line[contextStartIndex] === ' ') {\n hunkContext = trimLineEnd(line.slice(contextStartIndex + 1));\n }\n\n return {\n additionCount,\n additionStart,\n deletionCount,\n deletionStart,\n hunkContext,\n };\n}\n\nfunction readPositiveInteger(\n value: string,\n startIndex: number\n): { value: number; endIndex: number } | undefined {\n let index = startIndex;\n let parsedValue = 0;\n for (; index < value.length; index++) {\n const digit = value.charCodeAt(index) - 48;\n if (digit < 0 || digit > 9) {\n break;\n }\n parsedValue = parsedValue * 10 + digit;\n }\n\n if (index === startIndex) {\n return undefined;\n }\n return { value: parsedValue, endIndex: index };\n}\n\nfunction trimLineEnd(value: string): string {\n if (value.endsWith('\\r\\n')) {\n return value.slice(0, -2);\n }\n if (value.endsWith('\\n')) {\n return value.slice(0, -1);\n }\n return value;\n}\n\nfunction isGitDiffPatch(data: string): boolean {\n return data.startsWith('diff --git') || data.includes('\\ndiff --git');\n}\n\nfunction splitAtLinePrefix(contents: string, prefix: string): string[] {\n if (contents.length === 0) {\n return [''];\n }\n\n const newlinePrefix = `\\n${prefix}`;\n const firstBoundaryIndex = contents.startsWith(prefix)\n ? 0\n : findLinePrefixIndex(contents, newlinePrefix, 0);\n if (firstBoundaryIndex === -1) {\n return [contents];\n }\n\n const parts: string[] = [];\n if (firstBoundaryIndex > 0) {\n parts.push(contents.slice(0, firstBoundaryIndex));\n }\n\n let startIndex = firstBoundaryIndex;\n for (;;) {\n const nextBoundaryIndex = findLinePrefixIndex(\n contents,\n newlinePrefix,\n startIndex + 1\n );\n if (nextBoundaryIndex === -1) {\n break;\n }\n\n parts.push(contents.slice(startIndex, nextBoundaryIndex));\n startIndex = nextBoundaryIndex;\n }\n parts.push(contents.slice(startIndex));\n return parts;\n}\n\nfunction findLinePrefixIndex(\n contents: string,\n newlinePrefix: string,\n fromIndex: number\n): number {\n const index = contents.indexOf(newlinePrefix, fromIndex);\n return index === -1 ? -1 : index + 1;\n}\n\nfunction maybeDetachOptionalString<T extends string | undefined>(value: T): T {\n return (value == null ? value : detachString(value)) as T;\n}\n\nfunction parseRawLineType(\n firstChar: string | undefined\n): Exclude<HunkLineType, 'expanded'> {\n return firstChar === ' '\n ? 'context'\n : firstChar === '\\\\'\n ? 'metadata'\n : firstChar === '+'\n ? 'addition'\n : 'deletion';\n}\n\nfunction getParsedLineContent(rawLine: string): string {\n const processedLine = rawLine.slice(1);\n return detachString(processedLine === '' ? '\\n' : processedLine);\n}\n\nfunction createContentGroup(\n type: 'change',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent;\nfunction createContentGroup(\n type: 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ContextContent;\nfunction createContentGroup(\n type: 'change' | 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent | ContextContent {\n if (type === 'change') {\n return {\n type: 'change',\n additions: 0,\n deletions: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n }\n return {\n type: 'context',\n lines: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderDiffWithHighlighter.d.ts","names":[],"sources":["../../src/utils/renderDiffWithHighlighter.ts"],"mappings":";;;iBAqCgB,yBAAA,CACd,IAAA,EAAM,gBAAA,EACN,WAAA,EAAa,gBAAA,EACb,OAAA,EAAS,iBAAA;EAEP,cAAA;EACA,YAAA;EACA,UAAA;EACA,aAAA;EACA;AAAA,IACC,yBAAA,GACF,gBAAA"}
1
+ {"version":3,"file":"renderDiffWithHighlighter.d.ts","names":[],"sources":["../../src/utils/renderDiffWithHighlighter.ts"],"mappings":";;;iBAmCgB,yBAAA,CACd,IAAA,EAAM,gBAAA,EACN,WAAA,EAAa,gBAAA,EACb,OAAA,EAAS,iBAAA;EAEP,cAAA;EACA,YAAA;EACA,UAAA;EACA,aAAA;EACA;AAAA,IACC,yBAAA,GACF,gBAAA"}
@@ -1,11 +1,12 @@
1
- import { DEFAULT_THEMES } from "../constants.js";
2
- import { cleanLastNewline } from "./cleanLastNewline.js";
1
+ import "../constants.js";
3
2
  import { getFiletypeFromFileName } from "./getFiletypeFromFileName.js";
3
+ import { appendItems } from "./appendItems.js";
4
4
  import { createTransformerWithState } from "./createTransformerWithState.js";
5
5
  import { formatCSSVariablePrefix } from "./formatCSSVariablePrefix.js";
6
6
  import { getHighlighterThemeStyles } from "./getHighlighterThemeStyles.js";
7
7
  import { getLineNodes } from "./getLineNodes.js";
8
8
  import { iterateOverDiff } from "./iterateOverDiff.js";
9
+ import { cleanLastNewline } from "./cleanLastNewline.js";
9
10
  import { createDiffSpanDecoration, pushOrJoinSpan } from "./parseDiffDecorations.js";
10
11
  import { diffChars, diffWordsWithSpace } from "diff";
11
12
  //#region src/utils/renderDiffWithHighlighter.ts
@@ -121,9 +122,9 @@ function renderDiffWithHighlighter(diff, highlighter, options, { forcePlainText,
121
122
  continue;
122
123
  }
123
124
  if (bucket.deletionSegments.length > 0) for (const seg of bucket.deletionSegments) for (let i = 0; i < seg.count; i++) code.deletionLines[seg.targetIndex + i] = deletionLines[seg.originalOffset + i];
124
- else code.deletionLines.push(...deletionLines);
125
+ else appendItems(code.deletionLines, deletionLines);
125
126
  if (bucket.additionSegments.length > 0) for (const seg of bucket.additionSegments) for (let i = 0; i < seg.count; i++) code.additionLines[seg.targetIndex + i] = additionLines[seg.originalOffset + i];
126
- else code.additionLines.push(...additionLines);
127
+ else appendItems(code.additionLines, additionLines);
127
128
  }
128
129
  return {
129
130
  code,
@@ -216,7 +217,7 @@ function createBucket() {
216
217
  additionSegments: []
217
218
  };
218
219
  }
219
- function renderTwoFiles({ deletionFile, additionFile, deletionInfo, additionInfo, highlighter, deletionDecorations, additionDecorations, languageOverride, options: { theme: themeOrThemes = DEFAULT_THEMES, ...options } }) {
220
+ function renderTwoFiles({ deletionFile, additionFile, deletionInfo, additionInfo, highlighter, deletionDecorations, additionDecorations, languageOverride, options: { theme: themeOrThemes, ...options } }) {
220
221
  const deletionLang = languageOverride ?? getFiletypeFromFileName(deletionFile.name);
221
222
  const additionLang = languageOverride ?? getFiletypeFromFileName(additionFile.name);
222
223
  const { state, transformers } = createTransformerWithState(options.useTokenTransformer);
@@ -1 +1 @@
1
- {"version":3,"file":"renderDiffWithHighlighter.js","names":[],"sources":["../../src/utils/renderDiffWithHighlighter.ts"],"sourcesContent":["import { diffChars, diffWordsWithSpace } from 'diff';\n\nimport {\n DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n DEFAULT_THEMES,\n} from '../constants';\nimport type {\n CodeToHastOptions,\n DecorationItem,\n DiffsHighlighter,\n DiffsThemeNames,\n FileContents,\n FileDiffMetadata,\n ForceDiffPlainTextOptions,\n LineDiffTypes,\n LineInfo,\n RenderDiffFilesResult,\n RenderDiffOptions,\n SupportedLanguages,\n ThemedDiffResult,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { createTransformerWithState } from './createTransformerWithState';\nimport { formatCSSVariablePrefix } from './formatCSSVariablePrefix';\nimport { getFiletypeFromFileName } from './getFiletypeFromFileName';\nimport { getHighlighterThemeStyles } from './getHighlighterThemeStyles';\nimport { getLineNodes } from './getLineNodes';\nimport { iterateOverDiff } from './iterateOverDiff';\nimport {\n createDiffSpanDecoration,\n pushOrJoinSpan,\n} from './parseDiffDecorations';\n\nconst DEFAULT_PLAIN_TEXT_OPTIONS: ForceDiffPlainTextOptions = {\n forcePlainText: false,\n};\n\nexport function renderDiffWithHighlighter(\n diff: FileDiffMetadata,\n highlighter: DiffsHighlighter,\n options: RenderDiffOptions,\n {\n forcePlainText,\n startingLine,\n totalLines,\n expandedHunks,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n }: ForceDiffPlainTextOptions = DEFAULT_PLAIN_TEXT_OPTIONS\n): ThemedDiffResult {\n if (forcePlainText) {\n startingLine ??= 0;\n totalLines ??= Infinity;\n } else {\n // If we aren't forcing plain text, then we intentionally do not support\n // ranges for highlighting as that could break the syntax highlighting, we\n // we override any values that may have been passed in. Maybe one day we\n // warn about this?\n startingLine = 0;\n totalLines = Infinity;\n }\n const isWindowedHighlight = startingLine > 0 || totalLines < Infinity;\n const baseThemeType =\n typeof options.theme === 'string'\n ? highlighter.getTheme(options.theme).type\n : undefined;\n const themeStyles = getHighlighterThemeStyles({\n theme: options.theme,\n highlighter,\n });\n\n // If we have a large file and we are rendering the WHOLE plain diff ast,\n // then we should remove the lineDiffType to make sure things render quickly.\n // For highlighted ASTs or windowed highlights, we should just inherit the\n // setting\n const lineDiffType =\n forcePlainText &&\n !isWindowedHighlight &&\n (diff.unifiedLineCount > 1000 || diff.splitLineCount > 1000)\n ? 'none'\n : options.lineDiffType;\n\n const code: RenderDiffFilesResult = {\n deletionLines: [],\n additionLines: [],\n };\n\n const { maxLineDiffLength } = options;\n const shouldGroupAll = !forcePlainText && !diff.isPartial;\n const expandedHunksForIteration = forcePlainText ? expandedHunks : undefined;\n const buckets = new Map<number, RenderBucket>();\n function getBucketForHunk(hunkIndex: number) {\n const index = shouldGroupAll ? 0 : hunkIndex;\n const bucket = buckets.get(index) ?? createBucket();\n buckets.set(index, bucket);\n return bucket;\n }\n\n function appendContent(\n lineContent: string,\n lineIndex: number,\n segments: HighlightSegment[],\n contentWrapper: FakeArrayType\n ) {\n if (isWindowedHighlight) {\n let segment = segments.at(-1);\n if (\n segment == null ||\n segment.targetIndex + segment.count !== lineIndex\n ) {\n segment = {\n targetIndex: lineIndex,\n originalOffset: contentWrapper.length,\n count: 0,\n };\n segments.push(segment);\n }\n segment.count++;\n }\n contentWrapper.push(lineContent);\n }\n\n iterateOverDiff({\n diff,\n diffStyle: 'both',\n startingLine,\n totalLines,\n expandedHunks: isWindowedHighlight ? expandedHunksForIteration : true,\n collapsedContextThreshold,\n callback: ({ hunkIndex, additionLine, deletionLine, type }) => {\n const bucket = getBucketForHunk(hunkIndex);\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n\n if (type === 'change' && additionLine != null && deletionLine != null) {\n computeLineDiffDecorations({\n additionLine: diff.additionLines[additionLine.lineIndex],\n deletionLine: diff.deletionLines[deletionLine.lineIndex],\n deletionLineIndex: bucket.deletionContent.length,\n additionLineIndex: bucket.additionContent.length,\n deletionDecorations: bucket.deletionDecorations,\n additionDecorations: bucket.additionDecorations,\n lineDiffType,\n maxLineDiffLength,\n });\n }\n\n if (deletionLine != null) {\n appendContent(\n diff.deletionLines[deletionLine.lineIndex],\n deletionLine.lineIndex,\n bucket.deletionSegments,\n bucket.deletionContent\n );\n bucket.deletionInfo.push({\n type: type === 'change' ? 'change-deletion' : type,\n lineNumber: deletionLine.lineNumber,\n altLineNumber:\n type === 'change'\n ? undefined\n : (additionLine.lineNumber ?? undefined),\n lineIndex: `${deletionLine.unifiedLineIndex},${splitLineIndex}`,\n });\n }\n\n if (additionLine != null) {\n appendContent(\n diff.additionLines[additionLine.lineIndex],\n additionLine.lineIndex,\n bucket.additionSegments,\n bucket.additionContent\n );\n bucket.additionInfo.push({\n type: type === 'change' ? 'change-addition' : type,\n lineNumber: additionLine.lineNumber,\n altLineNumber:\n type === 'change'\n ? undefined\n : (deletionLine.lineNumber ?? undefined),\n lineIndex: `${additionLine.unifiedLineIndex},${splitLineIndex}`,\n });\n }\n },\n });\n\n for (const bucket of buckets.values()) {\n if (\n bucket.deletionContent.length === 0 &&\n bucket.additionContent.length === 0\n ) {\n continue;\n }\n\n const deletionFile = {\n name: diff.prevName ?? diff.name,\n contents: bucket.deletionContent.value,\n };\n const additionFile = {\n name: diff.name,\n contents: bucket.additionContent.value,\n };\n const { deletionLines, additionLines } = renderTwoFiles({\n deletionFile,\n deletionInfo: bucket.deletionInfo,\n deletionDecorations: bucket.deletionDecorations,\n\n additionFile,\n additionInfo: bucket.additionInfo,\n additionDecorations: bucket.additionDecorations,\n\n highlighter,\n options,\n languageOverride: forcePlainText ? 'text' : diff.lang,\n });\n\n if (shouldGroupAll) {\n code.deletionLines = deletionLines;\n code.additionLines = additionLines;\n continue;\n }\n\n if (bucket.deletionSegments.length > 0) {\n for (const seg of bucket.deletionSegments) {\n for (let i = 0; i < seg.count; i++) {\n code.deletionLines[seg.targetIndex + i] =\n deletionLines[seg.originalOffset + i];\n }\n }\n } else {\n code.deletionLines.push(...deletionLines);\n }\n if (bucket.additionSegments.length > 0) {\n for (const seg of bucket.additionSegments) {\n for (let i = 0; i < seg.count; i++) {\n code.additionLines[seg.targetIndex + i] =\n additionLines[seg.originalOffset + i];\n }\n }\n } else {\n code.additionLines.push(...additionLines);\n }\n }\n\n return { code, themeStyles, baseThemeType };\n}\n\ninterface ProcessLineDiffProps {\n deletionLine: string | undefined;\n additionLine: string | undefined;\n deletionLineIndex: number;\n additionLineIndex: number;\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n lineDiffType: LineDiffTypes;\n maxLineDiffLength: number;\n}\n\nfunction computeLineDiffDecorations({\n deletionLine,\n additionLine,\n deletionLineIndex,\n additionLineIndex,\n deletionDecorations,\n additionDecorations,\n lineDiffType,\n maxLineDiffLength,\n}: ProcessLineDiffProps) {\n if (deletionLine == null || additionLine == null || lineDiffType === 'none') {\n return;\n }\n deletionLine = cleanLastNewline(deletionLine);\n additionLine = cleanLastNewline(additionLine);\n // If we have really long lines, we probably shouldn't compute diffs on them.\n if (\n deletionLine.length > maxLineDiffLength ||\n additionLine.length > maxLineDiffLength\n ) {\n return;\n }\n // NOTE(amadeus): Because we visually trim trailing newlines when rendering,\n // we also gotta make sure the diff parsing doesn't include the newline\n // character that could be there...\n const lineDiff =\n lineDiffType === 'char'\n ? diffChars(deletionLine, additionLine)\n : diffWordsWithSpace(deletionLine, additionLine);\n const deletionSpans: [0 | 1, string][] = [];\n const additionSpans: [0 | 1, string][] = [];\n const enableJoin = lineDiffType === 'word-alt';\n const lastItem = lineDiff.at(-1);\n for (const item of lineDiff) {\n const isLastItem = item === lastItem;\n if (!item.added && !item.removed) {\n pushOrJoinSpan({\n item,\n arr: deletionSpans,\n enableJoin,\n isNeutral: true,\n isLastItem,\n });\n pushOrJoinSpan({\n item,\n arr: additionSpans,\n enableJoin,\n isNeutral: true,\n isLastItem,\n });\n } else if (item.removed) {\n pushOrJoinSpan({ item, arr: deletionSpans, enableJoin, isLastItem });\n } else {\n pushOrJoinSpan({ item, arr: additionSpans, enableJoin, isLastItem });\n }\n }\n let spanIndex = 0;\n for (const span of deletionSpans) {\n if (span[0] === 1) {\n deletionDecorations.push(\n createDiffSpanDecoration({\n line: deletionLineIndex,\n spanStart: spanIndex,\n spanLength: span[1].length,\n })\n );\n }\n spanIndex += span[1].length;\n }\n spanIndex = 0;\n for (const span of additionSpans) {\n if (span[0] === 1) {\n additionDecorations.push(\n createDiffSpanDecoration({\n line: additionLineIndex,\n spanStart: spanIndex,\n spanLength: span[1].length,\n })\n );\n }\n spanIndex += span[1].length;\n }\n}\n\ninterface HighlightSegment {\n // The where the highlighted region starts\n originalOffset: number;\n // Where to place the highlighted line in RenderDiffFilesResult\n targetIndex: number;\n // Number of highlighted lines\n count: number;\n}\n\ninterface FakeArrayType {\n push(value: string): void;\n value: string;\n length: number;\n}\n\ninterface RenderBucket {\n deletionContent: FakeArrayType;\n additionContent: FakeArrayType;\n deletionInfo: (LineInfo | undefined)[];\n additionInfo: (LineInfo | undefined)[];\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n deletionSegments: HighlightSegment[];\n additionSegments: HighlightSegment[];\n}\n\nfunction createBucket(): RenderBucket {\n return {\n deletionContent: {\n push(value: string) {\n this.value += value;\n this.length++;\n },\n value: '',\n length: 0,\n },\n additionContent: {\n push(value: string) {\n this.value += value;\n this.length++;\n },\n value: '',\n length: 0,\n },\n deletionInfo: [],\n additionInfo: [],\n deletionDecorations: [],\n additionDecorations: [],\n deletionSegments: [],\n additionSegments: [],\n };\n}\n\ninterface RenderTwoFilesProps {\n deletionFile: FileContents;\n additionFile: FileContents;\n deletionInfo: (LineInfo | undefined)[];\n additionInfo: (LineInfo | undefined)[];\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n options: RenderDiffOptions;\n highlighter: DiffsHighlighter;\n languageOverride: SupportedLanguages | undefined;\n}\n\nfunction renderTwoFiles({\n deletionFile,\n additionFile,\n deletionInfo,\n additionInfo,\n highlighter,\n deletionDecorations,\n additionDecorations,\n languageOverride,\n options: { theme: themeOrThemes = DEFAULT_THEMES, ...options },\n}: RenderTwoFilesProps): RenderDiffFilesResult {\n const deletionLang =\n languageOverride ?? getFiletypeFromFileName(deletionFile.name);\n const additionLang =\n languageOverride ?? getFiletypeFromFileName(additionFile.name);\n const { state, transformers } = createTransformerWithState(\n options.useTokenTransformer\n );\n // tokenizeTimeLimit: 0 — never trade silently-wrong token colors for\n // latency; see renderFileWithHighlighter for the full rationale.\n const hastConfig: CodeToHastOptions<DiffsThemeNames> = (() => {\n return typeof themeOrThemes === 'string'\n ? {\n ...options,\n // language will be overwritten for each highlight\n lang: 'text',\n theme: themeOrThemes,\n transformers,\n decorations: undefined,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n }\n : {\n ...options,\n // language will be overwritten for each highlight\n lang: 'text',\n themes: themeOrThemes,\n transformers,\n decorations: undefined,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n };\n })();\n\n const deletionLines = (() => {\n if (deletionFile.contents === '') {\n return [];\n }\n hastConfig.lang = deletionLang;\n state.lineInfo = deletionInfo;\n hastConfig.decorations = deletionDecorations;\n return getLineNodes(\n highlighter.codeToHast(\n cleanLastNewline(deletionFile.contents),\n hastConfig\n )\n );\n })();\n const additionLines = (() => {\n if (additionFile.contents === '') {\n return [];\n }\n hastConfig.lang = additionLang;\n hastConfig.decorations = additionDecorations;\n state.lineInfo = additionInfo;\n return getLineNodes(\n highlighter.codeToHast(\n cleanLastNewline(additionFile.contents),\n hastConfig\n )\n );\n })();\n\n return { deletionLines, additionLines };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"renderDiffWithHighlighter.js","names":[],"sources":["../../src/utils/renderDiffWithHighlighter.ts"],"sourcesContent":["import { diffChars, diffWordsWithSpace } from 'diff';\n\nimport { DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } from '../constants';\nimport type {\n CodeToHastOptions,\n DecorationItem,\n DiffsHighlighter,\n DiffsThemeNames,\n FileContents,\n FileDiffMetadata,\n ForceDiffPlainTextOptions,\n LineDiffTypes,\n LineInfo,\n RenderDiffFilesResult,\n RenderDiffOptions,\n SupportedLanguages,\n ThemedDiffResult,\n} from '../types';\nimport { appendItems } from './appendItems';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { createTransformerWithState } from './createTransformerWithState';\nimport { formatCSSVariablePrefix } from './formatCSSVariablePrefix';\nimport { getFiletypeFromFileName } from './getFiletypeFromFileName';\nimport { getHighlighterThemeStyles } from './getHighlighterThemeStyles';\nimport { getLineNodes } from './getLineNodes';\nimport { iterateOverDiff } from './iterateOverDiff';\nimport {\n createDiffSpanDecoration,\n pushOrJoinSpan,\n} from './parseDiffDecorations';\n\nconst DEFAULT_PLAIN_TEXT_OPTIONS: ForceDiffPlainTextOptions = {\n forcePlainText: false,\n};\n\nexport function renderDiffWithHighlighter(\n diff: FileDiffMetadata,\n highlighter: DiffsHighlighter,\n options: RenderDiffOptions,\n {\n forcePlainText,\n startingLine,\n totalLines,\n expandedHunks,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n }: ForceDiffPlainTextOptions = DEFAULT_PLAIN_TEXT_OPTIONS\n): ThemedDiffResult {\n if (forcePlainText) {\n startingLine ??= 0;\n totalLines ??= Infinity;\n } else {\n // If we aren't forcing plain text, then we intentionally do not support\n // ranges for highlighting as that could break the syntax highlighting, we\n // we override any values that may have been passed in. Maybe one day we\n // warn about this?\n startingLine = 0;\n totalLines = Infinity;\n }\n const isWindowedHighlight = startingLine > 0 || totalLines < Infinity;\n const baseThemeType =\n typeof options.theme === 'string'\n ? highlighter.getTheme(options.theme).type\n : undefined;\n const themeStyles = getHighlighterThemeStyles({\n theme: options.theme,\n highlighter,\n });\n\n // If we have a large file and we are rendering the WHOLE plain diff ast,\n // then we should remove the lineDiffType to make sure things render quickly.\n // For highlighted ASTs or windowed highlights, we should just inherit the\n // setting\n const lineDiffType =\n forcePlainText &&\n !isWindowedHighlight &&\n (diff.unifiedLineCount > 1000 || diff.splitLineCount > 1000)\n ? 'none'\n : options.lineDiffType;\n\n const code: RenderDiffFilesResult = {\n deletionLines: [],\n additionLines: [],\n };\n\n const { maxLineDiffLength } = options;\n const shouldGroupAll = !forcePlainText && !diff.isPartial;\n const expandedHunksForIteration = forcePlainText ? expandedHunks : undefined;\n const buckets = new Map<number, RenderBucket>();\n function getBucketForHunk(hunkIndex: number) {\n const index = shouldGroupAll ? 0 : hunkIndex;\n const bucket = buckets.get(index) ?? createBucket();\n buckets.set(index, bucket);\n return bucket;\n }\n\n function appendContent(\n lineContent: string,\n lineIndex: number,\n segments: HighlightSegment[],\n contentWrapper: FakeArrayType\n ) {\n if (isWindowedHighlight) {\n let segment = segments.at(-1);\n if (\n segment == null ||\n segment.targetIndex + segment.count !== lineIndex\n ) {\n segment = {\n targetIndex: lineIndex,\n originalOffset: contentWrapper.length,\n count: 0,\n };\n segments.push(segment);\n }\n segment.count++;\n }\n contentWrapper.push(lineContent);\n }\n\n iterateOverDiff({\n diff,\n diffStyle: 'both',\n startingLine,\n totalLines,\n expandedHunks: isWindowedHighlight ? expandedHunksForIteration : true,\n collapsedContextThreshold,\n callback: ({ hunkIndex, additionLine, deletionLine, type }) => {\n const bucket = getBucketForHunk(hunkIndex);\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n\n if (type === 'change' && additionLine != null && deletionLine != null) {\n computeLineDiffDecorations({\n additionLine: diff.additionLines[additionLine.lineIndex],\n deletionLine: diff.deletionLines[deletionLine.lineIndex],\n deletionLineIndex: bucket.deletionContent.length,\n additionLineIndex: bucket.additionContent.length,\n deletionDecorations: bucket.deletionDecorations,\n additionDecorations: bucket.additionDecorations,\n lineDiffType,\n maxLineDiffLength,\n });\n }\n\n if (deletionLine != null) {\n appendContent(\n diff.deletionLines[deletionLine.lineIndex],\n deletionLine.lineIndex,\n bucket.deletionSegments,\n bucket.deletionContent\n );\n bucket.deletionInfo.push({\n type: type === 'change' ? 'change-deletion' : type,\n lineNumber: deletionLine.lineNumber,\n altLineNumber:\n type === 'change'\n ? undefined\n : (additionLine.lineNumber ?? undefined),\n lineIndex: `${deletionLine.unifiedLineIndex},${splitLineIndex}`,\n });\n }\n\n if (additionLine != null) {\n appendContent(\n diff.additionLines[additionLine.lineIndex],\n additionLine.lineIndex,\n bucket.additionSegments,\n bucket.additionContent\n );\n bucket.additionInfo.push({\n type: type === 'change' ? 'change-addition' : type,\n lineNumber: additionLine.lineNumber,\n altLineNumber:\n type === 'change'\n ? undefined\n : (deletionLine.lineNumber ?? undefined),\n lineIndex: `${additionLine.unifiedLineIndex},${splitLineIndex}`,\n });\n }\n },\n });\n\n for (const bucket of buckets.values()) {\n if (\n bucket.deletionContent.length === 0 &&\n bucket.additionContent.length === 0\n ) {\n continue;\n }\n\n const deletionFile = {\n name: diff.prevName ?? diff.name,\n contents: bucket.deletionContent.value,\n };\n const additionFile = {\n name: diff.name,\n contents: bucket.additionContent.value,\n };\n const { deletionLines, additionLines } = renderTwoFiles({\n deletionFile,\n deletionInfo: bucket.deletionInfo,\n deletionDecorations: bucket.deletionDecorations,\n\n additionFile,\n additionInfo: bucket.additionInfo,\n additionDecorations: bucket.additionDecorations,\n\n highlighter,\n options,\n languageOverride: forcePlainText ? 'text' : diff.lang,\n });\n\n if (shouldGroupAll) {\n code.deletionLines = deletionLines;\n code.additionLines = additionLines;\n continue;\n }\n\n if (bucket.deletionSegments.length > 0) {\n for (const seg of bucket.deletionSegments) {\n for (let i = 0; i < seg.count; i++) {\n code.deletionLines[seg.targetIndex + i] =\n deletionLines[seg.originalOffset + i];\n }\n }\n } else {\n appendItems(code.deletionLines, deletionLines);\n }\n if (bucket.additionSegments.length > 0) {\n for (const seg of bucket.additionSegments) {\n for (let i = 0; i < seg.count; i++) {\n code.additionLines[seg.targetIndex + i] =\n additionLines[seg.originalOffset + i];\n }\n }\n } else {\n appendItems(code.additionLines, additionLines);\n }\n }\n\n return { code, themeStyles, baseThemeType };\n}\n\ninterface ProcessLineDiffProps {\n deletionLine: string | undefined;\n additionLine: string | undefined;\n deletionLineIndex: number;\n additionLineIndex: number;\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n lineDiffType: LineDiffTypes;\n maxLineDiffLength: number;\n}\n\nfunction computeLineDiffDecorations({\n deletionLine,\n additionLine,\n deletionLineIndex,\n additionLineIndex,\n deletionDecorations,\n additionDecorations,\n lineDiffType,\n maxLineDiffLength,\n}: ProcessLineDiffProps) {\n if (deletionLine == null || additionLine == null || lineDiffType === 'none') {\n return;\n }\n deletionLine = cleanLastNewline(deletionLine);\n additionLine = cleanLastNewline(additionLine);\n // If we have really long lines, we probably shouldn't compute diffs on them.\n if (\n deletionLine.length > maxLineDiffLength ||\n additionLine.length > maxLineDiffLength\n ) {\n return;\n }\n // NOTE(amadeus): Because we visually trim trailing newlines when rendering,\n // we also gotta make sure the diff parsing doesn't include the newline\n // character that could be there...\n const lineDiff =\n lineDiffType === 'char'\n ? diffChars(deletionLine, additionLine)\n : diffWordsWithSpace(deletionLine, additionLine);\n const deletionSpans: [0 | 1, string][] = [];\n const additionSpans: [0 | 1, string][] = [];\n const enableJoin = lineDiffType === 'word-alt';\n const lastItem = lineDiff.at(-1);\n for (const item of lineDiff) {\n const isLastItem = item === lastItem;\n if (!item.added && !item.removed) {\n pushOrJoinSpan({\n item,\n arr: deletionSpans,\n enableJoin,\n isNeutral: true,\n isLastItem,\n });\n pushOrJoinSpan({\n item,\n arr: additionSpans,\n enableJoin,\n isNeutral: true,\n isLastItem,\n });\n } else if (item.removed) {\n pushOrJoinSpan({ item, arr: deletionSpans, enableJoin, isLastItem });\n } else {\n pushOrJoinSpan({ item, arr: additionSpans, enableJoin, isLastItem });\n }\n }\n let spanIndex = 0;\n for (const span of deletionSpans) {\n if (span[0] === 1) {\n deletionDecorations.push(\n createDiffSpanDecoration({\n line: deletionLineIndex,\n spanStart: spanIndex,\n spanLength: span[1].length,\n })\n );\n }\n spanIndex += span[1].length;\n }\n spanIndex = 0;\n for (const span of additionSpans) {\n if (span[0] === 1) {\n additionDecorations.push(\n createDiffSpanDecoration({\n line: additionLineIndex,\n spanStart: spanIndex,\n spanLength: span[1].length,\n })\n );\n }\n spanIndex += span[1].length;\n }\n}\n\ninterface HighlightSegment {\n // The where the highlighted region starts\n originalOffset: number;\n // Where to place the highlighted line in RenderDiffFilesResult\n targetIndex: number;\n // Number of highlighted lines\n count: number;\n}\n\ninterface FakeArrayType {\n push(value: string): void;\n value: string;\n length: number;\n}\n\ninterface RenderBucket {\n deletionContent: FakeArrayType;\n additionContent: FakeArrayType;\n deletionInfo: (LineInfo | undefined)[];\n additionInfo: (LineInfo | undefined)[];\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n deletionSegments: HighlightSegment[];\n additionSegments: HighlightSegment[];\n}\n\nfunction createBucket(): RenderBucket {\n return {\n deletionContent: {\n push(value: string) {\n this.value += value;\n this.length++;\n },\n value: '',\n length: 0,\n },\n additionContent: {\n push(value: string) {\n this.value += value;\n this.length++;\n },\n value: '',\n length: 0,\n },\n deletionInfo: [],\n additionInfo: [],\n deletionDecorations: [],\n additionDecorations: [],\n deletionSegments: [],\n additionSegments: [],\n };\n}\n\ninterface RenderTwoFilesProps {\n deletionFile: FileContents;\n additionFile: FileContents;\n deletionInfo: (LineInfo | undefined)[];\n additionInfo: (LineInfo | undefined)[];\n deletionDecorations: DecorationItem[];\n additionDecorations: DecorationItem[];\n options: RenderDiffOptions;\n highlighter: DiffsHighlighter;\n languageOverride: SupportedLanguages | undefined;\n}\n\nfunction renderTwoFiles({\n deletionFile,\n additionFile,\n deletionInfo,\n additionInfo,\n highlighter,\n deletionDecorations,\n additionDecorations,\n languageOverride,\n options: { theme: themeOrThemes, ...options },\n}: RenderTwoFilesProps): RenderDiffFilesResult {\n const deletionLang =\n languageOverride ?? getFiletypeFromFileName(deletionFile.name);\n const additionLang =\n languageOverride ?? getFiletypeFromFileName(additionFile.name);\n const { state, transformers } = createTransformerWithState(\n options.useTokenTransformer\n );\n // tokenizeTimeLimit: 0 — never trade silently-wrong token colors for\n // latency; see renderFileWithHighlighter for the full rationale.\n const hastConfig: CodeToHastOptions<DiffsThemeNames> = (() => {\n return typeof themeOrThemes === 'string'\n ? {\n ...options,\n // language will be overwritten for each highlight\n lang: 'text',\n theme: themeOrThemes,\n transformers,\n decorations: undefined,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n }\n : {\n ...options,\n // language will be overwritten for each highlight\n lang: 'text',\n themes: themeOrThemes,\n transformers,\n decorations: undefined,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n };\n })();\n\n const deletionLines = (() => {\n if (deletionFile.contents === '') {\n return [];\n }\n hastConfig.lang = deletionLang;\n state.lineInfo = deletionInfo;\n hastConfig.decorations = deletionDecorations;\n return getLineNodes(\n highlighter.codeToHast(\n cleanLastNewline(deletionFile.contents),\n hastConfig\n )\n );\n })();\n const additionLines = (() => {\n if (additionFile.contents === '') {\n return [];\n }\n hastConfig.lang = additionLang;\n hastConfig.decorations = additionDecorations;\n state.lineInfo = additionInfo;\n return getLineNodes(\n highlighter.codeToHast(\n cleanLastNewline(additionFile.contents),\n hastConfig\n )\n );\n })();\n\n return { deletionLines, additionLines };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderFileWithHighlighter.d.ts","names":[],"sources":["../../src/utils/renderFileWithHighlighter.ts"],"mappings":";;;iBAqBgB,yBAAA,CACd,IAAA,EAAM,YAAA,EACN,WAAA,EAAa,gBAAA;EAEX,KAAA;EACA,qBAAA;EACA;AAAA,GACC,iBAAA;EAED,cAAA;EACA,YAAA;EACA,UAAA;EACA;AAAA,IACC,yBAAA,GACF,gBAAA"}
1
+ {"version":3,"file":"renderFileWithHighlighter.d.ts","names":[],"sources":["../../src/utils/renderFileWithHighlighter.ts"],"mappings":";;;iBAqBgB,yBAAA,CACd,IAAA,EAAM,YAAA,EACN,WAAA,EAAa,gBAAA;EACX,KAAA;EAAO,qBAAA;EAAuB;AAAA,GAAuB,iBAAA;EAErD,cAAA;EACA,YAAA;EACA,UAAA;EACA;AAAA,IACC,yBAAA,GACF,gBAAA"}
@@ -1,13 +1,13 @@
1
- import { DEFAULT_THEMES } from "../constants.js";
2
1
  import { linesFromFileContents } from "./computeFileOffsets.js";
3
2
  import { getFiletypeFromFileName } from "./getFiletypeFromFileName.js";
3
+ import { appendItems } from "./appendItems.js";
4
4
  import { createTransformerWithState } from "./createTransformerWithState.js";
5
5
  import { formatCSSVariablePrefix } from "./formatCSSVariablePrefix.js";
6
6
  import { getHighlighterThemeStyles } from "./getHighlighterThemeStyles.js";
7
7
  import { getLineNodes } from "./getLineNodes.js";
8
8
  //#region src/utils/renderFileWithHighlighter.ts
9
9
  const DEFAULT_PLAIN_TEXT_OPTIONS = { forcePlainText: false };
10
- function renderFileWithHighlighter(file, highlighter, { theme = DEFAULT_THEMES, tokenizeMaxLineLength, useTokenTransformer }, { forcePlainText, startingLine, totalLines, lines } = DEFAULT_PLAIN_TEXT_OPTIONS) {
10
+ function renderFileWithHighlighter(file, highlighter, { theme, tokenizeMaxLineLength, useTokenTransformer }, { forcePlainText, startingLine, totalLines, lines } = DEFAULT_PLAIN_TEXT_OPTIONS) {
11
11
  if (forcePlainText) {
12
12
  startingLine ??= 0;
13
13
  totalLines ??= Infinity;
@@ -48,15 +48,18 @@ function renderFileWithHighlighter(file, highlighter, { theme = DEFAULT_THEMES,
48
48
  tokenizeTimeLimit: 0
49
49
  };
50
50
  })();
51
- const highlightedLines = getLineNodes(highlighter.codeToHast(isWindowedHighlight ? extractWindowedFileContent(lines ?? linesFromFileContents(file.contents), startingLine, totalLines) : file.contents, hastConfig));
51
+ const highlightedLines = getLineNodes(highlighter.codeToHast(normalizeHighlightLineEndings(isWindowedHighlight ? extractWindowedFileContent(lines ?? linesFromFileContents(file.contents), startingLine, totalLines) : file.contents), hastConfig));
52
52
  const code = isWindowedHighlight ? new Array(startingLine) : highlightedLines;
53
- if (isWindowedHighlight) code.push(...highlightedLines);
53
+ if (isWindowedHighlight) appendItems(code, highlightedLines);
54
54
  return {
55
55
  code,
56
56
  themeStyles,
57
57
  baseThemeType
58
58
  };
59
59
  }
60
+ function normalizeHighlightLineEndings(contents) {
61
+ return contents.replace(/\r(?!\n)/g, "\n");
62
+ }
60
63
  function extractWindowedFileContent(lines, startingLine, totalLines) {
61
64
  if (lines.length === 0) return "";
62
65
  const endLine = Math.min(startingLine + totalLines, lines.length);
@@ -1 +1 @@
1
- {"version":3,"file":"renderFileWithHighlighter.js","names":[],"sources":["../../src/utils/renderFileWithHighlighter.ts"],"sourcesContent":["import { DEFAULT_THEMES } from '../constants';\nimport type {\n CodeToHastOptions,\n DiffsHighlighter,\n DiffsThemeNames,\n FileContents,\n ForceFilePlainTextOptions,\n RenderFileOptions,\n ThemedFileResult,\n} from '../types';\nimport { linesFromFileContents } from './computeFileOffsets';\nimport { createTransformerWithState } from './createTransformerWithState';\nimport { formatCSSVariablePrefix } from './formatCSSVariablePrefix';\nimport { getFiletypeFromFileName } from './getFiletypeFromFileName';\nimport { getHighlighterThemeStyles } from './getHighlighterThemeStyles';\nimport { getLineNodes } from './getLineNodes';\n\nconst DEFAULT_PLAIN_TEXT_OPTIONS: ForceFilePlainTextOptions = {\n forcePlainText: false,\n};\n\nexport function renderFileWithHighlighter(\n file: FileContents,\n highlighter: DiffsHighlighter,\n {\n theme = DEFAULT_THEMES,\n tokenizeMaxLineLength,\n useTokenTransformer,\n }: RenderFileOptions,\n {\n forcePlainText,\n startingLine,\n totalLines,\n lines,\n }: ForceFilePlainTextOptions = DEFAULT_PLAIN_TEXT_OPTIONS\n): ThemedFileResult {\n if (forcePlainText) {\n startingLine ??= 0;\n totalLines ??= Infinity;\n } else {\n // If we aren't forcing plain text, then we intentionally do not support\n // ranges for highlighting as that could break the syntax highlighting, we\n // we override any values that may have been passed in. Maybe one day we\n // warn about this?\n startingLine = 0;\n totalLines = Infinity;\n }\n const isWindowedHighlight = startingLine > 0 || totalLines < Infinity;\n const { state, transformers } =\n createTransformerWithState(useTokenTransformer);\n const lang = forcePlainText\n ? 'text'\n : (file.lang ?? getFiletypeFromFileName(file.name));\n const baseThemeType =\n typeof theme === 'string' ? highlighter.getTheme(theme).type : undefined;\n const themeStyles = getHighlighterThemeStyles({\n theme,\n highlighter,\n });\n state.lineInfo = (shikiLineNumber: number) => ({\n type: 'context',\n lineIndex: shikiLineNumber - 1 + startingLine,\n lineNumber: shikiLineNumber + startingLine,\n });\n // tokenizeTimeLimit: 0 disables shiki's silent 500ms-per-line tokenization\n // abort. When it trips (slow devices, cold JS-regex-engine compile), the\n // rest of the line collapses to the enclosing scope's color — and since\n // dual-theme rendering tokenizes per theme, the first (dark) pass can smear\n // while the warm second (light) pass stays correct. Pathological content is\n // already guarded by tokenizeMaxLineLength, which renders long lines plain.\n const hastConfig: CodeToHastOptions<DiffsThemeNames> = (() => {\n if (typeof theme === 'string') {\n return {\n lang,\n theme,\n transformers,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeMaxLineLength,\n tokenizeTimeLimit: 0,\n };\n }\n return {\n lang,\n themes: theme,\n transformers,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeMaxLineLength,\n tokenizeTimeLimit: 0,\n };\n })();\n const highlightedLines = getLineNodes(\n highlighter.codeToHast(\n isWindowedHighlight\n ? extractWindowedFileContent(\n lines ?? linesFromFileContents(file.contents),\n startingLine,\n totalLines\n )\n : file.contents,\n hastConfig\n )\n );\n\n // Create sparse array for windowed rendering\n const code = isWindowedHighlight ? new Array(startingLine) : highlightedLines;\n if (isWindowedHighlight) {\n code.push(...highlightedLines);\n }\n\n return { code, themeStyles, baseThemeType };\n}\n\nfunction extractWindowedFileContent(\n lines: string[],\n startingLine: number,\n totalLines: number\n): string {\n if (lines.length === 0) {\n return '';\n }\n const endLine = Math.min(startingLine + totalLines, lines.length);\n return lines.slice(startingLine, endLine).join('');\n}\n"],"mappings":";;;;;;;;AAiBA,MAAM,6BAAwD,EAC5D,gBAAgB,MAClB;AAEA,SAAgB,0BACd,MACA,aACA,EACE,QAAQ,gBACR,uBACA,uBAEF,EACE,gBACA,cACA,YACA,UAC6B,4BACb;CAClB,IAAI,gBAAgB;EAClB,iBAAiB;EACjB,eAAe;CACjB,OAAO;EAKL,eAAe;EACf,aAAa;CACf;CACA,MAAM,sBAAsB,eAAe,KAAK,aAAa;CAC7D,MAAM,EAAE,OAAO,iBACb,2BAA2B,mBAAmB;CAChD,MAAM,OAAO,iBACT,SACC,KAAK,QAAQ,wBAAwB,KAAK,IAAI;CACnD,MAAM,gBACJ,OAAO,UAAU,WAAW,YAAY,SAAS,KAAK,CAAC,CAAC,OAAO,KAAA;CACjE,MAAM,cAAc,0BAA0B;EAC5C;EACA;CACF,CAAC;CACD,MAAM,YAAY,qBAA6B;EAC7C,MAAM;EACN,WAAW,kBAAkB,IAAI;EACjC,YAAY,kBAAkB;CAChC;CAOA,MAAM,oBAAwD;EAC5D,IAAI,OAAO,UAAU,UACnB,OAAO;GACL;GACA;GACA;GACA,cAAc;GACd,mBAAmB,wBAAwB,OAAO;GAClD;GACA,mBAAmB;EACrB;EAEF,OAAO;GACL;GACA,QAAQ;GACR;GACA,cAAc;GACd,mBAAmB,wBAAwB,OAAO;GAClD;GACA,mBAAmB;EACrB;CACF,EAAA,CAAG;CACH,MAAM,mBAAmB,aACvB,YAAY,WACV,sBACI,2BACE,SAAS,sBAAsB,KAAK,QAAQ,GAC5C,cACA,UACF,IACA,KAAK,UACT,UACF,CACF;CAGA,MAAM,OAAO,sBAAsB,IAAI,MAAM,YAAY,IAAI;CAC7D,IAAI,qBACF,KAAK,KAAK,GAAG,gBAAgB;CAG/B,OAAO;EAAE;EAAM;EAAa;CAAc;AAC5C;AAEA,SAAS,2BACP,OACA,cACA,YACQ;CACR,IAAI,MAAM,WAAW,GACnB,OAAO;CAET,MAAM,UAAU,KAAK,IAAI,eAAe,YAAY,MAAM,MAAM;CAChE,OAAO,MAAM,MAAM,cAAc,OAAO,CAAC,CAAC,KAAK,EAAE;AACnD"}
1
+ {"version":3,"file":"renderFileWithHighlighter.js","names":[],"sources":["../../src/utils/renderFileWithHighlighter.ts"],"sourcesContent":["import type {\n CodeToHastOptions,\n DiffsHighlighter,\n DiffsThemeNames,\n FileContents,\n ForceFilePlainTextOptions,\n RenderFileOptions,\n ThemedFileResult,\n} from '../types';\nimport { appendItems } from './appendItems';\nimport { linesFromFileContents } from './computeFileOffsets';\nimport { createTransformerWithState } from './createTransformerWithState';\nimport { formatCSSVariablePrefix } from './formatCSSVariablePrefix';\nimport { getFiletypeFromFileName } from './getFiletypeFromFileName';\nimport { getHighlighterThemeStyles } from './getHighlighterThemeStyles';\nimport { getLineNodes } from './getLineNodes';\n\nconst DEFAULT_PLAIN_TEXT_OPTIONS: ForceFilePlainTextOptions = {\n forcePlainText: false,\n};\n\nexport function renderFileWithHighlighter(\n file: FileContents,\n highlighter: DiffsHighlighter,\n { theme, tokenizeMaxLineLength, useTokenTransformer }: RenderFileOptions,\n {\n forcePlainText,\n startingLine,\n totalLines,\n lines,\n }: ForceFilePlainTextOptions = DEFAULT_PLAIN_TEXT_OPTIONS\n): ThemedFileResult {\n if (forcePlainText) {\n startingLine ??= 0;\n totalLines ??= Infinity;\n } else {\n // If we aren't forcing plain text, then we intentionally do not support\n // ranges for highlighting as that could break the syntax highlighting, we\n // we override any values that may have been passed in. Maybe one day we\n // warn about this?\n startingLine = 0;\n totalLines = Infinity;\n }\n const isWindowedHighlight = startingLine > 0 || totalLines < Infinity;\n const { state, transformers } =\n createTransformerWithState(useTokenTransformer);\n const lang = forcePlainText\n ? 'text'\n : (file.lang ?? getFiletypeFromFileName(file.name));\n const baseThemeType =\n typeof theme === 'string' ? highlighter.getTheme(theme).type : undefined;\n const themeStyles = getHighlighterThemeStyles({\n theme,\n highlighter,\n });\n state.lineInfo = (shikiLineNumber: number) => ({\n type: 'context',\n lineIndex: shikiLineNumber - 1 + startingLine,\n lineNumber: shikiLineNumber + startingLine,\n });\n // tokenizeTimeLimit: 0 disables shiki's silent 500ms-per-line tokenization\n // abort. When it trips (slow devices, cold JS-regex-engine compile), the\n // rest of the line collapses to the enclosing scope's color — and since\n // dual-theme rendering tokenizes per theme, the first (dark) pass can smear\n // while the warm second (light) pass stays correct. Pathological content is\n // already guarded by tokenizeMaxLineLength, which renders long lines plain.\n const hastConfig: CodeToHastOptions<DiffsThemeNames> = (() => {\n if (typeof theme === 'string') {\n return {\n lang,\n theme,\n transformers,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeMaxLineLength,\n tokenizeTimeLimit: 0,\n };\n }\n return {\n lang,\n themes: theme,\n transformers,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeMaxLineLength,\n tokenizeTimeLimit: 0,\n };\n })();\n const highlightedLines = getLineNodes(\n highlighter.codeToHast(\n normalizeHighlightLineEndings(\n isWindowedHighlight\n ? extractWindowedFileContent(\n lines ?? linesFromFileContents(file.contents),\n startingLine,\n totalLines\n )\n : file.contents\n ),\n hastConfig\n )\n );\n\n // Create sparse array for windowed rendering\n const code = isWindowedHighlight ? new Array(startingLine) : highlightedLines;\n if (isWindowedHighlight) {\n appendItems(code, highlightedLines);\n }\n\n return { code, themeStyles, baseThemeType };\n}\n\n// Shiki does not treat a lone carriage return as a line break. Normalize only\n// the text sent to the highlighter so its output stays aligned with the file\n// model while the original document retains its line endings.\nfunction normalizeHighlightLineEndings(contents: string): string {\n return contents.replace(/\\r(?!\\n)/g, '\\n');\n}\n\nfunction extractWindowedFileContent(\n lines: string[],\n startingLine: number,\n totalLines: number\n): string {\n if (lines.length === 0) {\n return '';\n }\n const endLine = Math.min(startingLine + totalLines, lines.length);\n return lines.slice(startingLine, endLine).join('');\n}\n"],"mappings":";;;;;;;;AAiBA,MAAM,6BAAwD,EAC5D,gBAAgB,MAClB;AAEA,SAAgB,0BACd,MACA,aACA,EAAE,OAAO,uBAAuB,uBAChC,EACE,gBACA,cACA,YACA,UAC6B,4BACb;CAClB,IAAI,gBAAgB;EAClB,iBAAiB;EACjB,eAAe;CACjB,OAAO;EAKL,eAAe;EACf,aAAa;CACf;CACA,MAAM,sBAAsB,eAAe,KAAK,aAAa;CAC7D,MAAM,EAAE,OAAO,iBACb,2BAA2B,mBAAmB;CAChD,MAAM,OAAO,iBACT,SACC,KAAK,QAAQ,wBAAwB,KAAK,IAAI;CACnD,MAAM,gBACJ,OAAO,UAAU,WAAW,YAAY,SAAS,KAAK,CAAC,CAAC,OAAO,KAAA;CACjE,MAAM,cAAc,0BAA0B;EAC5C;EACA;CACF,CAAC;CACD,MAAM,YAAY,qBAA6B;EAC7C,MAAM;EACN,WAAW,kBAAkB,IAAI;EACjC,YAAY,kBAAkB;CAChC;CAOA,MAAM,oBAAwD;EAC5D,IAAI,OAAO,UAAU,UACnB,OAAO;GACL;GACA;GACA;GACA,cAAc;GACd,mBAAmB,wBAAwB,OAAO;GAClD;GACA,mBAAmB;EACrB;EAEF,OAAO;GACL;GACA,QAAQ;GACR;GACA,cAAc;GACd,mBAAmB,wBAAwB,OAAO;GAClD;GACA,mBAAmB;EACrB;CACF,EAAA,CAAG;CACH,MAAM,mBAAmB,aACvB,YAAY,WACV,8BACE,sBACI,2BACE,SAAS,sBAAsB,KAAK,QAAQ,GAC5C,cACA,UACF,IACA,KAAK,QACX,GACA,UACF,CACF;CAGA,MAAM,OAAO,sBAAsB,IAAI,MAAM,YAAY,IAAI;CAC7D,IAAI,qBACF,YAAY,MAAM,gBAAgB;CAGpC,OAAO;EAAE;EAAM;EAAa;CAAc;AAC5C;AAKA,SAAS,8BAA8B,UAA0B;CAC/D,OAAO,SAAS,QAAQ,aAAa,IAAI;AAC3C;AAEA,SAAS,2BACP,OACA,cACA,YACQ;CACR,IAAI,MAAM,WAAW,GACnB,OAAO;CAET,MAAM,UAAU,KAAK,IAAI,eAAe,YAAY,MAAM,MAAM;CAChE,OAAO,MAAM,MAAM,cAAc,OAAO,CAAC,CAAC,KAAK,EAAE;AACnD"}