@pierre/diffs 1.2.0-beta.4 → 1.2.0-beta.6

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 (59) hide show
  1. package/dist/components/CodeView.d.ts +10 -6
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +58 -8
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts.map +1 -1
  6. package/dist/components/VirtualizedFile.d.ts +1 -0
  7. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  8. package/dist/components/VirtualizedFile.js +19 -6
  9. package/dist/components/VirtualizedFile.js.map +1 -1
  10. package/dist/components/VirtualizedFileDiff.d.ts +6 -0
  11. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  12. package/dist/components/VirtualizedFileDiff.js +75 -35
  13. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +0 -1
  16. package/dist/constants.js.map +1 -1
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.js +1 -1
  19. package/dist/managers/InteractionManager.d.ts +20 -2
  20. package/dist/managers/InteractionManager.d.ts.map +1 -1
  21. package/dist/managers/InteractionManager.js +223 -28
  22. package/dist/managers/InteractionManager.js.map +1 -1
  23. package/dist/managers/ResizeManager.d.ts +7 -2
  24. package/dist/managers/ResizeManager.d.ts.map +1 -1
  25. package/dist/managers/ResizeManager.js +52 -16
  26. package/dist/managers/ResizeManager.js.map +1 -1
  27. package/dist/react/CodeView.d.ts +1 -0
  28. package/dist/react/CodeView.d.ts.map +1 -1
  29. package/dist/react/CodeView.js +9 -0
  30. package/dist/react/CodeView.js.map +1 -1
  31. package/dist/react/constants.js +3 -1
  32. package/dist/react/constants.js.map +1 -1
  33. package/dist/react/index.d.ts +2 -2
  34. package/dist/react/jsx.d.ts.map +1 -1
  35. package/dist/ssr/index.d.ts +2 -2
  36. package/dist/style.js +1 -1
  37. package/dist/style.js.map +1 -1
  38. package/dist/types.d.ts +6 -6
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/utils/computeVirtualFileMetrics.d.ts +11 -0
  41. package/dist/utils/computeVirtualFileMetrics.d.ts.map +1 -0
  42. package/dist/utils/{resolveVirtualFileMetrics.js → computeVirtualFileMetrics.js} +7 -10
  43. package/dist/utils/computeVirtualFileMetrics.js.map +1 -0
  44. package/dist/utils/createGutterUtilityContentNode.js +1 -0
  45. package/dist/utils/createGutterUtilityContentNode.js.map +1 -1
  46. package/dist/utils/detachString.d.ts +5 -0
  47. package/dist/utils/detachString.d.ts.map +1 -0
  48. package/dist/utils/detachString.js +19 -0
  49. package/dist/utils/detachString.js.map +1 -0
  50. package/dist/utils/parsePatchFiles.js +163 -56
  51. package/dist/utils/parsePatchFiles.js.map +1 -1
  52. package/dist/worker/worker-portable.js +0 -1
  53. package/dist/worker/worker-portable.js.map +1 -1
  54. package/dist/worker/worker.js +0 -1
  55. package/dist/worker/worker.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/utils/resolveVirtualFileMetrics.d.ts +0 -10
  58. package/dist/utils/resolveVirtualFileMetrics.d.ts.map +0 -1
  59. package/dist/utils/resolveVirtualFileMetrics.js.map +0 -1
@@ -1,21 +1,21 @@
1
- import { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, GIT_DIFF_FILE_BREAK_REGEX, HUNK_HEADER, INDEX_LINE_METADATA, SPLIT_WITH_NEWLINES, UNIFIED_DIFF_FILE_BREAK_REGEX } from "../constants.js";
1
+ import { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, GIT_DIFF_FILE_BREAK_REGEX, INDEX_LINE_METADATA, UNIFIED_DIFF_FILE_BREAK_REGEX } from "../constants.js";
2
2
  import { cleanLastNewline } from "./cleanLastNewline.js";
3
- import { parseLineType } from "./parseLineType.js";
3
+ import { detachString } from "./detachString.js";
4
4
 
