@pierre/diffs 1.3.0-beta.8 → 1.3.0-rc.1
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.
- package/dist/components/CodeView.d.ts +12 -12
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +51 -43
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +7 -2
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +36 -8
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +34 -6
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +198 -17
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/FileStream.d.ts.map +1 -1
- package/dist/components/FileStream.js +2 -1
- package/dist/components/FileStream.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts +2 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +9 -5
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +14 -0
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +61 -3
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +14 -2
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +29 -3
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/edit/index.d.ts +5 -0
- package/dist/edit/index.js +3 -0
- package/dist/editor/command.d.ts +1 -1
- package/dist/editor/command.d.ts.map +1 -1
- package/dist/editor/command.js +11 -0
- package/dist/editor/command.js.map +1 -1
- package/dist/editor/editStack.d.ts +6 -2
- package/dist/editor/editStack.d.ts.map +1 -1
- package/dist/editor/editStack.js +54 -13
- package/dist/editor/editStack.js.map +1 -1
- package/dist/editor/editor.d.ts +51 -23
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +995 -242
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +1 -1
- package/dist/editor/languages.d.ts +26 -0
- package/dist/editor/languages.d.ts.map +1 -0
- package/dist/editor/languages.js +363 -0
- package/dist/editor/languages.js.map +1 -0
- package/dist/editor/lineAnnotations.d.ts.map +1 -1
- package/dist/editor/lineAnnotations.js +25 -26
- package/dist/editor/lineAnnotations.js.map +1 -1
- package/dist/editor/marker.d.ts +5 -3
- package/dist/editor/marker.d.ts.map +1 -1
- package/dist/editor/marker.js +111 -91
- package/dist/editor/marker.js.map +1 -1
- package/dist/editor/matchBrackets.d.ts +3 -2
- package/dist/editor/matchBrackets.d.ts.map +1 -1
- package/dist/editor/matchBrackets.js +2 -10
- package/dist/editor/matchBrackets.js.map +1 -1
- package/dist/editor/pieceTable.d.ts +2 -1
- package/dist/editor/pieceTable.d.ts.map +1 -1
- package/dist/editor/pieceTable.js +103 -39
- package/dist/editor/pieceTable.js.map +1 -1
- package/dist/editor/platform.js +2 -0
- package/dist/editor/platform.js.map +1 -1
- package/dist/editor/popover.d.ts +25 -5
- package/dist/editor/popover.d.ts.map +1 -1
- package/dist/editor/popover.js +60 -14
- package/dist/editor/popover.js.map +1 -1
- package/dist/editor/searchPanel.d.ts +1 -0
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +15 -8
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.d.ts +31 -11
- package/dist/editor/selection.d.ts.map +1 -1
- package/dist/editor/selection.js +198 -70
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/selectionAction.d.ts +4 -3
- package/dist/editor/selectionAction.d.ts.map +1 -1
- package/dist/editor/selectionAction.js +9 -5
- package/dist/editor/selectionAction.js.map +1 -1
- package/dist/editor/stateStorage.d.ts +15 -0
- package/dist/editor/stateStorage.d.ts.map +1 -0
- package/dist/editor/stateStorage.js +65 -0
- package/dist/editor/stateStorage.js.map +1 -0
- package/dist/editor/textDocument.d.ts +12 -77
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +76 -34
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textMeasure.d.ts +7 -12
- package/dist/editor/textMeasure.d.ts.map +1 -1
- package/dist/editor/textMeasure.js +40 -32
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/{tokenzier.d.ts → tokenizer.d.ts} +3 -3
- package/dist/editor/tokenizer.d.ts.map +1 -0
- package/dist/editor/{tokenzier.js → tokenizer.js} +91 -26
- package/dist/editor/tokenizer.js.map +1 -0
- package/dist/editor/utils.js +1 -1
- package/dist/editor/utils.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +7 -6
- package/dist/managers/InteractionManager.d.ts +18 -7
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +154 -99
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/managers/UniversalRenderingManager.d.ts +2 -1
- package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
- package/dist/managers/UniversalRenderingManager.js +7 -3
- package/dist/managers/UniversalRenderingManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +13 -13
- package/dist/react/CodeView.d.ts.map +1 -1
- package/dist/react/CodeView.js +38 -29
- package/dist/react/CodeView.js.map +1 -1
- package/dist/react/EditContext.d.ts +20 -0
- package/dist/react/EditContext.d.ts.map +1 -0
- package/dist/react/EditContext.js +20 -0
- package/dist/react/EditContext.js.map +1 -0
- package/dist/react/File.d.ts +2 -1
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +3 -2
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +2 -2
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -2
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +2 -2
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -2
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +2 -2
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -2
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/UnresolvedFile.d.ts +1 -1
- package/dist/react/UnresolvedFile.js.map +1 -1
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +3 -3
- package/dist/react/types.d.ts +9 -1
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +5 -3
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +28 -27
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +5 -3
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js +29 -25
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.js +3 -3
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +33 -1
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +134 -21
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +2 -0
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +33 -11
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/FileDiffReact.js +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.js +1 -1
- package/dist/style.js +1 -1
- package/dist/types.d.ts +185 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/annotationHelpers.d.ts +20 -0
- package/dist/utils/annotationHelpers.d.ts.map +1 -0
- package/dist/utils/annotationHelpers.js +29 -0
- package/dist/utils/annotationHelpers.js.map +1 -0
- package/dist/utils/computeFileOffsets.d.ts.map +1 -1
- package/dist/utils/computeFileOffsets.js +34 -5
- package/dist/utils/computeFileOffsets.js.map +1 -1
- package/dist/utils/editSessionHunks.d.ts +66 -0
- package/dist/utils/editSessionHunks.d.ts.map +1 -0
- package/dist/utils/editSessionHunks.js +437 -0
- package/dist/utils/editSessionHunks.js.map +1 -0
- package/dist/utils/getHunkSideBoundaries.d.ts +7 -0
- package/dist/utils/getHunkSideBoundaries.d.ts.map +1 -0
- package/dist/utils/getHunkSideBoundaries.js +12 -0
- package/dist/utils/getHunkSideBoundaries.js.map +1 -0
- package/dist/utils/getTotalLineCountFromHunks.d.ts.map +1 -1
- package/dist/utils/getTotalLineCountFromHunks.js +2 -1
- package/dist/utils/getTotalLineCountFromHunks.js.map +1 -1
- package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
- package/dist/utils/hydratePartialDiff.js +4 -3
- package/dist/utils/hydratePartialDiff.js.map +1 -1
- package/dist/utils/isGutterUtilityPath.d.ts +5 -0
- package/dist/utils/isGutterUtilityPath.d.ts.map +1 -0
- package/dist/utils/isGutterUtilityPath.js +12 -0
- package/dist/utils/isGutterUtilityPath.js.map +1 -0
- package/dist/utils/iterateOverDiff.d.ts.map +1 -1
- package/dist/utils/iterateOverDiff.js +17 -12
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.js +9 -6
- package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
- package/dist/utils/parsePatchFiles.d.ts.map +1 -1
- package/dist/utils/parsePatchFiles.js +6 -3
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/realignChangeContent.d.ts +33 -0
- package/dist/utils/realignChangeContent.d.ts.map +1 -0
- package/dist/utils/realignChangeContent.js +148 -0
- package/dist/utils/realignChangeContent.js.map +1 -0
- package/dist/utils/renderDiffWithHighlighter.js +1 -1
- package/dist/utils/resolveRegion.d.ts.map +1 -1
- package/dist/utils/resolveRegion.js +18 -2
- package/dist/utils/resolveRegion.js.map +1 -1
- package/dist/utils/updateDiffHunks.d.ts +8 -2
- package/dist/utils/updateDiffHunks.d.ts.map +1 -1
- package/dist/utils/updateDiffHunks.js +9 -7
- package/dist/utils/updateDiffHunks.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +42 -2
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +101 -7
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +4 -2
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +4 -0
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/worker-portable.js +60 -20
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +61 -19
- package/dist/worker/worker.js.map +1 -1
- package/package.json +13 -8
- package/dist/editor/index.d.ts +0 -3
- package/dist/editor/index.js +0 -3
- package/dist/editor/tokenzier.d.ts.map +0 -1
- package/dist/editor/tokenzier.js.map +0 -1
- package/dist/react/EditorContext.d.ts +0 -15
- package/dist/react/EditorContext.d.ts.map +0 -1
- package/dist/react/EditorContext.js +0 -23
- package/dist/react/EditorContext.js.map +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//#region src/utils/realignChangeContent.ts
|
|
2
|
+
const MAX_ALIGNMENT_COMPARISONS = 4096;
|
|
3
|
+
const MIN_IMPROVEMENT_PER_PAIR = .5;
|
|
4
|
+
/**
|
|
5
|
+
* Re-split count-mismatched change blocks in every hunk so paired lines are
|
|
6
|
+
* chosen by content similarity instead of position, then slide blank-line
|
|
7
|
+
* insert/delete blocks to the top of their blank run. Mutates `hunks` in
|
|
8
|
+
* place; rendered row counts are unchanged (a split block covers the same
|
|
9
|
+
* split/unified rows as the original).
|
|
10
|
+
*/
|
|
11
|
+
function realignChangeContentBySimilarity(diff) {
|
|
12
|
+
for (const hunk of diff.hunks) {
|
|
13
|
+
for (let index = 0; index < hunk.hunkContent.length; index++) {
|
|
14
|
+
const content = hunk.hunkContent[index];
|
|
15
|
+
if (content.type !== "change") continue;
|
|
16
|
+
const replacement = realignChangeBlock(diff, content);
|
|
17
|
+
if (replacement != null) {
|
|
18
|
+
hunk.hunkContent.splice(index, 1, ...replacement);
|
|
19
|
+
index += replacement.length - 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
slideBlankBoundaryBlocksUp(hunk, diff);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Slide pure insert/delete blocks made entirely of blank lines to the top of
|
|
27
|
+
* the blank run they sit in. Adding or removing a blank line next to
|
|
28
|
+
* existing blanks is ambiguous, and the diff library reports the change at
|
|
29
|
+
* the run's bottom — so pressing Enter at the end of a line marks a blank
|
|
30
|
+
* *below* the caret as inserted while the caret's own new line renders as
|
|
31
|
+
* context. Sliding up anchors the change to the content above it (the caret
|
|
32
|
+
* line after an Enter) instead.
|
|
33
|
+
*
|
|
34
|
+
* The slide is all-or-nothing: it only applies when the block comes to rest
|
|
35
|
+
* directly beneath remaining in-hunk content. A slide that would consume the
|
|
36
|
+
* hunk's entire leading context was stopped by the hunk's edge — a context
|
|
37
|
+
* window cut, not the top of the blank run — and that landing spot is
|
|
38
|
+
* arbitrary, so the block keeps the library's bottom-of-run anchor (which
|
|
39
|
+
* sits against the content below the run). Non-blank blocks never slide, so
|
|
40
|
+
* code that merely ends like its neighbor (an added function before an
|
|
41
|
+
* identical `}`) keeps the library's canonical position.
|
|
42
|
+
*/
|
|
43
|
+
function slideBlankBoundaryBlocksUp(hunk, diff) {
|
|
44
|
+
const { hunkContent } = hunk;
|
|
45
|
+
for (let index = 1; index < hunkContent.length; index++) {
|
|
46
|
+
const block = hunkContent[index];
|
|
47
|
+
const previous = hunkContent[index - 1];
|
|
48
|
+
if (block.type !== "change" || block.additions > 0 && block.deletions > 0 || previous.type !== "context") continue;
|
|
49
|
+
const isInsert = block.additions > 0;
|
|
50
|
+
const lines = isInsert ? diff.additionLines : diff.deletionLines;
|
|
51
|
+
const blockStart = isInsert ? block.additionLineIndex : block.deletionLineIndex;
|
|
52
|
+
const blockLength = isInsert ? block.additions : block.deletions;
|
|
53
|
+
const unit = lines[blockStart] ?? "";
|
|
54
|
+
if (unit.trim() !== "") continue;
|
|
55
|
+
let uniform = true;
|
|
56
|
+
for (let offset = 1; offset < blockLength; offset++) if (lines[blockStart + offset] !== unit) {
|
|
57
|
+
uniform = false;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (!uniform) continue;
|
|
61
|
+
let slide = 0;
|
|
62
|
+
while (slide < previous.lines && diff.additionLines[previous.additionLineIndex + previous.lines - 1 - slide] === unit) slide++;
|
|
63
|
+
if (slide === 0) continue;
|
|
64
|
+
if (index === 1 && slide === previous.lines) continue;
|
|
65
|
+
block.additionLineIndex -= slide;
|
|
66
|
+
block.deletionLineIndex -= slide;
|
|
67
|
+
const blockAdditionEnd = block.additionLineIndex + block.additions;
|
|
68
|
+
const blockDeletionEnd = block.deletionLineIndex + block.deletions;
|
|
69
|
+
const next = hunkContent[index + 1];
|
|
70
|
+
if (next?.type === "context") {
|
|
71
|
+
next.lines += slide;
|
|
72
|
+
next.additionLineIndex = blockAdditionEnd;
|
|
73
|
+
next.deletionLineIndex = blockDeletionEnd;
|
|
74
|
+
} else hunkContent.splice(index + 1, 0, {
|
|
75
|
+
type: "context",
|
|
76
|
+
lines: slide,
|
|
77
|
+
additionLineIndex: blockAdditionEnd,
|
|
78
|
+
deletionLineIndex: blockDeletionEnd
|
|
79
|
+
});
|
|
80
|
+
previous.lines -= slide;
|
|
81
|
+
if (previous.lines === 0) {
|
|
82
|
+
hunkContent.splice(index - 1, 1);
|
|
83
|
+
index--;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function realignChangeBlock(diff, content) {
|
|
88
|
+
const { deletions, additions, deletionLineIndex, additionLineIndex } = content;
|
|
89
|
+
const pairCount = Math.min(deletions, additions);
|
|
90
|
+
const surplus = Math.abs(additions - deletions);
|
|
91
|
+
if (pairCount === 0 || surplus === 0 || pairCount * (surplus + 1) > MAX_ALIGNMENT_COMPARISONS) return null;
|
|
92
|
+
const strippedDeletions = [];
|
|
93
|
+
for (let line = 0; line < deletions; line++) strippedDeletions.push(stripWhitespace(diff.deletionLines[deletionLineIndex + line] ?? ""));
|
|
94
|
+
const strippedAdditions = [];
|
|
95
|
+
for (let line = 0; line < additions; line++) strippedAdditions.push(stripWhitespace(diff.additionLines[additionLineIndex + line] ?? ""));
|
|
96
|
+
const additionsAreLonger = additions > deletions;
|
|
97
|
+
let bestOffset = 0;
|
|
98
|
+
let bestScore = -1;
|
|
99
|
+
for (let offset = 0; offset <= surplus; offset++) {
|
|
100
|
+
let score = 0;
|
|
101
|
+
for (let pair = 0; pair < pairCount; pair++) score += lineSimilarity(strippedDeletions[pair + (additionsAreLonger ? 0 : offset)], strippedAdditions[pair + (additionsAreLonger ? offset : 0)]);
|
|
102
|
+
if (offset === 0) bestScore = score + pairCount * MIN_IMPROVEMENT_PER_PAIR;
|
|
103
|
+
else if (score > bestScore) {
|
|
104
|
+
bestScore = score;
|
|
105
|
+
bestOffset = offset;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (bestOffset === 0) return null;
|
|
109
|
+
const blocks = [];
|
|
110
|
+
const pushBlock = (blockDeletions, blockAdditions, blockDeletionIndex, blockAdditionIndex) => {
|
|
111
|
+
if (blockDeletions > 0 || blockAdditions > 0) blocks.push({
|
|
112
|
+
type: "change",
|
|
113
|
+
deletions: blockDeletions,
|
|
114
|
+
additions: blockAdditions,
|
|
115
|
+
deletionLineIndex: blockDeletionIndex,
|
|
116
|
+
additionLineIndex: blockAdditionIndex
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
if (additionsAreLonger) {
|
|
120
|
+
pushBlock(0, bestOffset, deletionLineIndex, additionLineIndex);
|
|
121
|
+
pushBlock(pairCount, pairCount, deletionLineIndex, additionLineIndex + bestOffset);
|
|
122
|
+
pushBlock(0, additions - pairCount - bestOffset, deletionLineIndex + pairCount, additionLineIndex + bestOffset + pairCount);
|
|
123
|
+
} else {
|
|
124
|
+
pushBlock(bestOffset, 0, deletionLineIndex, additionLineIndex);
|
|
125
|
+
pushBlock(pairCount, pairCount, deletionLineIndex + bestOffset, additionLineIndex);
|
|
126
|
+
pushBlock(deletions - pairCount - bestOffset, 0, deletionLineIndex + bestOffset + pairCount, additionLineIndex + pairCount);
|
|
127
|
+
}
|
|
128
|
+
return blocks;
|
|
129
|
+
}
|
|
130
|
+
const WHITESPACE = /\s+/g;
|
|
131
|
+
function stripWhitespace(line) {
|
|
132
|
+
return line.replace(WHITESPACE, "");
|
|
133
|
+
}
|
|
134
|
+
function lineSimilarity(a, b) {
|
|
135
|
+
if (a === b) return 1;
|
|
136
|
+
const maxLength = Math.max(a.length, b.length);
|
|
137
|
+
const minLength = Math.min(a.length, b.length);
|
|
138
|
+
if (minLength === 0) return 0;
|
|
139
|
+
let prefix = 0;
|
|
140
|
+
while (prefix < minLength && a[prefix] === b[prefix]) prefix++;
|
|
141
|
+
let suffix = 0;
|
|
142
|
+
while (suffix < minLength - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix]) suffix++;
|
|
143
|
+
return (prefix + suffix) / maxLength;
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
export { realignChangeContentBySimilarity, slideBlankBoundaryBlocksUp };
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=realignChangeContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realignChangeContent.js","names":[],"sources":["../../src/utils/realignChangeContent.ts"],"sourcesContent":["import type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\n\n// The diff library emits one change block per replaced run, ordering every\n// deleted line before every added line. Renderers pair a block's lines\n// positionally (deletion[i] across from addition[i] in split view), so a\n// block like { deletions: 1, additions: 2 } pairs the deleted line with\n// whichever addition happens to come first — even when a later addition is\n// the edited version of it (e.g. pressing Enter above a changed line pushes\n// a blank line in front of it). These helpers re-split such blocks so the\n// most similar lines pair up and the surplus renders as pure insert/delete\n// rows at the block's edges.\n\n// Skip realignment when a block would need more than this many line\n// comparisons; pathological blocks keep the library's positional pairing.\nconst MAX_ALIGNMENT_COMPARISONS = 4096;\n\n// A shifted pairing must beat the positional one by this much per paired\n// line before the block is re-split. Near-ties (e.g. lines that merely share\n// an import-statement shape) keep the library's canonical order; the\n// realignment is meant for decisive wins like a blank or unrelated inserted\n// line displacing an edited one, where the gap approaches 1.\nconst MIN_IMPROVEMENT_PER_PAIR = 0.5;\n\n/**\n * Re-split count-mismatched change blocks in every hunk so paired lines are\n * chosen by content similarity instead of position, then slide blank-line\n * insert/delete blocks to the top of their blank run. Mutates `hunks` in\n * place; rendered row counts are unchanged (a split block covers the same\n * split/unified rows as the original).\n */\nexport function realignChangeContentBySimilarity(\n diff: Pick<FileDiffMetadata, 'hunks' | 'additionLines' | 'deletionLines'>\n): void {\n for (const hunk of diff.hunks) {\n for (let index = 0; index < hunk.hunkContent.length; index++) {\n const content = hunk.hunkContent[index];\n if (content.type !== 'change') {\n continue;\n }\n const replacement = realignChangeBlock(diff, content);\n if (replacement != null) {\n hunk.hunkContent.splice(index, 1, ...replacement);\n index += replacement.length - 1;\n }\n }\n slideBlankBoundaryBlocksUp(hunk, diff);\n }\n}\n\n/**\n * Slide pure insert/delete blocks made entirely of blank lines to the top of\n * the blank run they sit in. Adding or removing a blank line next to\n * existing blanks is ambiguous, and the diff library reports the change at\n * the run's bottom — so pressing Enter at the end of a line marks a blank\n * *below* the caret as inserted while the caret's own new line renders as\n * context. Sliding up anchors the change to the content above it (the caret\n * line after an Enter) instead.\n *\n * The slide is all-or-nothing: it only applies when the block comes to rest\n * directly beneath remaining in-hunk content. A slide that would consume the\n * hunk's entire leading context was stopped by the hunk's edge — a context\n * window cut, not the top of the blank run — and that landing spot is\n * arbitrary, so the block keeps the library's bottom-of-run anchor (which\n * sits against the content below the run). Non-blank blocks never slide, so\n * code that merely ends like its neighbor (an added function before an\n * identical `}`) keeps the library's canonical position.\n */\nexport function slideBlankBoundaryBlocksUp(\n hunk: Hunk,\n diff: Pick<FileDiffMetadata, 'additionLines' | 'deletionLines'>\n): void {\n const { hunkContent } = hunk;\n for (let index = 1; index < hunkContent.length; index++) {\n const block = hunkContent[index];\n const previous = hunkContent[index - 1];\n if (\n block.type !== 'change' ||\n (block.additions > 0 && block.deletions > 0) ||\n previous.type !== 'context'\n ) {\n continue;\n }\n const isInsert = block.additions > 0;\n const lines = isInsert ? diff.additionLines : diff.deletionLines;\n const blockStart = isInsert\n ? block.additionLineIndex\n : block.deletionLineIndex;\n const blockLength = isInsert ? block.additions : block.deletions;\n\n // Sliding through identical lines is only well-defined when the block is\n // a uniform run; require every block line to equal the first one, and\n // that line to be blank.\n const unit = lines[blockStart] ?? '';\n if (unit.trim() !== '') {\n continue;\n }\n let uniform = true;\n for (let offset = 1; offset < blockLength; offset++) {\n if (lines[blockStart + offset] !== unit) {\n uniform = false;\n break;\n }\n }\n if (!uniform) {\n continue;\n }\n\n // Slide distance: how many trailing context lines match the block's line\n // exactly (context lines are identical on both sides by definition).\n let slide = 0;\n while (\n slide < previous.lines &&\n diff.additionLines[\n previous.additionLineIndex + previous.lines - 1 - slide\n ] === unit\n ) {\n slide++;\n }\n if (slide === 0) {\n continue;\n }\n // Stopped by the hunk's leading edge rather than by content: the blank\n // run extends to (and possibly beyond) the hunk's top, so there is no\n // in-hunk line to anchor beneath. Keep the bottom-of-run position.\n if (index === 1 && slide === previous.lines) {\n continue;\n }\n\n block.additionLineIndex -= slide;\n block.deletionLineIndex -= slide;\n const blockAdditionEnd = block.additionLineIndex + block.additions;\n const blockDeletionEnd = block.deletionLineIndex + block.deletions;\n const next = hunkContent[index + 1];\n if (next?.type === 'context') {\n next.lines += slide;\n next.additionLineIndex = blockAdditionEnd;\n next.deletionLineIndex = blockDeletionEnd;\n } else {\n hunkContent.splice(index + 1, 0, {\n type: 'context',\n lines: slide,\n additionLineIndex: blockAdditionEnd,\n deletionLineIndex: blockDeletionEnd,\n });\n }\n previous.lines -= slide;\n if (previous.lines === 0) {\n hunkContent.splice(index - 1, 1);\n index--;\n }\n }\n}\n\n// Returns the split blocks for one change block, or null when the block is\n// balanced, too large to scan, or already best paired positionally.\nfunction realignChangeBlock(\n diff: Pick<FileDiffMetadata, 'additionLines' | 'deletionLines'>,\n content: ChangeContent\n): (ContextContent | ChangeContent)[] | null {\n const { deletions, additions, deletionLineIndex, additionLineIndex } =\n content;\n const pairCount = Math.min(deletions, additions);\n const surplus = Math.abs(additions - deletions);\n if (\n pairCount === 0 ||\n surplus === 0 ||\n pairCount * (surplus + 1) > MAX_ALIGNMENT_COMPARISONS\n ) {\n return null;\n }\n\n // Whitespace (indentation, formatter churn, trailing spaces, the line\n // break itself) is noise for deciding which lines pair — the rendered diff\n // still shows every whitespace change on the paired row. Strip it once per\n // line here rather than per comparison.\n const strippedDeletions: string[] = [];\n for (let line = 0; line < deletions; line++) {\n strippedDeletions.push(\n stripWhitespace(diff.deletionLines[deletionLineIndex + line] ?? '')\n );\n }\n const strippedAdditions: string[] = [];\n for (let line = 0; line < additions; line++) {\n strippedAdditions.push(\n stripWhitespace(diff.additionLines[additionLineIndex + line] ?? '')\n );\n }\n\n // Score every offset of the shorter side along the longer side and keep\n // the best one only when it decisively beats the positional pairing.\n const additionsAreLonger = additions > deletions;\n let bestOffset = 0;\n let bestScore = -1;\n for (let offset = 0; offset <= surplus; offset++) {\n let score = 0;\n for (let pair = 0; pair < pairCount; pair++) {\n score += lineSimilarity(\n strippedDeletions[pair + (additionsAreLonger ? 0 : offset)],\n strippedAdditions[pair + (additionsAreLonger ? offset : 0)]\n );\n }\n if (offset === 0) {\n bestScore = score + pairCount * MIN_IMPROVEMENT_PER_PAIR;\n } else if (score > bestScore) {\n bestScore = score;\n bestOffset = offset;\n }\n }\n if (bestOffset === 0) {\n return null;\n }\n\n const blocks: ChangeContent[] = [];\n const pushBlock = (\n blockDeletions: number,\n blockAdditions: number,\n blockDeletionIndex: number,\n blockAdditionIndex: number\n ) => {\n if (blockDeletions > 0 || blockAdditions > 0) {\n blocks.push({\n type: 'change',\n deletions: blockDeletions,\n additions: blockAdditions,\n deletionLineIndex: blockDeletionIndex,\n additionLineIndex: blockAdditionIndex,\n });\n }\n };\n if (additionsAreLonger) {\n pushBlock(0, bestOffset, deletionLineIndex, additionLineIndex);\n pushBlock(\n pairCount,\n pairCount,\n deletionLineIndex,\n additionLineIndex + bestOffset\n );\n pushBlock(\n 0,\n additions - pairCount - bestOffset,\n deletionLineIndex + pairCount,\n additionLineIndex + bestOffset + pairCount\n );\n } else {\n pushBlock(bestOffset, 0, deletionLineIndex, additionLineIndex);\n pushBlock(\n pairCount,\n pairCount,\n deletionLineIndex + bestOffset,\n additionLineIndex\n );\n pushBlock(\n deletions - pairCount - bestOffset,\n 0,\n deletionLineIndex + bestOffset + pairCount,\n additionLineIndex + pairCount\n );\n }\n return blocks;\n}\n\nconst WHITESPACE = /\\s+/g;\n\nfunction stripWhitespace(line: string): string {\n return line.replace(WHITESPACE, '');\n}\n\n// Cheap 0..1 similarity over whitespace-stripped lines: shared prefix plus\n// shared suffix over the longer length. Exact for lines that differ only in\n// whitespace, 0 for a blank against content — enough to steer pairing\n// without a real edit-distance pass.\nfunction lineSimilarity(a: string, b: string): number {\n if (a === b) {\n return 1;\n }\n const maxLength = Math.max(a.length, b.length);\n const minLength = Math.min(a.length, b.length);\n if (minLength === 0) {\n return 0;\n }\n let prefix = 0;\n while (prefix < minLength && a[prefix] === b[prefix]) {\n prefix++;\n }\n let suffix = 0;\n while (\n suffix < minLength - prefix &&\n a[a.length - 1 - suffix] === b[b.length - 1 - suffix]\n ) {\n suffix++;\n }\n return (prefix + suffix) / maxLength;\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_THEMES } from "../constants.js";
|
|
2
|
+
import { cleanLastNewline } from "./cleanLastNewline.js";
|
|
2
3
|
import { getFiletypeFromFileName } from "./getFiletypeFromFileName.js";
|
|
3
4
|
import { createTransformerWithState } from "./createTransformerWithState.js";
|
|
4
5
|
import { formatCSSVariablePrefix } from "./formatCSSVariablePrefix.js";
|
|
5
6
|
import { getHighlighterThemeStyles } from "./getHighlighterThemeStyles.js";
|
|
6
7
|
import { getLineNodes } from "./getLineNodes.js";
|
|
7
8
|
import { iterateOverDiff } from "./iterateOverDiff.js";
|
|
8
|
-
import { cleanLastNewline } from "./cleanLastNewline.js";
|
|
9
9
|
import { createDiffSpanDecoration, pushOrJoinSpan } from "./parseDiffDecorations.js";
|
|
10
10
|
import { diffChars, diffWordsWithSpace } from "diff";
|
|
11
11
|
//#region src/utils/renderDiffWithHighlighter.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveRegion.d.ts","names":[],"sources":["../../src/utils/resolveRegion.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"resolveRegion.d.ts","names":[],"sources":["../../src/utils/resolveRegion.ts"],"mappings":";;;UAWU,sBAAA;EACR,SAAA;EACA,iBAAA;EACA,eAAA;EACA,UAAA;EACA,eAAA,GAAkB,GAAG;AAAA;AAAA,iBAYP,aAAA,CACd,IAAA,EAAM,gBAAA,EACN,MAAA,EAAQ,sBAAA,GACP,gBAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getHunkSideEndBoundary, getHunkSideStartBoundary } from "./getHunkSideBoundaries.js";
|
|
1
2
|
//#region src/utils/resolveRegion.ts
|
|
2
3
|
function resolveRegion(diff, target) {
|
|
3
4
|
const { resolution, hunkIndex, startContentIndex, endContentIndex, indexesToDelete = /* @__PURE__ */ new Set() } = target;
|
|
@@ -31,7 +32,7 @@ function resolveRegion(diff, target) {
|
|
|
31
32
|
const updatesEOFState = hunkIndex === hunks.length - 1 && endContentIndex === currentHunk.hunkContent.length - 1;
|
|
32
33
|
const shouldProcessCollapsedContext = !diff.isPartial;
|
|
33
34
|
for (const [index, hunk] of hunks.entries()) {
|
|
34
|
-
processCollapsedContext(diff, resolvedDiff, cursor, hunk.
|
|
35
|
+
processCollapsedContext(diff, resolvedDiff, cursor, getHunkSideStartBoundary(hunk.deletionStart, hunk.deletionCount) - hunk.collapsedBefore, getHunkSideStartBoundary(hunk.additionStart, hunk.additionCount) - hunk.collapsedBefore, hunk.collapsedBefore, shouldProcessCollapsedContext);
|
|
35
36
|
const newHunk = {
|
|
36
37
|
...hunk,
|
|
37
38
|
hunkContent: [],
|
|
@@ -88,10 +89,25 @@ function resolveRegion(diff, target) {
|
|
|
88
89
|
newHunk.noEOFCRAdditions = noEOFCR;
|
|
89
90
|
newHunk.noEOFCRDeletions = noEOFCR;
|
|
90
91
|
}
|
|
92
|
+
if (newHunk.additionCount === 0) {
|
|
93
|
+
newHunk.additionStart--;
|
|
94
|
+
if (!diff.isPartial) newHunk.additionLineIndex--;
|
|
95
|
+
}
|
|
96
|
+
if (newHunk.deletionCount === 0) {
|
|
97
|
+
newHunk.deletionStart--;
|
|
98
|
+
if (!diff.isPartial) newHunk.deletionLineIndex--;
|
|
99
|
+
}
|
|
91
100
|
resolvedDiff.hunks.push(newHunk);
|
|
92
101
|
}
|
|
93
102
|
const finalHunk = hunks.at(-1);
|
|
94
|
-
if (finalHunk != null && !diff.isPartial)
|
|
103
|
+
if (finalHunk != null && !diff.isPartial) {
|
|
104
|
+
const finalDeletionEnd = getHunkSideEndBoundary(finalHunk.deletionStart, finalHunk.deletionCount);
|
|
105
|
+
const finalAdditionEnd = getHunkSideEndBoundary(finalHunk.additionStart, finalHunk.additionCount);
|
|
106
|
+
const trailingContext = Math.min(deletionLines.length - finalDeletionEnd, additionLines.length - finalAdditionEnd);
|
|
107
|
+
pushCollapsedContextLines(resolvedDiff, deletionLines, additionLines, finalDeletionEnd, finalAdditionEnd, trailingContext);
|
|
108
|
+
cursor.splitLineCount += trailingContext;
|
|
109
|
+
cursor.unifiedLineCount += trailingContext;
|
|
110
|
+
}
|
|
95
111
|
resolvedDiff.splitLineCount = cursor.splitLineCount;
|
|
96
112
|
resolvedDiff.unifiedLineCount = cursor.unifiedLineCount;
|
|
97
113
|
return resolvedDiff;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveRegion.js","names":[],"sources":["../../src/utils/resolveRegion.ts"],"sourcesContent":["import type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\n\ninterface RegionResolutionTarget {\n hunkIndex: number;\n startContentIndex: number;\n endContentIndex: number;\n resolution: 'deletions' | 'additions' | 'both';\n indexesToDelete?: Set<number>;\n}\n\ninterface CursorState {\n nextAdditionLineIndex: number;\n nextDeletionLineIndex: number;\n nextAdditionStart: number;\n nextDeletionStart: number;\n splitLineCount: number;\n unifiedLineCount: number;\n}\n\nexport function resolveRegion(\n diff: FileDiffMetadata,\n target: RegionResolutionTarget\n): FileDiffMetadata {\n const {\n resolution,\n hunkIndex,\n startContentIndex,\n endContentIndex,\n indexesToDelete = new Set(),\n } = target;\n const currentHunk = diff.hunks[hunkIndex];\n if (currentHunk == null) {\n console.error({ diff, hunkIndex });\n throw new Error(`resolveRegion: Invalid hunk index: ${hunkIndex}`);\n }\n\n if (\n startContentIndex < 0 ||\n endContentIndex >= currentHunk.hunkContent.length ||\n startContentIndex > endContentIndex\n ) {\n throw new Error(\n `resolveRegion: Invalid content range, ${startContentIndex}, ${endContentIndex}`\n );\n }\n\n const { hunks, additionLines, deletionLines } = diff;\n const resolvedDiff: FileDiffMetadata = {\n ...diff,\n hunks: [],\n deletionLines: [],\n additionLines: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n cacheKey:\n diff.cacheKey != null\n ? `${diff.cacheKey}:${resolution[0]}-${hunkIndex}:${startContentIndex}-${endContentIndex}`\n : undefined,\n };\n\n const cursor: CursorState = {\n nextAdditionLineIndex: 0,\n nextDeletionLineIndex: 0,\n nextAdditionStart: 1,\n nextDeletionStart: 1,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n const updatesEOFState =\n hunkIndex === hunks.length - 1 &&\n endContentIndex === currentHunk.hunkContent.length - 1;\n const shouldProcessCollapsedContext = !diff.isPartial;\n\n for (const [index, hunk] of hunks.entries()) {\n processCollapsedContext(\n diff,\n resolvedDiff,\n cursor,\n hunk.deletionLineIndex - hunk.collapsedBefore,\n hunk.additionLineIndex - hunk.collapsedBefore,\n hunk.collapsedBefore,\n shouldProcessCollapsedContext\n );\n\n const newHunk: Hunk = {\n ...hunk,\n hunkContent: [],\n additionStart: cursor.nextAdditionStart,\n deletionStart: cursor.nextDeletionStart,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionCount: 0,\n deletionCount: 0,\n deletionLines: 0,\n additionLines: 0,\n splitLineStart: cursor.splitLineCount,\n unifiedLineStart: cursor.unifiedLineCount,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n\n for (const [contentIndex, content] of hunk.hunkContent.entries()) {\n // If we are outside of the targeted hunk or content region\n if (\n index !== hunkIndex ||\n contentIndex < startContentIndex ||\n contentIndex > endContentIndex\n ) {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent = {\n ...content,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // If we are at an index to delete, replace with an empty context node\n else if (indexesToDelete.has(contentIndex)) {\n newHunk.hunkContent.push({\n type: 'context',\n lines: 0,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n });\n }\n // There's nothing to `resolve` with context nodes, so just push them as\n // they are\n else if (content.type === 'context') {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n ...content,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // Looks like we have a change to resolve and push\n else {\n pushResolveLinesToDiff(\n resolution,\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n type: 'context',\n lines:\n resolution === 'deletions'\n ? content.deletions\n : resolution === 'additions'\n ? content.additions\n : content.deletions + content.additions,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n }\n\n if (index === hunkIndex && updatesEOFState) {\n const noEOFCR =\n resolution === 'deletions'\n ? hunk.noEOFCRDeletions\n : hunk.noEOFCRAdditions;\n newHunk.noEOFCRAdditions = noEOFCR;\n newHunk.noEOFCRDeletions = noEOFCR;\n }\n\n resolvedDiff.hunks.push(newHunk);\n }\n\n const finalHunk = hunks.at(-1);\n if (finalHunk != null && !diff.isPartial) {\n pushCollapsedContextLines(\n resolvedDiff,\n deletionLines,\n additionLines,\n finalHunk.deletionLineIndex + finalHunk.deletionCount,\n finalHunk.additionLineIndex + finalHunk.additionCount,\n Math.min(\n deletionLines.length -\n (finalHunk.deletionLineIndex + finalHunk.deletionCount),\n additionLines.length -\n (finalHunk.additionLineIndex + finalHunk.additionCount)\n )\n );\n }\n\n resolvedDiff.splitLineCount = cursor.splitLineCount;\n resolvedDiff.unifiedLineCount = cursor.unifiedLineCount;\n\n return resolvedDiff;\n}\n\nfunction pushCollapsedContextLines(\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[],\n deletionLineIndex: number,\n additionLineIndex: number,\n lineCount: number\n) {\n for (let index = 0; index < lineCount; index++) {\n const deletionLine = deletionLines[deletionLineIndex + index];\n const additionLine = additionLines[additionLineIndex + index];\n if (deletionLine == null || additionLine == null) {\n throw new Error(\n 'pushCollapsedContextLines: missing collapsed context line'\n );\n }\n diff.deletionLines.push(deletionLine);\n diff.additionLines.push(additionLine);\n }\n}\n\n// Partial patches track omitted context in `collapsedBefore`, but those lines do\n// not exist in the diff's line arrays. Keep the virtual row counts and file\n// positions in sync without inventing hidden lines.\nfunction processCollapsedContext(\n sourceDiff: FileDiffMetadata,\n resolvedDiff: FileDiffMetadata,\n cursor: CursorState,\n deletionLineIndex: number,\n additionLineIndex: number,\n lineCount: number,\n shouldProcessContent: boolean\n) {\n if (lineCount <= 0) {\n return;\n }\n\n if (shouldProcessContent) {\n pushCollapsedContextLines(\n resolvedDiff,\n sourceDiff.deletionLines,\n sourceDiff.additionLines,\n deletionLineIndex,\n additionLineIndex,\n lineCount\n );\n cursor.nextAdditionLineIndex += lineCount;\n cursor.nextDeletionLineIndex += lineCount;\n }\n\n cursor.nextAdditionStart += lineCount;\n cursor.nextDeletionStart += lineCount;\n cursor.splitLineCount += lineCount;\n cursor.unifiedLineCount += lineCount;\n}\n\nfunction pushContentLinesToDiff(\n content: ContextContent | ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (content.type === 'context') {\n for (let i = 0; i < content.lines; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushContentLinesToDiff: Context line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n } else {\n const len = Math.max(content.deletions, content.additions);\n for (let i = 0; i < len; i++) {\n if (i < content.deletions) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Deletion line does not exist'\n );\n }\n diff.deletionLines.push(line);\n }\n if (i < content.additions) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Addition line does not exist'\n );\n }\n diff.additionLines.push(line);\n }\n }\n }\n}\n\nfunction pushResolveLinesToDiff(\n resolution: 'deletions' | 'additions' | 'both',\n content: ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (resolution === 'deletions' || resolution === 'both') {\n for (let i = 0; i < content.deletions; i++) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Deletion line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n if (resolution === 'additions' || resolution === 'both') {\n for (let i = 0; i < content.additions; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Addition line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n}\n\nfunction advanceCursor(\n content: ChangeContent | ContextContent,\n cursor: CursorState,\n hunk: Hunk\n) {\n if (content.type === 'context') {\n cursor.nextAdditionLineIndex += content.lines;\n cursor.nextDeletionLineIndex += content.lines;\n cursor.nextAdditionStart += content.lines;\n cursor.nextDeletionStart += content.lines;\n cursor.splitLineCount += content.lines;\n cursor.unifiedLineCount += content.lines;\n\n hunk.additionCount += content.lines;\n hunk.deletionCount += content.lines;\n hunk.splitLineCount += content.lines;\n hunk.unifiedLineCount += content.lines;\n } else {\n cursor.nextAdditionLineIndex += content.additions;\n cursor.nextDeletionLineIndex += content.deletions;\n cursor.nextAdditionStart += content.additions;\n cursor.nextDeletionStart += content.deletions;\n cursor.splitLineCount += Math.max(content.deletions, content.additions);\n cursor.unifiedLineCount += content.deletions + content.additions;\n\n hunk.deletionCount += content.deletions;\n hunk.deletionLines += content.deletions;\n hunk.additionCount += content.additions;\n hunk.additionLines += content.additions;\n hunk.splitLineCount += Math.max(content.deletions, content.additions);\n hunk.unifiedLineCount += content.deletions + content.additions;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolveRegion.js","names":[],"sources":["../../src/utils/resolveRegion.ts"],"sourcesContent":["import type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\nimport {\n getHunkSideEndBoundary,\n getHunkSideStartBoundary,\n} from './getHunkSideBoundaries';\n\ninterface RegionResolutionTarget {\n hunkIndex: number;\n startContentIndex: number;\n endContentIndex: number;\n resolution: 'deletions' | 'additions' | 'both';\n indexesToDelete?: Set<number>;\n}\n\ninterface CursorState {\n nextAdditionLineIndex: number;\n nextDeletionLineIndex: number;\n nextAdditionStart: number;\n nextDeletionStart: number;\n splitLineCount: number;\n unifiedLineCount: number;\n}\n\nexport function resolveRegion(\n diff: FileDiffMetadata,\n target: RegionResolutionTarget\n): FileDiffMetadata {\n const {\n resolution,\n hunkIndex,\n startContentIndex,\n endContentIndex,\n indexesToDelete = new Set(),\n } = target;\n const currentHunk = diff.hunks[hunkIndex];\n if (currentHunk == null) {\n console.error({ diff, hunkIndex });\n throw new Error(`resolveRegion: Invalid hunk index: ${hunkIndex}`);\n }\n\n if (\n startContentIndex < 0 ||\n endContentIndex >= currentHunk.hunkContent.length ||\n startContentIndex > endContentIndex\n ) {\n throw new Error(\n `resolveRegion: Invalid content range, ${startContentIndex}, ${endContentIndex}`\n );\n }\n\n const { hunks, additionLines, deletionLines } = diff;\n const resolvedDiff: FileDiffMetadata = {\n ...diff,\n hunks: [],\n deletionLines: [],\n additionLines: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n cacheKey:\n diff.cacheKey != null\n ? `${diff.cacheKey}:${resolution[0]}-${hunkIndex}:${startContentIndex}-${endContentIndex}`\n : undefined,\n };\n\n const cursor: CursorState = {\n nextAdditionLineIndex: 0,\n nextDeletionLineIndex: 0,\n nextAdditionStart: 1,\n nextDeletionStart: 1,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n const updatesEOFState =\n hunkIndex === hunks.length - 1 &&\n endContentIndex === currentHunk.hunkContent.length - 1;\n const shouldProcessCollapsedContext = !diff.isPartial;\n\n for (const [index, hunk] of hunks.entries()) {\n processCollapsedContext(\n diff,\n resolvedDiff,\n cursor,\n getHunkSideStartBoundary(hunk.deletionStart, hunk.deletionCount) -\n hunk.collapsedBefore,\n getHunkSideStartBoundary(hunk.additionStart, hunk.additionCount) -\n hunk.collapsedBefore,\n hunk.collapsedBefore,\n shouldProcessCollapsedContext\n );\n\n const newHunk: Hunk = {\n ...hunk,\n hunkContent: [],\n additionStart: cursor.nextAdditionStart,\n deletionStart: cursor.nextDeletionStart,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionCount: 0,\n deletionCount: 0,\n deletionLines: 0,\n additionLines: 0,\n splitLineStart: cursor.splitLineCount,\n unifiedLineStart: cursor.unifiedLineCount,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n\n for (const [contentIndex, content] of hunk.hunkContent.entries()) {\n // If we are outside of the targeted hunk or content region\n if (\n index !== hunkIndex ||\n contentIndex < startContentIndex ||\n contentIndex > endContentIndex\n ) {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent = {\n ...content,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // If we are at an index to delete, replace with an empty context node\n else if (indexesToDelete.has(contentIndex)) {\n newHunk.hunkContent.push({\n type: 'context',\n lines: 0,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n });\n }\n // There's nothing to `resolve` with context nodes, so just push them as\n // they are\n else if (content.type === 'context') {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n ...content,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // Looks like we have a change to resolve and push\n else {\n pushResolveLinesToDiff(\n resolution,\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n type: 'context',\n lines:\n resolution === 'deletions'\n ? content.deletions\n : resolution === 'additions'\n ? content.additions\n : content.deletions + content.additions,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n }\n\n if (index === hunkIndex && updatesEOFState) {\n const noEOFCR =\n resolution === 'deletions'\n ? hunk.noEOFCRDeletions\n : hunk.noEOFCRAdditions;\n newHunk.noEOFCRAdditions = noEOFCR;\n newHunk.noEOFCRDeletions = noEOFCR;\n }\n\n if (newHunk.additionCount === 0) {\n newHunk.additionStart--;\n if (!diff.isPartial) {\n newHunk.additionLineIndex--;\n }\n }\n if (newHunk.deletionCount === 0) {\n newHunk.deletionStart--;\n if (!diff.isPartial) {\n newHunk.deletionLineIndex--;\n }\n }\n\n resolvedDiff.hunks.push(newHunk);\n }\n\n const finalHunk = hunks.at(-1);\n if (finalHunk != null && !diff.isPartial) {\n const finalDeletionEnd = getHunkSideEndBoundary(\n finalHunk.deletionStart,\n finalHunk.deletionCount\n );\n const finalAdditionEnd = getHunkSideEndBoundary(\n finalHunk.additionStart,\n finalHunk.additionCount\n );\n const trailingContext = Math.min(\n deletionLines.length - finalDeletionEnd,\n additionLines.length - finalAdditionEnd\n );\n pushCollapsedContextLines(\n resolvedDiff,\n deletionLines,\n additionLines,\n finalDeletionEnd,\n finalAdditionEnd,\n trailingContext\n );\n cursor.splitLineCount += trailingContext;\n cursor.unifiedLineCount += trailingContext;\n }\n\n resolvedDiff.splitLineCount = cursor.splitLineCount;\n resolvedDiff.unifiedLineCount = cursor.unifiedLineCount;\n\n return resolvedDiff;\n}\n\nfunction pushCollapsedContextLines(\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[],\n deletionLineIndex: number,\n additionLineIndex: number,\n lineCount: number\n) {\n for (let index = 0; index < lineCount; index++) {\n const deletionLine = deletionLines[deletionLineIndex + index];\n const additionLine = additionLines[additionLineIndex + index];\n if (deletionLine == null || additionLine == null) {\n throw new Error(\n 'pushCollapsedContextLines: missing collapsed context line'\n );\n }\n diff.deletionLines.push(deletionLine);\n diff.additionLines.push(additionLine);\n }\n}\n\n// Partial patches track omitted context in `collapsedBefore`, but those lines do\n// not exist in the diff's line arrays. Keep the virtual row counts and file\n// positions in sync without inventing hidden lines.\nfunction processCollapsedContext(\n sourceDiff: FileDiffMetadata,\n resolvedDiff: FileDiffMetadata,\n cursor: CursorState,\n deletionLineIndex: number,\n additionLineIndex: number,\n lineCount: number,\n shouldProcessContent: boolean\n) {\n if (lineCount <= 0) {\n return;\n }\n\n if (shouldProcessContent) {\n pushCollapsedContextLines(\n resolvedDiff,\n sourceDiff.deletionLines,\n sourceDiff.additionLines,\n deletionLineIndex,\n additionLineIndex,\n lineCount\n );\n cursor.nextAdditionLineIndex += lineCount;\n cursor.nextDeletionLineIndex += lineCount;\n }\n\n cursor.nextAdditionStart += lineCount;\n cursor.nextDeletionStart += lineCount;\n cursor.splitLineCount += lineCount;\n cursor.unifiedLineCount += lineCount;\n}\n\nfunction pushContentLinesToDiff(\n content: ContextContent | ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (content.type === 'context') {\n for (let i = 0; i < content.lines; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushContentLinesToDiff: Context line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n } else {\n const len = Math.max(content.deletions, content.additions);\n for (let i = 0; i < len; i++) {\n if (i < content.deletions) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Deletion line does not exist'\n );\n }\n diff.deletionLines.push(line);\n }\n if (i < content.additions) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Addition line does not exist'\n );\n }\n diff.additionLines.push(line);\n }\n }\n }\n}\n\nfunction pushResolveLinesToDiff(\n resolution: 'deletions' | 'additions' | 'both',\n content: ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (resolution === 'deletions' || resolution === 'both') {\n for (let i = 0; i < content.deletions; i++) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Deletion line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n if (resolution === 'additions' || resolution === 'both') {\n for (let i = 0; i < content.additions; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Addition line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n}\n\nfunction advanceCursor(\n content: ChangeContent | ContextContent,\n cursor: CursorState,\n hunk: Hunk\n) {\n if (content.type === 'context') {\n cursor.nextAdditionLineIndex += content.lines;\n cursor.nextDeletionLineIndex += content.lines;\n cursor.nextAdditionStart += content.lines;\n cursor.nextDeletionStart += content.lines;\n cursor.splitLineCount += content.lines;\n cursor.unifiedLineCount += content.lines;\n\n hunk.additionCount += content.lines;\n hunk.deletionCount += content.lines;\n hunk.splitLineCount += content.lines;\n hunk.unifiedLineCount += content.lines;\n } else {\n cursor.nextAdditionLineIndex += content.additions;\n cursor.nextDeletionLineIndex += content.deletions;\n cursor.nextAdditionStart += content.additions;\n cursor.nextDeletionStart += content.deletions;\n cursor.splitLineCount += Math.max(content.deletions, content.additions);\n cursor.unifiedLineCount += content.deletions + content.additions;\n\n hunk.deletionCount += content.deletions;\n hunk.deletionLines += content.deletions;\n hunk.additionCount += content.additions;\n hunk.additionLines += content.additions;\n hunk.splitLineCount += Math.max(content.deletions, content.additions);\n hunk.unifiedLineCount += content.deletions + content.additions;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { FileDiffMetadata } from "../types.js";
|
|
1
|
+
import { ChangeContent, ContextContent, FileDiffMetadata, Hunk } from "../types.js";
|
|
2
2
|
import { CreatePatchOptionsNonabortable } from "diff";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/updateDiffHunks.d.ts
|
|
5
|
+
type HunkContent = ContextContent | ChangeContent;
|
|
5
6
|
type HunkMetadataUpdate = Pick<FileDiffMetadata, 'hunks' | 'splitLineCount' | 'unifiedLineCount' | 'type'>;
|
|
6
7
|
type FullDiffHunkUpdate = HunkMetadataUpdate & Pick<FileDiffMetadata, 'additionLines' | 'deletionLines'>;
|
|
7
8
|
/** Rebuilds all hunk metadata from the current deletion/addition line arrays. */
|
|
@@ -11,8 +12,13 @@ declare function recomputeTopAlignedAdditionDiff(diff: FileDiffMetadata, additio
|
|
|
11
12
|
declare function recomputeEmptyDocumentDiff(diff: FileDiffMetadata, parseDiffOptions?: CreatePatchOptionsNonabortable): FullDiffHunkUpdate;
|
|
12
13
|
/** Rebuilds diff hunks after an edit, top-aligning sparse addition sides when needed. */
|
|
13
14
|
declare function recomputeDiffHunksForEdit(diff: FileDiffMetadata, parseDiffOptions?: CreatePatchOptionsNonabortable): FullDiffHunkUpdate;
|
|
15
|
+
declare function preserveTrailingEditorBlankLine(recomputed: Pick<FullDiffHunkUpdate, 'hunks' | 'additionLines' | 'splitLineCount' | 'unifiedLineCount'>, additionLines: string[]): void;
|
|
14
16
|
/** Updates hunk metadata after addition lines change; re-parses affected hunks only. */
|
|
15
17
|
declare function updateDiffHunks(diff: FileDiffMetadata, changedAdditionLineIndexes: Iterable<number>, parseDiffOptions?: CreatePatchOptionsNonabortable): HunkMetadataUpdate;
|
|
18
|
+
declare function syncHunkNoEOFCRFromFullFile(diff: FileDiffMetadata, hunkIndex: number): void;
|
|
19
|
+
declare function offsetHunkContent(content: HunkContent, additionOffset: number, deletionOffset: number): HunkContent;
|
|
20
|
+
declare function recomputeHunkRenderLineCounts(hunk: Hunk): void;
|
|
21
|
+
declare function recomputeDiffRenderLineCounts(diff: Pick<FileDiffMetadata, 'hunks' | 'splitLineCount' | 'unifiedLineCount' | 'additionLines'>): void;
|
|
16
22
|
//#endregion
|
|
17
|
-
export { recomputeDiffHunks, recomputeDiffHunksForEdit, recomputeEmptyDocumentDiff, recomputeTopAlignedAdditionDiff, shouldTopAlignAdditionRecompute, updateDiffHunks };
|
|
23
|
+
export { offsetHunkContent, preserveTrailingEditorBlankLine, recomputeDiffHunks, recomputeDiffHunksForEdit, recomputeDiffRenderLineCounts, recomputeEmptyDocumentDiff, recomputeHunkRenderLineCounts, recomputeTopAlignedAdditionDiff, shouldTopAlignAdditionRecompute, syncHunkNoEOFCRFromFullFile, updateDiffHunks };
|
|
18
24
|
//# sourceMappingURL=updateDiffHunks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateDiffHunks.d.ts","names":[],"sources":["../../src/utils/updateDiffHunks.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"updateDiffHunks.d.ts","names":[],"sources":["../../src/utils/updateDiffHunks.ts"],"mappings":";;;;KAgBK,WAAA,GAAc,cAAA,GAAiB,aAAa;AAAA,KAE5C,kBAAA,GAAqB,IAAI,CAC5B,gBAAA;AAAA,KAIG,kBAAA,GAAqB,kBAAA,GACxB,IAAA,CAAK,gBAAA;;iBAGS,kBAAA,CACd,IAAA,EAAM,gBAAA,EACN,gBAAA,GAAmB,8BAAA,GAClB,kBAAA;AAAA,iBAwDa,+BAAA,CACd,IAAA,EAAM,gBAAgB,EACtB,aAAA;AAAA,iBAYc,+BAAA,CACd,IAAA,EAAM,gBAAA,EACN,aAAA,YACA,gBAAA,GAAmB,8BAAA,GAClB,kBAAA;AAAA,iBAkCa,0BAAA,CACd,IAAA,EAAM,gBAAA,EACN,gBAAA,GAAmB,8BAAA,GAClB,kBAAA;;iBAKa,yBAAA,CACd,IAAA,EAAM,gBAAA,EACN,gBAAA,GAAmB,8BAAA,GAClB,kBAAA;AAAA,iBAwBa,+BAAA,CACd,UAAA,EAAY,IAAI,CACd,kBAAA,sEAGF,aAAA;AA/JgB;AAAA,iBAwMF,eAAA,CACd,IAAA,EAAM,gBAAA,EACN,0BAAA,EAA4B,QAAA,UAC5B,gBAAA,GAAmB,8BAAA,GAClB,kBAAA;AAAA,iBA2Ja,2BAAA,CACd,IAAA,EAAM,gBAAgB,EACtB,SAAA;AAAA,iBAiDc,iBAAA,CACd,OAAA,EAAS,WAAA,EACT,cAAA,UACA,cAAA,WACC,WAAW;AAAA,iBAQE,6BAAA,CAA8B,IAAU,EAAJ,IAAI;AAAA,iBAkBxC,6BAAA,CACd,IAAA,EAAM,IAAI,CACR,gBAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getHunkSideEndBoundary, getHunkSideStartBoundary } from "./getHunkSideBoundaries.js";
|
|
2
2
|
import { cleanLastNewline } from "./cleanLastNewline.js";
|
|
3
3
|
import { parseDiffFromFile } from "./parseDiffFromFile.js";
|
|
4
|
+
import { hasTrailingContextMismatch } from "./virtualDiffLayout.js";
|
|
4
5
|
//#region src/utils/updateDiffHunks.ts
|
|
5
6
|
/** Rebuilds all hunk metadata from the current deletion/addition line arrays. */
|
|
6
7
|
function recomputeDiffHunks(diff, parseDiffOptions) {
|
|
@@ -63,19 +64,20 @@ function recomputeDiffHunksForEdit(diff, parseDiffOptions) {
|
|
|
63
64
|
if (shouldTopAlignAdditionRecompute(diff, diff.additionLines)) return recomputeTopAlignedAdditionDiff(diff, diff.additionLines, parseDiffOptions);
|
|
64
65
|
const additionLines = diff.additionLines;
|
|
65
66
|
const recomputed = recomputeDiffHunks(diff, parseDiffOptions);
|
|
66
|
-
|
|
67
|
+
preserveTrailingEditorBlankLine(recomputed, additionLines);
|
|
67
68
|
return recomputed;
|
|
68
69
|
}
|
|
69
70
|
function hasTrailingEditorBlankLine(additionLines) {
|
|
70
71
|
return additionLines.length > 1 && additionLines.at(-1) === "";
|
|
71
72
|
}
|
|
72
73
|
function preserveTrailingEditorBlankLine(recomputed, additionLines) {
|
|
74
|
+
if (!hasTrailingEditorBlankLine(additionLines)) return;
|
|
73
75
|
const extraLineCount = additionLines.length - recomputed.additionLines.length;
|
|
74
76
|
if (extraLineCount <= 0) return;
|
|
75
77
|
const extraAdditionLineIndex = recomputed.additionLines.length;
|
|
76
78
|
const lastHunk = recomputed.hunks.at(-1);
|
|
77
79
|
if (lastHunk == null) return;
|
|
78
|
-
if (lastHunk.
|
|
80
|
+
if (getHunkSideEndBoundary(lastHunk.additionStart, lastHunk.additionCount) !== extraAdditionLineIndex) return;
|
|
79
81
|
for (const content of lastHunk.hunkContent) if (content.type === "change" && content.additions < content.deletions && content.additionLineIndex + content.additions === extraAdditionLineIndex) {
|
|
80
82
|
recomputed.additionLines = additionLines;
|
|
81
83
|
content.additions += extraLineCount;
|
|
@@ -211,17 +213,17 @@ function recomputeDiffRenderLineCounts(diff) {
|
|
|
211
213
|
let unifiedTotal = 0;
|
|
212
214
|
let lastHunkAdditionEnd = 0;
|
|
213
215
|
for (const hunk of diff.hunks) {
|
|
214
|
-
hunk.collapsedBefore = Math.max(hunk.additionStart
|
|
216
|
+
hunk.collapsedBefore = Math.max(getHunkSideStartBoundary(hunk.additionStart, hunk.additionCount) - lastHunkAdditionEnd, 0);
|
|
215
217
|
hunk.splitLineStart = splitTotal + hunk.collapsedBefore;
|
|
216
218
|
hunk.unifiedLineStart = unifiedTotal + hunk.collapsedBefore;
|
|
217
219
|
recomputeHunkRenderLineCounts(hunk);
|
|
218
220
|
splitTotal += hunk.collapsedBefore + hunk.splitLineCount;
|
|
219
221
|
unifiedTotal += hunk.collapsedBefore + hunk.unifiedLineCount;
|
|
220
|
-
lastHunkAdditionEnd = hunk.additionStart
|
|
222
|
+
lastHunkAdditionEnd = getHunkSideEndBoundary(hunk.additionStart, hunk.additionCount);
|
|
221
223
|
}
|
|
222
224
|
if (diff.hunks.length > 0) {
|
|
223
225
|
const lastHunk = diff.hunks[diff.hunks.length - 1];
|
|
224
|
-
const collapsedAfter = Math.max(diff.additionLines.length - (lastHunk.
|
|
226
|
+
const collapsedAfter = Math.max(diff.additionLines.length - getHunkSideEndBoundary(lastHunk.additionStart, lastHunk.additionCount), 0);
|
|
225
227
|
splitTotal += collapsedAfter;
|
|
226
228
|
unifiedTotal += collapsedAfter;
|
|
227
229
|
}
|
|
@@ -229,6 +231,6 @@ function recomputeDiffRenderLineCounts(diff) {
|
|
|
229
231
|
diff.unifiedLineCount = unifiedTotal;
|
|
230
232
|
}
|
|
231
233
|
//#endregion
|
|
232
|
-
export { recomputeDiffHunks, recomputeDiffHunksForEdit, recomputeEmptyDocumentDiff, recomputeTopAlignedAdditionDiff, shouldTopAlignAdditionRecompute, updateDiffHunks };
|
|
234
|
+
export { offsetHunkContent, preserveTrailingEditorBlankLine, recomputeDiffHunks, recomputeDiffHunksForEdit, recomputeDiffRenderLineCounts, recomputeEmptyDocumentDiff, recomputeHunkRenderLineCounts, recomputeTopAlignedAdditionDiff, shouldTopAlignAdditionRecompute, syncHunkNoEOFCRFromFullFile, updateDiffHunks };
|
|
233
235
|
|
|
234
236
|
//# sourceMappingURL=updateDiffHunks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateDiffHunks.js","names":[],"sources":["../../src/utils/updateDiffHunks.ts"],"sourcesContent":["import type { CreatePatchOptionsNonabortable } from 'diff';\n\nimport type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { parseDiffFromFile } from './parseDiffFromFile';\nimport { hasTrailingContextMismatch } from './virtualDiffLayout';\n\ntype HunkContent = ContextContent | ChangeContent;\n\ntype HunkMetadataUpdate = Pick<\n FileDiffMetadata,\n 'hunks' | 'splitLineCount' | 'unifiedLineCount' | 'type'\n>;\n\ntype FullDiffHunkUpdate = HunkMetadataUpdate &\n Pick<FileDiffMetadata, 'additionLines' | 'deletionLines'>;\n\n/** Rebuilds all hunk metadata from the current deletion/addition line arrays. */\nexport function recomputeDiffHunks(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n const recomputed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: diff.deletionLines.join(''),\n },\n {\n name: diff.name,\n contents: diff.additionLines.join(''),\n lang: diff.lang,\n },\n parseDiffOptions\n );\n return {\n hunks: recomputed.hunks,\n splitLineCount: recomputed.splitLineCount,\n unifiedLineCount: recomputed.unifiedLineCount,\n additionLines: recomputed.additionLines,\n deletionLines: recomputed.deletionLines,\n type: recomputed.type,\n };\n}\n\n// Builds placeholder addition contents that diff as one top-aligned change row per\n// line. Bare `\\\\n` sentinels align as context against the first deletion lines,\n// which pushes the real addition rows down in split view.\nfunction buildTopAlignedAdditionSentinel(\n lineCount: number,\n deletionContents: string\n): string {\n const count = Math.max(lineCount, 1);\n let sentinel = Array.from(\n { length: count },\n (_, index) => `${' '.repeat(index + 1)}\\n`\n ).join('');\n if (sentinel === deletionContents) {\n sentinel = Array.from(\n { length: count },\n (_, index) => `\\u0000${' '.repeat(index)}\\n`\n ).join('');\n }\n return sentinel;\n}\n\nfunction hasOnlyBlankAdditionContents(additionLines: string[]): boolean {\n for (const line of additionLines) {\n if (line.trim().length > 0) {\n return false;\n }\n }\n return true;\n}\n\n// After delete-all the editable side is blank and much shorter than the deletion\n// side. A normal recompute can still treat newline-only additions as context and\n// render the first editable row deep in split view instead of at the top.\nexport function shouldTopAlignAdditionRecompute(\n diff: FileDiffMetadata,\n additionLines: string[]\n): boolean {\n return (\n additionLines.length > 0 &&\n additionLines.length < diff.deletionLines.length &&\n hasOnlyBlankAdditionContents(additionLines)\n );\n}\n\n// Rebuilds hunk metadata while keeping the editable addition rows top-aligned in\n// split view. The sentinel only shapes hunks; the caller's addition lines are\n// preserved so the editor document stays the source of truth.\nexport function recomputeTopAlignedAdditionDiff(\n diff: FileDiffMetadata,\n additionLines: string[],\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n const deletionContents = diff.deletionLines.join('');\n const additionSentinel = buildTopAlignedAdditionSentinel(\n additionLines.length,\n deletionContents\n );\n const recomputed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: deletionContents,\n },\n {\n name: diff.name,\n contents: additionSentinel,\n lang: diff.lang,\n },\n parseDiffOptions\n );\n return {\n hunks: recomputed.hunks,\n splitLineCount: recomputed.splitLineCount,\n unifiedLineCount: recomputed.unifiedLineCount,\n additionLines,\n deletionLines: recomputed.deletionLines,\n type: recomputed.type,\n };\n}\n\n// Rebuilds hunk metadata when the editable side has been emptied of all text.\n//\n// The editor always keeps one (empty) line for an empty document, but an empty\n// string splits into zero addition lines, so a normal recompute produces a diff\n// with no addition rows at all. Rendering that leaves the attached editor with\n// no line element to host its caret.\nexport function recomputeEmptyDocumentDiff(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n return recomputeTopAlignedAdditionDiff(diff, [''], parseDiffOptions);\n}\n\n/** Rebuilds diff hunks after an edit, top-aligning sparse addition sides when needed. */\nexport function recomputeDiffHunksForEdit(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n if (diff.additionLines.length === 0) {\n return recomputeEmptyDocumentDiff(diff, parseDiffOptions);\n }\n if (shouldTopAlignAdditionRecompute(diff, diff.additionLines)) {\n return recomputeTopAlignedAdditionDiff(\n diff,\n diff.additionLines,\n parseDiffOptions\n );\n }\n const additionLines = diff.additionLines;\n const recomputed = recomputeDiffHunks(diff, parseDiffOptions);\n if (\n additionLines.length > recomputed.additionLines.length &&\n hasTrailingEditorBlankLine(additionLines)\n ) {\n preserveTrailingEditorBlankLine(recomputed, additionLines);\n }\n return recomputed;\n}\n\nfunction hasTrailingEditorBlankLine(additionLines: string[]): boolean {\n return additionLines.length > 1 && additionLines.at(-1) === '';\n}\n\nfunction preserveTrailingEditorBlankLine(\n recomputed: FullDiffHunkUpdate,\n additionLines: string[]\n): void {\n const extraLineCount = additionLines.length - recomputed.additionLines.length;\n if (extraLineCount <= 0) {\n return;\n }\n const extraAdditionLineIndex = recomputed.additionLines.length;\n\n const lastHunk = recomputed.hunks.at(-1);\n if (lastHunk == null) {\n return;\n }\n\n const lastHunkAdditionEnd =\n lastHunk.additionLineIndex + lastHunk.additionCount;\n if (lastHunkAdditionEnd !== extraAdditionLineIndex) {\n return;\n }\n\n for (const content of lastHunk.hunkContent) {\n if (\n content.type === 'change' &&\n content.additions < content.deletions &&\n content.additionLineIndex + content.additions === extraAdditionLineIndex\n ) {\n recomputed.additionLines = additionLines;\n content.additions += extraLineCount;\n lastHunk.additionCount += extraLineCount;\n lastHunk.additionLines += extraLineCount;\n recomputeDiffRenderLineCounts(recomputed);\n return;\n }\n }\n}\n\n/** Updates hunk metadata after addition lines change; re-parses affected hunks only. */\nexport function updateDiffHunks(\n diff: FileDiffMetadata,\n changedAdditionLineIndexes: Iterable<number>,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): HunkMetadataUpdate {\n if (diff.isPartial) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n if (diff.deletionLines.length !== diff.additionLines.length) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n const changedLines = Array.from(changedAdditionLineIndexes);\n if (changedLines.length === 0) {\n return applyHunkUpdateResult(diff, {\n hunks: diff.hunks,\n splitLineCount: diff.splitLineCount,\n unifiedLineCount: diff.unifiedLineCount,\n type: diff.type,\n });\n }\n for (const line of changedLines) {\n const additionLine = diff.additionLines[line];\n const deletionLine = diff.deletionLines[line];\n if (additionLine == null || deletionLine == null) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n // Restoring a line to the old side can merge/split hunks across context windows.\n if (cleanLastNewline(additionLine) === cleanLastNewline(deletionLine)) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n }\n\n const affectedHunkIndexes = getAffectedHunkIndexes(diff, changedLines);\n if (affectedHunkIndexes.size === 0) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n for (const hunkIndex of affectedHunkIndexes) {\n const updated = reparseHunkRegion(diff, hunkIndex, parseDiffOptions);\n if (!updated) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n }\n\n recomputeDiffRenderLineCounts(diff);\n\n if (hasTrailingContextMismatch(diff)) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n return applyHunkUpdateResult(diff, {\n hunks: diff.hunks,\n splitLineCount: diff.splitLineCount,\n unifiedLineCount: diff.unifiedLineCount,\n type: diff.type,\n });\n}\n\nfunction applyHunkUpdateResult<T extends HunkMetadataUpdate>(\n diff: FileDiffMetadata,\n result: T\n): T {\n Object.assign(diff, result);\n return result;\n}\n\nfunction getAffectedHunkIndexes(\n diff: FileDiffMetadata,\n changedAdditionLineIndexes: Iterable<number>\n): Set<number> {\n const indexes = new Set<number>();\n for (const line of changedAdditionLineIndexes) {\n const hunkIndex = findHunkIndexForAdditionLine(diff, line);\n if (hunkIndex == null) {\n return new Set();\n }\n indexes.add(hunkIndex);\n }\n return indexes;\n}\n\nfunction findHunkIndexForAdditionLine(\n diff: FileDiffMetadata,\n line: number\n): number | undefined {\n for (const [hunkIndex, hunk] of diff.hunks.entries()) {\n const end = hunk.additionLineIndex + hunk.additionCount;\n if (line >= hunk.additionLineIndex && line < end) {\n return hunkIndex;\n }\n }\n return undefined;\n}\n\nfunction reparseHunkRegion(\n diff: FileDiffMetadata,\n hunkIndex: number,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): boolean {\n const hunk = diff.hunks[hunkIndex];\n if (hunk == null) {\n return false;\n }\n\n const deletionSlice = diff.deletionLines.slice(\n hunk.deletionLineIndex,\n hunk.deletionLineIndex + hunk.deletionCount\n );\n const additionSlice = diff.additionLines.slice(\n hunk.additionLineIndex,\n hunk.additionLineIndex + hunk.additionCount\n );\n\n const reparsed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: deletionSlice.join(''),\n },\n {\n name: diff.name,\n contents: additionSlice.join(''),\n lang: diff.lang,\n },\n { ...parseDiffOptions, context: 0 }\n );\n\n const reparsedHunk = reparsed.hunks[0];\n if (reparsedHunk == null || reparsed.hunks.length !== 1) {\n return false;\n }\n\n applyReparsedHunk(hunk, reparsedHunk);\n syncHunkNoEOFCRFromFullFile(diff, hunkIndex);\n return true;\n}\n\nfunction syncHunkNoEOFCRFromFullFile(\n diff: FileDiffMetadata,\n hunkIndex: number\n): void {\n const hunk = diff.hunks[hunkIndex];\n if (hunk == null) {\n return;\n }\n\n const isLastHunk = hunkIndex === diff.hunks.length - 1;\n if (!isLastHunk) {\n hunk.noEOFCRAdditions = false;\n hunk.noEOFCRDeletions = false;\n return;\n }\n\n const lastAdditionLine = diff.additionLines.at(-1);\n const lastDeletionLine = diff.deletionLines.at(-1);\n hunk.noEOFCRAdditions =\n lastAdditionLine != null &&\n lastAdditionLine !== '' &&\n !lastAdditionLine.endsWith('\\n');\n hunk.noEOFCRDeletions =\n lastDeletionLine != null &&\n lastDeletionLine !== '' &&\n !lastDeletionLine.endsWith('\\n');\n}\n\nfunction applyReparsedHunk(target: Hunk, parsed: Hunk): void {\n const additionOffset = target.additionLineIndex;\n const deletionOffset = target.deletionLineIndex;\n\n target.hunkContent = parsed.hunkContent.map((content) =>\n offsetHunkContent(content, additionOffset, deletionOffset)\n );\n target.additionLineIndex = additionOffset + parsed.additionLineIndex;\n target.additionStart = target.additionStart + parsed.additionLineIndex;\n target.additionCount = parsed.additionCount;\n target.additionLines = parsed.additionLines;\n if (parsed.deletionLineIndex >= 0) {\n target.deletionLineIndex = deletionOffset + parsed.deletionLineIndex;\n target.deletionStart = target.deletionStart + parsed.deletionLineIndex;\n }\n target.deletionCount = parsed.deletionCount;\n target.deletionLines = parsed.deletionLines;\n target.noEOFCRAdditions = parsed.noEOFCRAdditions;\n target.noEOFCRDeletions = parsed.noEOFCRDeletions;\n\n recomputeHunkRenderLineCounts(target);\n}\n\nfunction offsetHunkContent(\n content: HunkContent,\n additionOffset: number,\n deletionOffset: number\n): HunkContent {\n return {\n ...content,\n additionLineIndex: content.additionLineIndex + additionOffset,\n deletionLineIndex: content.deletionLineIndex + deletionOffset,\n };\n}\n\nfunction recomputeHunkRenderLineCounts(hunk: Hunk): void {\n let splitLineCount = 0;\n let unifiedLineCount = 0;\n\n for (const content of hunk.hunkContent) {\n if (content.type === 'context') {\n splitLineCount += content.lines;\n unifiedLineCount += content.lines;\n } else {\n splitLineCount += Math.max(content.additions, content.deletions);\n unifiedLineCount += content.additions + content.deletions;\n }\n }\n\n hunk.splitLineCount = splitLineCount;\n hunk.unifiedLineCount = unifiedLineCount;\n}\n\nfunction recomputeDiffRenderLineCounts(\n diff: HunkMetadataUpdate & Pick<FileDiffMetadata, 'additionLines'>\n): void {\n let splitTotal = 0;\n let unifiedTotal = 0;\n let lastHunkAdditionEnd = 0;\n\n for (const hunk of diff.hunks) {\n hunk.collapsedBefore = Math.max(\n hunk.additionStart - 1 - lastHunkAdditionEnd,\n 0\n );\n hunk.splitLineStart = splitTotal + hunk.collapsedBefore;\n hunk.unifiedLineStart = unifiedTotal + hunk.collapsedBefore;\n\n recomputeHunkRenderLineCounts(hunk);\n\n splitTotal += hunk.collapsedBefore + hunk.splitLineCount;\n unifiedTotal += hunk.collapsedBefore + hunk.unifiedLineCount;\n lastHunkAdditionEnd = hunk.additionStart + hunk.additionCount - 1;\n }\n\n if (diff.hunks.length > 0) {\n const lastHunk = diff.hunks[diff.hunks.length - 1];\n const collapsedAfter = Math.max(\n diff.additionLines.length -\n (lastHunk.additionLineIndex + lastHunk.additionCount),\n 0\n );\n splitTotal += collapsedAfter;\n unifiedTotal += collapsedAfter;\n }\n\n diff.splitLineCount = splitTotal;\n diff.unifiedLineCount = unifiedTotal;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"updateDiffHunks.js","names":[],"sources":["../../src/utils/updateDiffHunks.ts"],"sourcesContent":["import type { CreatePatchOptionsNonabortable } from 'diff';\n\nimport type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport {\n getHunkSideEndBoundary,\n getHunkSideStartBoundary,\n} from './getHunkSideBoundaries';\nimport { parseDiffFromFile } from './parseDiffFromFile';\nimport { hasTrailingContextMismatch } from './virtualDiffLayout';\n\ntype HunkContent = ContextContent | ChangeContent;\n\ntype HunkMetadataUpdate = Pick<\n FileDiffMetadata,\n 'hunks' | 'splitLineCount' | 'unifiedLineCount' | 'type'\n>;\n\ntype FullDiffHunkUpdate = HunkMetadataUpdate &\n Pick<FileDiffMetadata, 'additionLines' | 'deletionLines'>;\n\n/** Rebuilds all hunk metadata from the current deletion/addition line arrays. */\nexport function recomputeDiffHunks(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n const recomputed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: diff.deletionLines.join(''),\n },\n {\n name: diff.name,\n contents: diff.additionLines.join(''),\n lang: diff.lang,\n },\n parseDiffOptions\n );\n return {\n hunks: recomputed.hunks,\n splitLineCount: recomputed.splitLineCount,\n unifiedLineCount: recomputed.unifiedLineCount,\n additionLines: recomputed.additionLines,\n deletionLines: recomputed.deletionLines,\n type: recomputed.type,\n };\n}\n\n// Builds placeholder addition contents that diff as one top-aligned change row per\n// line. Bare `\\\\n` sentinels align as context against the first deletion lines,\n// which pushes the real addition rows down in split view.\nfunction buildTopAlignedAdditionSentinel(\n lineCount: number,\n deletionContents: string\n): string {\n const count = Math.max(lineCount, 1);\n let sentinel = Array.from(\n { length: count },\n (_, index) => `${' '.repeat(index + 1)}\\n`\n ).join('');\n if (sentinel === deletionContents) {\n sentinel = Array.from(\n { length: count },\n (_, index) => `\\u0000${' '.repeat(index)}\\n`\n ).join('');\n }\n return sentinel;\n}\n\nfunction hasOnlyBlankAdditionContents(additionLines: string[]): boolean {\n for (const line of additionLines) {\n if (line.trim().length > 0) {\n return false;\n }\n }\n return true;\n}\n\n// After delete-all the editable side is blank and much shorter than the deletion\n// side. A normal recompute can still treat newline-only additions as context and\n// render the first editable row deep in split view instead of at the top.\nexport function shouldTopAlignAdditionRecompute(\n diff: FileDiffMetadata,\n additionLines: string[]\n): boolean {\n return (\n additionLines.length > 0 &&\n additionLines.length < diff.deletionLines.length &&\n hasOnlyBlankAdditionContents(additionLines)\n );\n}\n\n// Rebuilds hunk metadata while keeping the editable addition rows top-aligned in\n// split view. The sentinel only shapes hunks; the caller's addition lines are\n// preserved so the editor document stays the source of truth.\nexport function recomputeTopAlignedAdditionDiff(\n diff: FileDiffMetadata,\n additionLines: string[],\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n const deletionContents = diff.deletionLines.join('');\n const additionSentinel = buildTopAlignedAdditionSentinel(\n additionLines.length,\n deletionContents\n );\n const recomputed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: deletionContents,\n },\n {\n name: diff.name,\n contents: additionSentinel,\n lang: diff.lang,\n },\n parseDiffOptions\n );\n return {\n hunks: recomputed.hunks,\n splitLineCount: recomputed.splitLineCount,\n unifiedLineCount: recomputed.unifiedLineCount,\n additionLines,\n deletionLines: recomputed.deletionLines,\n type: recomputed.type,\n };\n}\n\n// Rebuilds hunk metadata when the editable side has been emptied of all text.\n//\n// The editor always keeps one (empty) line for an empty document, but an empty\n// string splits into zero addition lines, so a normal recompute produces a diff\n// with no addition rows at all. Rendering that leaves the attached editor with\n// no line element to host its caret.\nexport function recomputeEmptyDocumentDiff(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n return recomputeTopAlignedAdditionDiff(diff, [''], parseDiffOptions);\n}\n\n/** Rebuilds diff hunks after an edit, top-aligning sparse addition sides when needed. */\nexport function recomputeDiffHunksForEdit(\n diff: FileDiffMetadata,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): FullDiffHunkUpdate {\n if (diff.additionLines.length === 0) {\n return recomputeEmptyDocumentDiff(diff, parseDiffOptions);\n }\n if (shouldTopAlignAdditionRecompute(diff, diff.additionLines)) {\n return recomputeTopAlignedAdditionDiff(\n diff,\n diff.additionLines,\n parseDiffOptions\n );\n }\n const additionLines = diff.additionLines;\n const recomputed = recomputeDiffHunks(diff, parseDiffOptions);\n preserveTrailingEditorBlankLine(recomputed, additionLines);\n return recomputed;\n}\n\nfunction hasTrailingEditorBlankLine(additionLines: string[]): boolean {\n return additionLines.length > 1 && additionLines.at(-1) === '';\n}\n\n// Re-adds the editor's phantom trailing empty line (a document ending in a\n// newline exposes one extra empty line) as an addition row when the last\n// hunk's final change block can absorb it, so the caret keeps a rendered row.\nexport function preserveTrailingEditorBlankLine(\n recomputed: Pick<\n FullDiffHunkUpdate,\n 'hunks' | 'additionLines' | 'splitLineCount' | 'unifiedLineCount'\n >,\n additionLines: string[]\n): void {\n if (!hasTrailingEditorBlankLine(additionLines)) {\n return;\n }\n const extraLineCount = additionLines.length - recomputed.additionLines.length;\n if (extraLineCount <= 0) {\n return;\n }\n const extraAdditionLineIndex = recomputed.additionLines.length;\n\n const lastHunk = recomputed.hunks.at(-1);\n if (lastHunk == null) {\n return;\n }\n\n const lastHunkAdditionEnd = getHunkSideEndBoundary(\n lastHunk.additionStart,\n lastHunk.additionCount\n );\n if (lastHunkAdditionEnd !== extraAdditionLineIndex) {\n return;\n }\n\n for (const content of lastHunk.hunkContent) {\n if (\n content.type === 'change' &&\n content.additions < content.deletions &&\n content.additionLineIndex + content.additions === extraAdditionLineIndex\n ) {\n recomputed.additionLines = additionLines;\n content.additions += extraLineCount;\n lastHunk.additionCount += extraLineCount;\n lastHunk.additionLines += extraLineCount;\n recomputeDiffRenderLineCounts(recomputed);\n return;\n }\n }\n}\n\n/** Updates hunk metadata after addition lines change; re-parses affected hunks only. */\nexport function updateDiffHunks(\n diff: FileDiffMetadata,\n changedAdditionLineIndexes: Iterable<number>,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): HunkMetadataUpdate {\n if (diff.isPartial) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n if (diff.deletionLines.length !== diff.additionLines.length) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n const changedLines = Array.from(changedAdditionLineIndexes);\n if (changedLines.length === 0) {\n return applyHunkUpdateResult(diff, {\n hunks: diff.hunks,\n splitLineCount: diff.splitLineCount,\n unifiedLineCount: diff.unifiedLineCount,\n type: diff.type,\n });\n }\n for (const line of changedLines) {\n const additionLine = diff.additionLines[line];\n const deletionLine = diff.deletionLines[line];\n if (additionLine == null || deletionLine == null) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n // Restoring a line to the old side can merge/split hunks across context windows.\n if (cleanLastNewline(additionLine) === cleanLastNewline(deletionLine)) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n }\n\n const affectedHunkIndexes = getAffectedHunkIndexes(diff, changedLines);\n if (affectedHunkIndexes.size === 0) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n for (const hunkIndex of affectedHunkIndexes) {\n const updated = reparseHunkRegion(diff, hunkIndex, parseDiffOptions);\n if (!updated) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n }\n\n recomputeDiffRenderLineCounts(diff);\n\n if (hasTrailingContextMismatch(diff)) {\n return applyHunkUpdateResult(\n diff,\n recomputeDiffHunks(diff, parseDiffOptions)\n );\n }\n\n return applyHunkUpdateResult(diff, {\n hunks: diff.hunks,\n splitLineCount: diff.splitLineCount,\n unifiedLineCount: diff.unifiedLineCount,\n type: diff.type,\n });\n}\n\nfunction applyHunkUpdateResult<T extends HunkMetadataUpdate>(\n diff: FileDiffMetadata,\n result: T\n): T {\n Object.assign(diff, result);\n return result;\n}\n\nfunction getAffectedHunkIndexes(\n diff: FileDiffMetadata,\n changedAdditionLineIndexes: Iterable<number>\n): Set<number> {\n const indexes = new Set<number>();\n for (const line of changedAdditionLineIndexes) {\n const hunkIndex = findHunkIndexForAdditionLine(diff, line);\n if (hunkIndex == null) {\n return new Set();\n }\n indexes.add(hunkIndex);\n }\n return indexes;\n}\n\nfunction findHunkIndexForAdditionLine(\n diff: FileDiffMetadata,\n line: number\n): number | undefined {\n for (const [hunkIndex, hunk] of diff.hunks.entries()) {\n const end = hunk.additionLineIndex + hunk.additionCount;\n if (line >= hunk.additionLineIndex && line < end) {\n return hunkIndex;\n }\n }\n return undefined;\n}\n\nfunction reparseHunkRegion(\n diff: FileDiffMetadata,\n hunkIndex: number,\n parseDiffOptions?: CreatePatchOptionsNonabortable\n): boolean {\n const hunk = diff.hunks[hunkIndex];\n if (hunk == null) {\n return false;\n }\n\n const deletionSlice = diff.deletionLines.slice(\n hunk.deletionLineIndex,\n hunk.deletionLineIndex + hunk.deletionCount\n );\n const additionSlice = diff.additionLines.slice(\n hunk.additionLineIndex,\n hunk.additionLineIndex + hunk.additionCount\n );\n\n const reparsed = parseDiffFromFile(\n {\n name: diff.prevName ?? diff.name,\n contents: deletionSlice.join(''),\n },\n {\n name: diff.name,\n contents: additionSlice.join(''),\n lang: diff.lang,\n },\n { ...parseDiffOptions, context: 0 }\n );\n\n const reparsedHunk = reparsed.hunks[0];\n if (reparsedHunk == null || reparsed.hunks.length !== 1) {\n return false;\n }\n\n applyReparsedHunk(hunk, reparsedHunk);\n syncHunkNoEOFCRFromFullFile(diff, hunkIndex);\n return true;\n}\n\nexport function syncHunkNoEOFCRFromFullFile(\n diff: FileDiffMetadata,\n hunkIndex: number\n): void {\n const hunk = diff.hunks[hunkIndex];\n if (hunk == null) {\n return;\n }\n\n const isLastHunk = hunkIndex === diff.hunks.length - 1;\n if (!isLastHunk) {\n hunk.noEOFCRAdditions = false;\n hunk.noEOFCRDeletions = false;\n return;\n }\n\n const lastAdditionLine = diff.additionLines.at(-1);\n const lastDeletionLine = diff.deletionLines.at(-1);\n hunk.noEOFCRAdditions =\n lastAdditionLine != null &&\n lastAdditionLine !== '' &&\n !lastAdditionLine.endsWith('\\n');\n hunk.noEOFCRDeletions =\n lastDeletionLine != null &&\n lastDeletionLine !== '' &&\n !lastDeletionLine.endsWith('\\n');\n}\n\nfunction applyReparsedHunk(target: Hunk, parsed: Hunk): void {\n const additionOffset = target.additionLineIndex;\n const deletionOffset = target.deletionLineIndex;\n\n target.hunkContent = parsed.hunkContent.map((content) =>\n offsetHunkContent(content, additionOffset, deletionOffset)\n );\n target.additionLineIndex = additionOffset + parsed.additionLineIndex;\n target.additionStart = target.additionStart + parsed.additionLineIndex;\n target.additionCount = parsed.additionCount;\n target.additionLines = parsed.additionLines;\n if (parsed.deletionLineIndex >= 0) {\n target.deletionLineIndex = deletionOffset + parsed.deletionLineIndex;\n target.deletionStart = target.deletionStart + parsed.deletionLineIndex;\n }\n target.deletionCount = parsed.deletionCount;\n target.deletionLines = parsed.deletionLines;\n target.noEOFCRAdditions = parsed.noEOFCRAdditions;\n target.noEOFCRDeletions = parsed.noEOFCRDeletions;\n\n recomputeHunkRenderLineCounts(target);\n}\n\nexport function offsetHunkContent(\n content: HunkContent,\n additionOffset: number,\n deletionOffset: number\n): HunkContent {\n return {\n ...content,\n additionLineIndex: content.additionLineIndex + additionOffset,\n deletionLineIndex: content.deletionLineIndex + deletionOffset,\n };\n}\n\nexport function recomputeHunkRenderLineCounts(hunk: Hunk): void {\n let splitLineCount = 0;\n let unifiedLineCount = 0;\n\n for (const content of hunk.hunkContent) {\n if (content.type === 'context') {\n splitLineCount += content.lines;\n unifiedLineCount += content.lines;\n } else {\n splitLineCount += Math.max(content.additions, content.deletions);\n unifiedLineCount += content.additions + content.deletions;\n }\n }\n\n hunk.splitLineCount = splitLineCount;\n hunk.unifiedLineCount = unifiedLineCount;\n}\n\nexport function recomputeDiffRenderLineCounts(\n diff: Pick<\n FileDiffMetadata,\n 'hunks' | 'splitLineCount' | 'unifiedLineCount' | 'additionLines'\n >\n): void {\n let splitTotal = 0;\n let unifiedTotal = 0;\n let lastHunkAdditionEnd = 0;\n\n for (const hunk of diff.hunks) {\n hunk.collapsedBefore = Math.max(\n getHunkSideStartBoundary(hunk.additionStart, hunk.additionCount) -\n lastHunkAdditionEnd,\n 0\n );\n hunk.splitLineStart = splitTotal + hunk.collapsedBefore;\n hunk.unifiedLineStart = unifiedTotal + hunk.collapsedBefore;\n\n recomputeHunkRenderLineCounts(hunk);\n\n splitTotal += hunk.collapsedBefore + hunk.splitLineCount;\n unifiedTotal += hunk.collapsedBefore + hunk.unifiedLineCount;\n lastHunkAdditionEnd = getHunkSideEndBoundary(\n hunk.additionStart,\n hunk.additionCount\n );\n }\n\n if (diff.hunks.length > 0) {\n const lastHunk = diff.hunks[diff.hunks.length - 1];\n const collapsedAfter = Math.max(\n diff.additionLines.length -\n getHunkSideEndBoundary(lastHunk.additionStart, lastHunk.additionCount),\n 0\n );\n splitTotal += collapsedAfter;\n unifiedTotal += collapsedAfter;\n }\n\n diff.splitLineCount = splitTotal;\n diff.unifiedLineCount = unifiedTotal;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileDiffMetadata, HunkExpansionRegion, HunkSeparators, VirtualFileMetrics } from "../types.js";
|
|
1
|
+
import { FileDiffMetadata, Hunk, HunkExpansionRegion, HunkSeparators, VirtualFileMetrics } from "../types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/virtualDiffLayout.d.ts
|
|
4
4
|
interface ExpandedRegionResult {
|
|
@@ -24,6 +24,8 @@ interface GetTrailingExpandedRegionProps extends GetTrailingContextRangeSizeProp
|
|
|
24
24
|
expandedHunks: GetExpandedRegionProps['expandedHunks'];
|
|
25
25
|
collapsedContextThreshold: number;
|
|
26
26
|
}
|
|
27
|
+
/** One-based new-file line range covered by a hunk, as `[start, end)`. */
|
|
28
|
+
declare function getHunkAdditionLineRange(hunk: Hunk): [number, number];
|
|
27
29
|
interface HunkSeparatorLayout {
|
|
28
30
|
height: number;
|
|
29
31
|
gapBefore: number;
|
|
@@ -58,6 +60,44 @@ declare function getTrailingExpandedRegion({
|
|
|
58
60
|
collapsedContextThreshold,
|
|
59
61
|
errorPrefix
|
|
60
62
|
}: GetTrailingExpandedRegionProps): ExpandedRegionResult | undefined;
|
|
63
|
+
interface IsAdditionLineRenderableProps {
|
|
64
|
+
fileDiff: FileDiffMetadata;
|
|
65
|
+
/** One-based line number in the new file. */
|
|
66
|
+
lineNumber: number;
|
|
67
|
+
expandedHunks: Map<number, HunkExpansionRegion> | true | undefined;
|
|
68
|
+
collapsedContextThreshold: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether a one-based new-file line currently has (or will have on scroll) a
|
|
72
|
+
* rendered row under the given expansion state — the editor-facing
|
|
73
|
+
* visibility oracle. False only for lines hidden inside a collapsed
|
|
74
|
+
* unchanged region; lines outside the diff's modeled range report true so
|
|
75
|
+
* callers keep their existing missing-row handling. Computed from the same
|
|
76
|
+
* inputs as `iterateOverDiff` so layout math and the oracle cannot diverge.
|
|
77
|
+
*/
|
|
78
|
+
declare function isAdditionLineRenderable({
|
|
79
|
+
fileDiff,
|
|
80
|
+
lineNumber,
|
|
81
|
+
expandedHunks,
|
|
82
|
+
collapsedContextThreshold
|
|
83
|
+
}: IsAdditionLineRenderableProps): boolean;
|
|
84
|
+
interface GetNearestRenderableAdditionLineProps extends IsAdditionLineRenderableProps {
|
|
85
|
+
direction: 'up' | 'down';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The nearest renderable new-file line at or beyond `lineNumber` in the
|
|
89
|
+
* given direction (one-based), or undefined when every line that way is
|
|
90
|
+
* hidden inside collapsed regions. Sequential caret motion uses this to skip
|
|
91
|
+
* over collapsed regions like code folds; it walks the hunk metadata once
|
|
92
|
+
* instead of probing line by line across a gap.
|
|
93
|
+
*/
|
|
94
|
+
declare function getNearestRenderableAdditionLine({
|
|
95
|
+
fileDiff,
|
|
96
|
+
lineNumber,
|
|
97
|
+
direction,
|
|
98
|
+
expandedHunks,
|
|
99
|
+
collapsedContextThreshold
|
|
100
|
+
}: GetNearestRenderableAdditionLineProps): number | undefined;
|
|
61
101
|
declare function getHunkSeparatorHeight({
|
|
62
102
|
type,
|
|
63
103
|
metrics
|
|
@@ -83,5 +123,5 @@ declare function getTrailingHunkSeparatorLayout({
|
|
|
83
123
|
metrics
|
|
84
124
|
}: HunkSeparatorBaseProps): HunkSeparatorLayout | undefined;
|
|
85
125
|
//#endregion
|
|
86
|
-
export { ExpandedRegionResult, GetExpandedRegionProps, GetTrailingContextRangeSizeProps, GetTrailingExpandedRegionProps, HunkSeparatorLayout, getExpandedRegion, getHunkSeparatorGap, getHunkSeparatorHeight, getLeadingHunkSeparatorLayout, getTrailingContextRangeSize, getTrailingExpandedRegion, getTrailingHunkSeparatorLayout, hasLeadingHunkSeparator, hasTrailingContext, hasTrailingContextMismatch, hasTrailingHunkSeparator };
|
|
126
|
+
export { ExpandedRegionResult, GetExpandedRegionProps, GetNearestRenderableAdditionLineProps, GetTrailingContextRangeSizeProps, GetTrailingExpandedRegionProps, HunkSeparatorLayout, IsAdditionLineRenderableProps, getExpandedRegion, getHunkAdditionLineRange, getHunkSeparatorGap, getHunkSeparatorHeight, getLeadingHunkSeparatorLayout, getNearestRenderableAdditionLine, getTrailingContextRangeSize, getTrailingExpandedRegion, getTrailingHunkSeparatorLayout, hasLeadingHunkSeparator, hasTrailingContext, hasTrailingContextMismatch, hasTrailingHunkSeparator, isAdditionLineRenderable };
|
|
87
127
|
//# sourceMappingURL=virtualDiffLayout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtualDiffLayout.d.ts","names":[],"sources":["../../src/utils/virtualDiffLayout.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"virtualDiffLayout.d.ts","names":[],"sources":["../../src/utils/virtualDiffLayout.ts"],"mappings":";;;UAaiB,oBAAA;EACf,SAAA;EACA,OAAA;EACA,SAAA;EACA,cAAA;EACA,SAAA;AAAA;AAAA,UAGe,sBAAA;EACf,SAAA;EACA,SAAA;EACA,aAAA,EAAe,GAAG,SAAS,mBAAA;EAC3B,SAAA;EACA,yBAAA;AAAA;AAAA,UAGe,gCAAA;EACf,QAAA,EAAU,gBAAgB;EAC1B,WAAA;AAAA;AAAA,UAGe,8BAAA,SAAuC,gCAAgC;EACtF,SAAA;EACA,aAAA,EAAe,sBAAA;EACf,yBAAA;AAAA;;iBAIc,wBAAA,CAAyB,IAAU,EAAJ,IAAI;AAAA,UAOlC,mBAAA;EACf,MAAA;EACA,SAAA;EACA,QAAA;EACA,WAAA;AAAA;AAAA,UAGQ,sBAAA;EACR,IAAA,EAAM,cAAA;EACN,OAAA,EAAS,kBAAkB;AAAA;AAAA,UAGnB,+BAAA,SAAwC,sBAAsB;EACtE,SAAA;EACA,SAAA;AAAA;AAAA,iBAKc,iBAAA;EACd,SAAA;EACA,SAAA;EACA,aAAA;EACA,SAAA;EACA;AAAA,GACC,sBAAA,GAAyB,oBAAA;AAAA,iBA6CZ,kBAAA,CAAmB,QAA0B,EAAhB,gBAAgB;AAAA,iBAsB7C,0BAAA,CACd,QAA0B,EAAhB,gBAAgB;AAAA,iBA4BZ,2BAAA;EACd,QAAA;EACA;AAAA,GACC,gCAAA;AAAA,iBA8Ba,yBAAA;EACd,QAAA;EACA,SAAA;EACA,aAAA;EACA,yBAAA;EACA;AAAA,GACC,8BAAA,GAAiC,oBAAA;AAAA,UA0CnB,6BAAA;EACf,QAAA,EAAU,gBAAA;EAlNI;EAoNd,UAAA;EACA,aAAA,EAAe,GAAA,SAAY,mBAAA;EAC3B,yBAAA;AAAA;AA/MF;;;;;;;;AAAA,iBA0NgB,wBAAA;EACd,QAAA;EACA,UAAA;EACA,aAAA;EACA;AAAA,GACC,6BAAA;AAAA,UA+Cc,qCAAA,SAA8C,6BAA6B;EAC1F,SAAS;AAAA;;;;;;;;iBAUK,gCAAA;EACd,QAAA;EACA,UAAA;EACA,SAAA;EACA,aAAA;EACA;AAAA,GACC,qCAAA;AAAA,iBAsEa,sBAAA;EACd,IAAA;EACA;AAAA,GACC,sBAAA;AAAA,iBAIa,mBAAA;EACd,IAAA;EACA;AAAA,GACC,sBAAA;AAAA,iBAMa,uBAAA;EACd,IAAA;EACA,SAAA;EACA;AAAA,GACC,IAAA,CAAK,+BAAA;AAAA,iBAaQ,wBAAA,CAAyB,IAAoB,EAAd,cAAc;AAAA,iBAK7C,6BAAA;EACd,IAAA;EACA,OAAA;EACA,SAAA;EACA;AAAA,GACC,+BAAA,GAAkC,mBAAA;AAAA,iBAmBrB,8BAAA;EACd,IAAA;EACA;AAAA,GACC,sBAAA,GAAyB,mBAAA"}
|