5
5
  //#region src/utils/parsePatchFiles.ts
6
6
  function processPatch(data, cacheKeyPrefix, throwOnError = false) {
7
- const isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(data);
8
- const rawFiles = data.split(isGitDiff ? GIT_DIFF_FILE_BREAK_REGEX : UNIFIED_DIFF_FILE_BREAK_REGEX);
7
+ const isGitDiff = isGitDiffPatch(data);
8
+ const rawFiles = isGitDiff ? splitAtLinePrefix(data, "diff --git") : data.split(UNIFIED_DIFF_FILE_BREAK_REGEX);
9
9
  let patchMetadata;
10
10
  const files = [];
11
11
  for (const fileOrPatchMetadata of rawFiles) {
12
12
  if (isGitDiff && !GIT_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {
13
- if (patchMetadata == null) patchMetadata = fileOrPatchMetadata;
13
+ if (patchMetadata == null) patchMetadata = detachString(fileOrPatchMetadata);
14
14
  else if (throwOnError) throw Error("parsePatchContent: unknown file blob");
15
15
  else console.error("parsePatchContent: unknown file blob:", fileOrPatchMetadata);
16
16
  continue;
17
17
  } else if (!isGitDiff && !UNIFIED_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {
18
- if (patchMetadata == null) patchMetadata = fileOrPatchMetadata;
18
+ if (patchMetadata == null) patchMetadata = detachString(fileOrPatchMetadata);
19
19
  else if (throwOnError) throw Error("parsePatchContent: unknown file blob");
20
20
  else console.error("parsePatchContent: unknown file blob:", fileOrPatchMetadata);
21
21
  continue;
@@ -34,23 +34,23 @@ function processPatch(data, cacheKeyPrefix, throwOnError = false) {
34
34
  }
35
35
  function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(fileDiffString), oldFile, newFile, throwOnError = false } = {}) {
36
36
  let lastHunkEnd = 0;
37
- const hunks = fileDiffString.split(FILE_CONTEXT_BLOB);
37
+ const hunks = splitAtLinePrefix(fileDiffString, "@@ ");
38
38
  let currentFile;
39
39
  const isPartial = oldFile == null || newFile == null;
40
40
  let deletionLineIndex = 0;
41
41
  let additionLineIndex = 0;
42
42
  for (const hunk of hunks) {
43
- const lines = hunk.split(SPLIT_WITH_NEWLINES);
44
- const firstLine = lines.shift();
43
+ const lines = splitWithNewlines(hunk);
44
+ const firstLine = lines[0];
45
45
  if (firstLine == null) {
46
46
  if (throwOnError) throw Error("parsePatchContent: invalid hunk");
47
47
  else console.error("parsePatchContent: invalid hunk", hunk);
48
48
  continue;
49
49
  }
50
- const fileHeaderMatch = firstLine.match(HUNK_HEADER);
50
+ const fileHeader = parseHunkHeader(firstLine);
51
51
  let additionLines = 0;
52
52
  let deletionLines = 0;
53
- if (fileHeaderMatch == null || currentFile == null) {
53
+ if (fileHeader == null || currentFile == null) {
54
54
  if (currentFile != null) {
55
55
  if (throwOnError) throw Error("parsePatchContent: Invalid hunk");
56
56
  else console.error("parsePatchContent: Invalid hunk", hunk);
@@ -63,46 +63,48 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
63
63
  splitLineCount: 0,
64
64
  unifiedLineCount: 0,
65
65
  isPartial,
66
- additionLines: !isPartial && oldFile != null && newFile != null ? newFile.contents.split(SPLIT_WITH_NEWLINES) : [],
67
- deletionLines: !isPartial && oldFile != null && newFile != null ? oldFile.contents.split(SPLIT_WITH_NEWLINES) : [],
68
- cacheKey
66
+ additionLines: !isPartial && oldFile != null && newFile != null ? splitFileContents(newFile.contents) : [],
67
+ deletionLines: !isPartial && oldFile != null && newFile != null ? splitFileContents(oldFile.contents) : [],
68
+ cacheKey: maybeDetachOptionalString(cacheKey)
69
69
  };
70
70
  if (currentFile.additionLines.length === 1 && newFile?.contents === "") currentFile.additionLines.length = 0;
71
71
  if (currentFile.deletionLines.length === 1 && oldFile?.contents === "") currentFile.deletionLines.length = 0;
72
- lines.unshift(firstLine);
73
72
  for (const line of lines) {
74
- const filenameMatch = line.match(isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX);
75
73
  if (line.startsWith("diff --git")) {
76
74
  const [, , prevName, , name] = line.trim().match(ALTERNATE_FILE_NAMES_GIT) ?? [];
77
- currentFile.name = name.trim();
78
- if (prevName !== name) currentFile.prevName = prevName.trim();
79
- } else if (filenameMatch != null) {
75
+ currentFile.name = detachString(name.trim());
76
+ if (prevName !== name) currentFile.prevName = detachString(prevName.trim());
77
+ continue;
78
+ }
79
+ const filenameMatch = line.startsWith("---") || line.startsWith("+++") ? line.match(isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX) : null;
80
+ if (filenameMatch != null) {
80
81
  const [, type, fileName] = filenameMatch;
81
82
  if (type === "---" && fileName !== "/dev/null") {
82
- currentFile.prevName = fileName.trim();
83
- currentFile.name = fileName.trim();
84
- } else if (type === "+++" && fileName !== "/dev/null") currentFile.name = fileName.trim();
83
+ const detachedFileName = detachString(fileName.trim());
84
+ currentFile.prevName = detachedFileName;
85
+ currentFile.name = detachedFileName;
86
+ } else if (type === "+++" && fileName !== "/dev/null") currentFile.name = detachString(fileName.trim());
85
87
  } else if (isGitDiff) {
86
- if (line.startsWith("new mode ")) currentFile.mode = line.replace("new mode", "").trim();
87
- if (line.startsWith("old mode ")) currentFile.prevMode = line.replace("old mode", "").trim();
88
+ if (line.startsWith("new mode ")) currentFile.mode = detachString(line.slice(8).trim());
89
+ if (line.startsWith("old mode ")) currentFile.prevMode = detachString(line.slice(8).trim());
88
90
  if (line.startsWith("new file mode")) {
89
91
  currentFile.type = "new";
90
- currentFile.mode = line.replace("new file mode", "").trim();
92
+ currentFile.mode = detachString(line.slice(13).trim());
91
93
  }
92
94
  if (line.startsWith("deleted file mode")) {
93
95
  currentFile.type = "deleted";
94
- currentFile.mode = line.replace("deleted file mode", "").trim();
96
+ currentFile.mode = detachString(line.slice(17).trim());
95
97
  }
96
98
  if (line.startsWith("similarity index")) if (line.startsWith("similarity index 100%")) currentFile.type = "rename-pure";
97
99
  else currentFile.type = "rename-changed";
98
100
  if (line.startsWith("index ")) {
99
101
  const [, prevObjectId, newObjectId, mode] = line.trim().match(INDEX_LINE_METADATA) ?? [];
100
- if (prevObjectId != null) currentFile.prevObjectId = prevObjectId;
101
- if (newObjectId != null) currentFile.newObjectId = newObjectId;
102
- if (mode != null) currentFile.mode = mode;
102
+ if (prevObjectId != null) currentFile.prevObjectId = detachString(prevObjectId);
103
+ if (newObjectId != null) currentFile.newObjectId = detachString(newObjectId);
104
+ if (mode != null) currentFile.mode = detachString(mode);
103
105
  }
104
- if (line.startsWith("rename from ")) currentFile.prevName = line.replace("rename from ", "").trim();
105
- if (line.startsWith("rename to ")) currentFile.name = line.replace("rename to ", "").trim();
106
+ if (line.startsWith("rename from ")) currentFile.prevName = detachString(line.slice(12).trim());
107
+ if (line.startsWith("rename to ")) currentFile.name = detachString(line.slice(10).trim());
106
108
  }
107
109
  }
108
110
  continue;
@@ -110,8 +112,7 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
110
112
  let currentContent;
111
113
  let lastLineType;
112
114
  while (lines.length > 0 && (lines[lines.length - 1] === "\n" || lines[lines.length - 1] === "\r" || lines[lines.length - 1] === "\r\n" || lines[lines.length - 1] === "")) lines.pop();
113
- const additionStart = parseInt(fileHeaderMatch[3]);
114
- const deletionStart = parseInt(fileHeaderMatch[1]);
115
+ const { additionStart, deletionStart } = fileHeader;
115
116
  deletionLineIndex = isPartial ? deletionLineIndex : deletionStart - 1;
116
117
  additionLineIndex = isPartial ? additionLineIndex : additionStart - 1;
117
118
  const hunkData = {
@@ -120,41 +121,34 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
120
121
  splitLineStart: 0,
121
122
  unifiedLineCount: 0,
122
123
  unifiedLineStart: 0,
123
- additionCount: parseInt(fileHeaderMatch[4] ?? "1"),
124
+ additionCount: fileHeader.additionCount,
124
125
  additionStart,
125
126
  additionLines,
126
- deletionCount: parseInt(fileHeaderMatch[2] ?? "1"),
127
+ deletionCount: fileHeader.deletionCount,
127
128
  deletionStart,
128
129
  deletionLines,
129
130
  deletionLineIndex,
130
131
  additionLineIndex,
131
132
  hunkContent: [],
132
- hunkContext: fileHeaderMatch[5],
133
- hunkSpecs: firstLine,
133
+ hunkContext: maybeDetachOptionalString(fileHeader.hunkContext),
134
+ hunkSpecs: detachString(firstLine),
134
135
  noEOFCRAdditions: false,
135
136
  noEOFCRDeletions: false
136
137
  };
137
- if (isNaN(hunkData.additionCount) || isNaN(hunkData.deletionCount) || isNaN(hunkData.additionStart) || isNaN(hunkData.deletionStart)) {
138
- if (throwOnError) throw Error("parsePatchContent: invalid hunk metadata");
139
- else console.error("parsePatchContent: invalid hunk metadata", hunkData);
140
- continue;
141
- }
142
138
  let parsedAdditionLines = 0;
143
139
  let parsedDeletionLines = 0;
144
- for (const rawLine of lines) {
145
- if (isHunkBodyComplete({
146
- additionCount: hunkData.additionCount,
147
- parsedAdditionLines,
148
- deletionCount: hunkData.deletionCount,
149
- parsedDeletionLines
150
- }) && !rawLine.startsWith("\\")) break;
151
- const parsedLine = parseLineType(rawLine);
152
- if (parsedLine == null) {
140
+ for (let lineIndex = 1; lineIndex < lines.length; lineIndex++) {
141
+ const rawLine = lines[lineIndex];
142
+ if (parsedAdditionLines >= hunkData.additionCount && parsedDeletionLines >= hunkData.deletionCount && !rawLine.startsWith("\\")) break;
143
+ const firstChar = rawLine[0];
144
+ if (firstChar !== "+" && firstChar !== "-" && firstChar !== " " && firstChar !== "\\") {
145
+ console.error(`parseLineType: Invalid firstChar: "${firstChar}", full line: "${rawLine}"`);
153
146
  console.error("processFile: invalid rawLine:", rawLine);
154
147
  continue;
155
148
  }
156
- const { type, line } = parsedLine;
149
+ const type = parseRawLineType(firstChar);
157
150
  if (type === "addition") {
151
+ const line = getParsedLineContent(rawLine);
158
152
  if (currentContent == null || currentContent.type !== "change") {
159
153
  currentContent = createContentGroup("change", deletionLineIndex, additionLineIndex);
160
154
  hunkData.hunkContent.push(currentContent);
@@ -166,6 +160,7 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
166
160
  additionLines++;
167
161
  lastLineType = "addition";
168
162
  } else if (type === "deletion") {
163
+ const line = getParsedLineContent(rawLine);
169
164
  if (currentContent == null || currentContent.type !== "change") {
170
165
  currentContent = createContentGroup("change", deletionLineIndex, additionLineIndex);
171
166
  hunkData.hunkContent.push(currentContent);
@@ -177,6 +172,7 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
177
172
  deletionLines++;
178
173
  lastLineType = "deletion";
179
174
  } else if (type === "context") {
175
+ const line = getParsedLineContent(rawLine);
180
176
  if (currentContent == null || currentContent.type !== "context") {
181
177
  currentContent = createContentGroup("context", deletionLineIndex, additionLineIndex);
182
178
  hunkData.hunkContent.push(currentContent);
@@ -242,9 +238,6 @@ function processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREAK
242
238
  if (currentFile.type !== "rename-pure" && currentFile.type !== "rename-changed") currentFile.prevName = void 0;
243
239
  return currentFile;
244
240
  }
245
- function isHunkBodyComplete({ additionCount, parsedAdditionLines, deletionCount, parsedDeletionLines }) {
246
- return parsedAdditionLines >= additionCount && parsedDeletionLines >= deletionCount;
247
- }
248
241
  /**
249
242
  * Parses a patch file string into an array of parsed patches.
250
243
  *
@@ -255,7 +248,8 @@ function isHunkBodyComplete({ additionCount, parsedAdditionLines, deletionCount,
255
248
  */
256
249
  function parsePatchFiles(data, cacheKeyPrefix, throwOnError = false) {
257
250
  const patches = [];
258
- for (const patch of data.split(COMMIT_METADATA_SPLIT)) try {
251
+ const rawPatches = hasCommitMetadataBoundary(data) ? data.split(COMMIT_METADATA_SPLIT) : [data];
252
+ for (const patch of rawPatches) try {
259
253
  patches.push(processPatch(patch, cacheKeyPrefix != null ? `${cacheKeyPrefix}-${patches.length}` : void 0, throwOnError));
260
254
  } catch (error) {
261
255
  if (throwOnError) throw error;
@@ -263,6 +257,119 @@ function parsePatchFiles(data, cacheKeyPrefix, throwOnError = false) {
263
257
  }
264
258
  return patches;
265
259
  }
260
+ function hasCommitMetadataBoundary(data) {
261
+ return data.startsWith("From ") || data.includes("\nFrom ");
262
+ }
263
+ function splitFileContents(contents) {
264
+ const lines = splitWithNewlines(contents);
265
+ for (let index = 0; index < lines.length; index++) lines[index] = detachString(lines[index]);
266
+ return lines;
267
+ }
268
+ function splitWithNewlines(contents) {
269
+ if (contents.length === 0) return [""];
270
+ const lines = [];
271
+ let startIndex = 0;
272
+ for (;;) {
273
+ const newlineIndex = contents.indexOf("\n", startIndex);
274
+ if (newlineIndex === -1) break;
275
+ lines.push(contents.slice(startIndex, newlineIndex + 1));
276
+ startIndex = newlineIndex + 1;
277
+ }
278
+ if (startIndex < contents.length) lines.push(contents.slice(startIndex));
279
+ return lines;
280
+ }
281
+ function parseHunkHeader(line) {
282
+ if (!line.startsWith("@@ -")) return;
283
+ let index = 4;
284
+ const deletionStartResult = readPositiveInteger(line, index);
285
+ if (deletionStartResult == null) return;
286
+ const deletionStart = deletionStartResult.value;
287
+ index = deletionStartResult.endIndex;
288
+ let deletionCount = 1;
289
+ if (line[index] === ",") {
290
+ const deletionCountResult = readPositiveInteger(line, index + 1);
291
+ if (deletionCountResult == null) return;
292
+ deletionCount = deletionCountResult.value;
293
+ index = deletionCountResult.endIndex;
294
+ }
295
+ if (line[index] !== " " || line[index + 1] !== "+") return;
296
+ index += 2;
297
+ const additionStartResult = readPositiveInteger(line, index);
298
+ if (additionStartResult == null) return;
299
+ const additionStart = additionStartResult.value;
300
+ index = additionStartResult.endIndex;
301
+ let additionCount = 1;
302
+ if (line[index] === ",") {
303
+ const additionCountResult = readPositiveInteger(line, index + 1);
304
+ if (additionCountResult == null) return;
305
+ additionCount = additionCountResult.value;
306
+ index = additionCountResult.endIndex;
307
+ }
308
+ if (line[index] !== " " || line[index + 1] !== "@" || line[index + 2] !== "@") return;
309
+ let hunkContext;
310
+ const contextStartIndex = index + 3;
311
+ if (line[contextStartIndex] === " ") hunkContext = trimLineEnd(line.slice(contextStartIndex + 1));
312
+ return {
313
+ additionCount,
314
+ additionStart,
315
+ deletionCount,
316
+ deletionStart,
317
+ hunkContext
318
+ };
319
+ }
320
+ function readPositiveInteger(value, startIndex) {
321
+ let index = startIndex;
322
+ let parsedValue = 0;
323
+ for (; index < value.length; index++) {
324
+ const digit = value.charCodeAt(index) - 48;
325
+ if (digit < 0 || digit > 9) break;
326
+ parsedValue = parsedValue * 10 + digit;
327
+ }
328
+ if (index === startIndex) return;
329
+ return {
330
+ value: parsedValue,
331
+ endIndex: index
332
+ };
333
+ }
334
+ function trimLineEnd(value) {
335
+ if (value.endsWith("\r\n")) return value.slice(0, -2);
336
+ if (value.endsWith("\n")) return value.slice(0, -1);
337
+ return value;
338
+ }
339
+ function isGitDiffPatch(data) {
340
+ return data.startsWith("diff --git") || data.includes("\ndiff --git");
341
+ }
342
+ function splitAtLinePrefix(contents, prefix) {
343
+ if (contents.length === 0) return [""];
344
+ const newlinePrefix = `\n${prefix}`;
345
+ const firstBoundaryIndex = contents.startsWith(prefix) ? 0 : findLinePrefixIndex(contents, newlinePrefix, 0);
346
+ if (firstBoundaryIndex === -1) return [contents];
347
+ const parts = [];
348
+ if (firstBoundaryIndex > 0) parts.push(contents.slice(0, firstBoundaryIndex));
349
+ let startIndex = firstBoundaryIndex;
350
+ for (;;) {
351
+ const nextBoundaryIndex = findLinePrefixIndex(contents, newlinePrefix, startIndex + 1);
352
+ if (nextBoundaryIndex === -1) break;
353
+ parts.push(contents.slice(startIndex, nextBoundaryIndex));
354
+ startIndex = nextBoundaryIndex;
355
+ }
356
+ parts.push(contents.slice(startIndex));
357
+ return parts;
358
+ }
359
+ function findLinePrefixIndex(contents, newlinePrefix, fromIndex) {
360
+ const index = contents.indexOf(newlinePrefix, fromIndex);
361
+ return index === -1 ? -1 : index + 1;
362
+ }
363
+ function maybeDetachOptionalString(value) {
364
+ return value == null ? value : detachString(value);
365
+ }
366
+ function parseRawLineType(firstChar) {
367
+ return firstChar === " " ? "context" : firstChar === "\\" ? "metadata" : firstChar === "+" ? "addition" : "deletion";
368
+ }
369
+ function getParsedLineContent(rawLine) {
370
+ const processedLine = rawLine.slice(1);
371
+ return detachString(processedLine === "" ? "\n" : processedLine);
372
+ }
266
373
  function createContentGroup(type, deletionLineIndex, additionLineIndex) {
267
374
  if (type === "change") return {
268
375
  type: "change",
@@ -1 +1 @@
1
- {"version":3,"file":"parsePatchFiles.js","names":["patchMetadata: string | undefined","files: FileDiffMetadata[]","currentFile: FileDiffMetadata | undefined","currentContent: ContextContent | ChangeContent | undefined","lastLineType: 'context' | 'addition' | 'deletion' | undefined","hunkData: Hunk","lastHunkEnd","patches: ParsedPatch[]"],"sources":["../../src/utils/parsePatchFiles.ts"],"sourcesContent":["import {\n ALTERNATE_FILE_NAMES_GIT,\n COMMIT_METADATA_SPLIT,\n FILE_CONTEXT_BLOB,\n FILENAME_HEADER_REGEX,\n FILENAME_HEADER_REGEX_GIT,\n GIT_DIFF_FILE_BREAK_REGEX,\n HUNK_HEADER,\n INDEX_LINE_METADATA,\n SPLIT_WITH_NEWLINES,\n UNIFIED_DIFF_FILE_BREAK_REGEX,\n} from '../constants';\nimport type {\n ChangeContent,\n ContextContent,\n FileContents,\n FileDiffMetadata,\n Hunk,\n ParsedPatch,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { parseLineType } from './parseLineType';\n\nexport function processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch {\n const isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(data);\n const rawFiles = data.split(\n isGitDiff ? GIT_DIFF_FILE_BREAK_REGEX : UNIFIED_DIFF_FILE_BREAK_REGEX\n );\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 = 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 !UNIFIED_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)\n ) {\n if (patchMetadata == null) {\n patchMetadata = 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 {\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 = fileDiffString.split(FILE_CONTEXT_BLOB);\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 = hunk.split(SPLIT_WITH_NEWLINES);\n const firstLine = lines.shift();\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 fileHeaderMatch = firstLine.match(HUNK_HEADER);\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 (fileHeaderMatch == 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 ? newFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n deletionLines:\n !isPartial && oldFile != null && newFile != null\n ? oldFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n 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 // Push that first line back into the group of lines so we can properly\n // parse it out\n lines.unshift(firstLine);\n for (const line of lines) {\n const filenameMatch = line.match(\n isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX\n );\n if (line.startsWith('diff --git')) {\n const [, , prevName, , name] =\n line.trim().match(ALTERNATE_FILE_NAMES_GIT) ?? [];\n currentFile.name = name.trim();\n if (prevName !== name) {\n currentFile.prevName = prevName.trim();\n }\n } else if (filenameMatch != null) {\n const [, type, fileName] = filenameMatch;\n if (type === '---' && fileName !== '/dev/null') {\n currentFile.prevName = fileName.trim();\n currentFile.name = fileName.trim();\n } else if (type === '+++' && fileName !== '/dev/null') {\n currentFile.name = 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 = line.replace('new mode', '').trim();\n }\n if (line.startsWith('old mode ')) {\n currentFile.prevMode = line.replace('old mode', '').trim();\n }\n if (line.startsWith('new file mode')) {\n currentFile.type = 'new';\n currentFile.mode = line.replace('new file mode', '').trim();\n }\n if (line.startsWith('deleted file mode')) {\n currentFile.type = 'deleted';\n currentFile.mode = line.replace('deleted file mode', '').trim();\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 = prevObjectId;\n }\n if (newObjectId != null) {\n currentFile.newObjectId = newObjectId;\n }\n if (mode != null) {\n currentFile.mode = 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 = line.replace('rename from ', '').trim();\n }\n if (line.startsWith('rename to ')) {\n currentFile.name = line.replace('rename to ', '').trim();\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 = parseInt(fileHeaderMatch[3]);\n const deletionStart = parseInt(fileHeaderMatch[1]);\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: parseInt(fileHeaderMatch[4] ?? '1'),\n additionStart,\n additionLines,\n\n deletionCount: parseInt(fileHeaderMatch[2] ?? '1'),\n deletionStart,\n deletionLines,\n\n deletionLineIndex,\n additionLineIndex,\n\n hunkContent: [],\n hunkContext: fileHeaderMatch[5],\n hunkSpecs: firstLine,\n\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n // Lets validate out hunkData to ensure there's no broken data from the\n // regex\n if (\n isNaN(hunkData.additionCount) ||\n isNaN(hunkData.deletionCount) ||\n isNaN(hunkData.additionStart) ||\n isNaN(hunkData.deletionStart)\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk metadata');\n } else {\n console.error('parsePatchContent: invalid hunk metadata', hunkData);\n }\n continue;\n }\n\n // Now we process each line of the hunk\n let parsedAdditionLines = 0;\n let parsedDeletionLines = 0;\n for (const rawLine of lines) {\n if (\n isHunkBodyComplete({\n additionCount: hunkData.additionCount,\n parsedAdditionLines,\n deletionCount: hunkData.deletionCount,\n parsedDeletionLines,\n }) &&\n !rawLine.startsWith('\\\\')\n ) {\n break;\n }\n\n const parsedLine = parseLineType(rawLine);\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 (parsedLine == null) {\n console.error('processFile: invalid rawLine:', rawLine);\n continue;\n }\n\n const { type, line } = parsedLine;\n if (type === 'addition') {\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 (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 (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 hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n hunkData.additionStart - 1 - lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = hunkData.additionStart + hunkData.additionCount - 1;\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\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 = lastHunk.additionStart + lastHunk.additionCount - 1;\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 return currentFile;\n}\n\ninterface isHunkBodyCompleteProps {\n additionCount: number;\n parsedAdditionLines: number;\n deletionCount: number;\n parsedDeletionLines: number;\n}\n\n// Git format-patch trailers follow the final hunk without a new diff marker.\n// Once both header-declared line counts are satisfied, the next non-metadata\n// line belongs to the surrounding patch file rather than the hunk body.\nfunction isHunkBodyComplete({\n additionCount,\n parsedAdditionLines,\n deletionCount,\n parsedDeletionLines,\n}: isHunkBodyCompleteProps): boolean {\n return (\n parsedAdditionLines >= additionCount && parsedDeletionLines >= deletionCount\n );\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 for (const patch of data.split(COMMIT_METADATA_SPLIT)) {\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 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":["patchMetadata: string | undefined","files: FileDiffMetadata[]","currentFile: FileDiffMetadata | undefined","currentContent: ContextContent | ChangeContent | undefined","lastLineType: 'context' | 'addition' | 'deletion' | undefined","hunkData: Hunk","lastHunkEnd","patches: ParsedPatch[]","lines: string[]","hunkContext: string | undefined","parts: string[]"],"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 UNIFIED_DIFF_FILE_BREAK_REGEX,\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 } from './detachString';\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 = false\n): ParsedPatch {\n const isGitDiff = isGitDiffPatch(data);\n const rawFiles = isGitDiff\n ? splitAtLinePrefix(data, 'diff --git')\n : data.split(UNIFIED_DIFF_FILE_BREAK_REGEX);\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 !UNIFIED_DIFF_FILE_BREAK_REGEX.test(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 {\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 [, , prevName, , name] =\n line.trim().match(ALTERNATE_FILE_NAMES_GIT) ?? [];\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 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 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 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 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 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 hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n hunkData.additionStart - 1 - lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = hunkData.additionStart + hunkData.additionCount - 1;\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\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 = lastHunk.additionStart + lastHunk.additionCount - 1;\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 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 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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"}
@@ -12176,7 +12176,6 @@ const DEFAULT_VIRTUAL_FILE_METRICS = {
12176
12176
  hunkLineCount: 50,
12177
12177
  lineHeight: 20,
12178
12178
  diffHeaderHeight: 44,
12179
- hunkSeparatorHeight: 32,
12180
12179
  spacing: 8
12181
12180
  };
12182
12181
  const DEFAULT_CODE_VIEW_FILE_METRICS = {