@pierre/diffs 1.2.1 → 1.2.3
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 +22 -7
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +202 -105
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +2 -0
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +13 -9
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +2 -0
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +12 -6
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.d.ts +4 -2
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +23 -6
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +9 -8
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +329 -142
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/react/index.d.ts +2 -2
- package/dist/renderers/DiffHunksRenderer.d.ts +1 -0
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +19 -9
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +1 -0
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +12 -6
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.d.ts +28 -0
- package/dist/utils/computeEstimatedDiffHeights.d.ts.map +1 -0
- package/dist/utils/computeEstimatedDiffHeights.js +111 -0
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -0
- package/dist/utils/getDiffHunksRendererOptions.d.ts +8 -0
- package/dist/utils/getDiffHunksRendererOptions.d.ts.map +1 -0
- package/dist/utils/getDiffHunksRendererOptions.js +31 -0
- package/dist/utils/getDiffHunksRendererOptions.js.map +1 -0
- package/dist/utils/getFileRendererOptions.d.ts +8 -0
- package/dist/utils/getFileRendererOptions.d.ts.map +1 -0
- package/dist/utils/getFileRendererOptions.js +24 -0
- package/dist/utils/getFileRendererOptions.js.map +1 -0
- package/dist/utils/iterateOverDiff.js +29 -30
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/parsePatchFiles.js +8 -1
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +65 -0
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -0
- package/dist/utils/virtualDiffLayout.js +94 -0
- package/dist/utils/virtualDiffLayout.js.map +1 -0
- package/dist/worker/WorkerPoolManager.d.ts +4 -1
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +49 -24
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +2 -0
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/worker-portable.js +163 -40
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +60 -30
- package/dist/worker/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } from "../constants.js";
|
|
2
2
|
import { areObjectsEqual } from "../utils/areObjectsEqual.js";
|
|
3
3
|
import { areOptionsEqual } from "../utils/areOptionsEqual.js";
|
|
4
|
-
import { computeVirtualFileMetrics,
|
|
4
|
+
import { computeVirtualFileMetrics, getVirtualFileHeaderRegion, getVirtualFilePaddingBottom } from "../utils/computeVirtualFileMetrics.js";
|
|
5
|
+
import { areDiffTargetsEqual } from "../utils/areDiffTargetsEqual.js";
|
|
6
|
+
import { getExpandedRegion, getLeadingHunkSeparatorLayout, getTrailingHunkSeparatorLayout } from "../utils/virtualDiffLayout.js";
|
|
7
|
+
import { computeEstimatedDiffHeights } from "../utils/computeEstimatedDiffHeights.js";
|
|
5
8
|
import { iterateOverDiff } from "../utils/iterateOverDiff.js";
|
|
6
9
|
import { parseDiffFromFile } from "../utils/parseDiffFromFile.js";
|
|
7
10
|
import { FileDiff } from "./FileDiff.js";
|
|
@@ -15,7 +18,10 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
15
18
|
height = 0;
|
|
16
19
|
metrics;
|
|
17
20
|
cache = {
|
|
18
|
-
|
|
21
|
+
heightDeltas: /* @__PURE__ */ new Map(),
|
|
22
|
+
measuredHeightDeltaTotal: 0,
|
|
23
|
+
estimatedSplitHeight: void 0,
|
|
24
|
+
estimatedUnifiedHeight: void 0,
|
|
19
25
|
checkpoints: [],
|
|
20
26
|
totalLines: 0
|
|
21
27
|
};
|
|
@@ -24,6 +30,7 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
24
30
|
virtualizer;
|
|
25
31
|
layoutDirty = true;
|
|
26
32
|
forceRenderOverride;
|
|
33
|
+
currentCollapsed;
|
|
27
34
|
constructor(options, virtualizer, metrics, workerManager, isContainerManaged = false) {
|
|
28
35
|
super(options, workerManager, isContainerManaged);
|
|
29
36
|
this.virtualizer = virtualizer;
|
|
@@ -33,31 +40,45 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
33
40
|
const nextMetrics = computeVirtualFileMetrics(metrics);
|
|
34
41
|
if (!force && areObjectsEqual(this.metrics, nextMetrics)) return;
|
|
35
42
|
this.metrics = nextMetrics;
|
|
36
|
-
this.resetLayoutCache();
|
|
43
|
+
this.resetLayoutCache({ includeEstimatedHeights: true });
|
|
37
44
|
}
|
|
38
45
|
getLineHeight(lineIndex, hasMetadataLine = false) {
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
return this.getEstimatedLineHeight(hasMetadataLine) + (this.cache.heightDeltas.get(lineIndex) ?? 0);
|
|
47
|
+
}
|
|
48
|
+
getEstimatedLineHeight(hasMetadataLine = false) {
|
|
41
49
|
const multiplier = hasMetadataLine ? 2 : 1;
|
|
42
50
|
return this.metrics.lineHeight * multiplier;
|
|
43
51
|
}
|
|
44
52
|
setOptions(options) {
|
|
53
|
+
if (this.isAdvancedMode()) throw new Error("VirtualizedFileDiff.setOptions cannot be used inside CodeView. Update CodeView options instead.");
|
|
45
54
|
if (options == null) return;
|
|
46
55
|
const { options: previousOptions } = this;
|
|
47
56
|
const optionsChanged = !areOptionsEqual(previousOptions, options);
|
|
48
57
|
const layoutChanged = optionsChanged && hasDiffLayoutOptionChanged(previousOptions, options);
|
|
49
58
|
super.setOptions(options);
|
|
50
|
-
if (layoutChanged) this.resetLayoutCache(
|
|
59
|
+
if (layoutChanged) this.resetLayoutCache({
|
|
60
|
+
forceSimpleRecompute: true,
|
|
61
|
+
includeEstimatedHeights: hasDiffEstimateOptionChanged(previousOptions, options)
|
|
62
|
+
});
|
|
51
63
|
if (optionsChanged) this.forceRenderOverride = true;
|
|
52
64
|
if (optionsChanged && this.isSimpleMode()) this.virtualizer.instanceChanged(this, layoutChanged);
|
|
53
65
|
}
|
|
54
|
-
|
|
66
|
+
setThemeType(themeType) {
|
|
67
|
+
if (this.isAdvancedMode()) throw new Error("VirtualizedFileDiff.setThemeType cannot be used inside CodeView. Update CodeView options instead.");
|
|
68
|
+
super.setThemeType(themeType);
|
|
69
|
+
}
|
|
70
|
+
resetLayoutCache({ forceSimpleRecompute = false, includeEstimatedHeights = false } = {}) {
|
|
55
71
|
this.layoutDirty = true;
|
|
56
|
-
this.cache.
|
|
57
|
-
this.cache.
|
|
58
|
-
this.cache.
|
|
59
|
-
this.
|
|
60
|
-
if (
|
|
72
|
+
if (this.cache.heightDeltas.size > 0) this.cache.heightDeltas.clear();
|
|
73
|
+
if (this.cache.measuredHeightDeltaTotal !== 0) this.cache.measuredHeightDeltaTotal = 0;
|
|
74
|
+
if (this.cache.checkpoints.length > 0) this.cache.checkpoints.length = 0;
|
|
75
|
+
if (this.cache.totalLines !== 0) this.cache.totalLines = 0;
|
|
76
|
+
if (includeEstimatedHeights) {
|
|
77
|
+
this.cache.estimatedSplitHeight = void 0;
|
|
78
|
+
this.cache.estimatedUnifiedHeight = void 0;
|
|
79
|
+
}
|
|
80
|
+
if (this.renderRange != null) this.renderRange = void 0;
|
|
81
|
+
if (forceSimpleRecompute && this.isSimpleMode()) this.computeApproximateSize();
|
|
61
82
|
}
|
|
62
83
|
reconcileHeights() {
|
|
63
84
|
let hasHeightChange = false;
|
|
@@ -86,11 +107,14 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
86
107
|
if ("noNewline" in line.nextElementSibling.dataset) hasMetadata = true;
|
|
87
108
|
measuredHeight += line.nextElementSibling.getBoundingClientRect().height;
|
|
88
109
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
110
|
+
const estimatedHeight = this.getEstimatedLineHeight(hasMetadata);
|
|
111
|
+
const previousDelta = this.cache.heightDeltas.get(lineIndex) ?? 0;
|
|
112
|
+
const nextDelta = measuredHeight - estimatedHeight;
|
|
113
|
+
if (nextDelta === previousDelta) continue;
|
|
91
114
|
hasHeightChange = true;
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
this.cache.measuredHeightDeltaTotal += nextDelta - previousDelta;
|
|
116
|
+
if (nextDelta === 0) this.cache.heightDeltas.delete(lineIndex);
|
|
117
|
+
else this.cache.heightDeltas.set(lineIndex, nextDelta);
|
|
94
118
|
}
|
|
95
119
|
}
|
|
96
120
|
if (hasHeightChange || this.isResizeDebuggingEnabled()) this.computeApproximateSize(true);
|
|
@@ -101,10 +125,23 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
101
125
|
if (dirty) this.top = this.getVirtualizedTop();
|
|
102
126
|
return this.render();
|
|
103
127
|
};
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
prepareCodeViewItem(fileDiff, top, reset) {
|
|
129
|
+
const targetChanged = !areDiffTargetsEqual(this.fileDiff, fileDiff);
|
|
130
|
+
let shouldResetLayoutCache = reset?.resetDiffLayoutCache === true || targetChanged;
|
|
131
|
+
let includeEstimatedHeights = targetChanged || reset?.resetDiffLayoutCache === true && reset.includeEstimatedDiffHeights;
|
|
132
|
+
if (reset?.metrics != null) {
|
|
133
|
+
this.metrics = computeVirtualFileMetrics(reset.metrics);
|
|
134
|
+
shouldResetLayoutCache = true;
|
|
135
|
+
includeEstimatedHeights = true;
|
|
136
|
+
}
|
|
137
|
+
const { collapsed = false } = this.options;
|
|
138
|
+
if (this.currentCollapsed !== collapsed) {
|
|
139
|
+
this.currentCollapsed = collapsed;
|
|
140
|
+
shouldResetLayoutCache = true;
|
|
141
|
+
}
|
|
142
|
+
if (shouldResetLayoutCache) this.resetLayoutCache({ includeEstimatedHeights });
|
|
106
143
|
this.fileDiff = fileDiff;
|
|
107
|
-
this.top =
|
|
144
|
+
this.top = top;
|
|
108
145
|
this.computeApproximateSize();
|
|
109
146
|
return this.height;
|
|
110
147
|
}
|
|
@@ -115,9 +152,8 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
115
152
|
const { disableFileHeader = false, expandUnchanged = false, collapsed = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
|
|
116
153
|
const diffStyle = this.getDiffStyle();
|
|
117
154
|
const hunkSeparators = this.getHunkSeparatorType();
|
|
118
|
-
const hunkSeparatorHeight = this.getHunkSeparatorHeight(hunkSeparators);
|
|
119
|
-
const separatorGap = this.getSeparatorGap(hunkSeparators);
|
|
120
155
|
const targetLineIndex = diffStyle === "split" ? targetLineIndexes[1] : targetLineIndexes[0];
|
|
156
|
+
this.approximateLayoutCheckpoints();
|
|
121
157
|
const checkpoint = this.getLayoutCheckpointBeforeLineIndex(targetLineIndex);
|
|
122
158
|
let top = checkpoint?.top ?? getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
123
159
|
if (collapsed) return {
|
|
@@ -134,16 +170,24 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
134
170
|
callback: ({ hunkIndex, hunk, collapsedBefore, collapsedAfter, deletionLine, additionLine }) => {
|
|
135
171
|
const lineIndex = diffStyle === "split" ? additionLine?.splitLineIndex ?? deletionLine?.splitLineIndex : additionLine?.unifiedLineIndex ?? deletionLine?.unifiedLineIndex;
|
|
136
172
|
if (lineIndex == null) throw new Error("VirtualizedFileDiff.getLinePosition: missing line index data");
|
|
137
|
-
if (collapsedBefore > 0
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
173
|
+
if (collapsedBefore > 0) {
|
|
174
|
+
const separator = getLeadingHunkSeparatorLayout({
|
|
175
|
+
type: hunkSeparators,
|
|
176
|
+
metrics: this.metrics,
|
|
177
|
+
hunkIndex,
|
|
178
|
+
hunkSpecs: hunk?.hunkSpecs
|
|
179
|
+
});
|
|
180
|
+
if (separator != null) {
|
|
181
|
+
top += separator.gapBefore;
|
|
182
|
+
if (targetLineIndex >= lineIndex - collapsedBefore && targetLineIndex < lineIndex) {
|
|
183
|
+
position = {
|
|
184
|
+
top,
|
|
185
|
+
height: separator.height
|
|
186
|
+
};
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
top += separator.height + separator.gapAfter;
|
|
145
190
|
}
|
|
146
|
-
top += hunkSeparatorHeight + separatorGap;
|
|
147
191
|
}
|
|
148
192
|
const lineHeight = this.getLineHeight(lineIndex, (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false));
|
|
149
193
|
if (lineIndex === targetLineIndex) {
|
|
@@ -154,15 +198,21 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
154
198
|
return true;
|
|
155
199
|
}
|
|
156
200
|
top += lineHeight;
|
|
157
|
-
if (collapsedAfter > 0
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
201
|
+
if (collapsedAfter > 0) {
|
|
202
|
+
const separator = getTrailingHunkSeparatorLayout({
|
|
203
|
+
type: hunkSeparators,
|
|
204
|
+
metrics: this.metrics
|
|
205
|
+
});
|
|
206
|
+
if (separator != null) {
|
|
207
|
+
if (targetLineIndex > lineIndex && targetLineIndex <= lineIndex + collapsedAfter) {
|
|
208
|
+
position = {
|
|
209
|
+
top: top + separator.gapBefore,
|
|
210
|
+
height: separator.height
|
|
211
|
+
};
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
top += separator.totalHeight;
|
|
164
215
|
}
|
|
165
|
-
top += separatorGap + hunkSeparatorHeight;
|
|
166
216
|
}
|
|
167
217
|
return false;
|
|
168
218
|
}
|
|
@@ -175,8 +225,7 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
175
225
|
if (collapsed) return;
|
|
176
226
|
const diffStyle = this.getDiffStyle();
|
|
177
227
|
const hunkSeparators = this.getHunkSeparatorType();
|
|
178
|
-
|
|
179
|
-
const separatorGap = this.getSeparatorGap(hunkSeparators);
|
|
228
|
+
this.approximateLayoutCheckpoints();
|
|
180
229
|
const checkpoint = this.getLayoutCheckpointBeforeTop(localViewportTop);
|
|
181
230
|
let top = checkpoint?.top ?? getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
182
231
|
let anchor;
|
|
@@ -189,9 +238,14 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
189
238
|
callback: ({ hunkIndex, hunk, collapsedBefore, collapsedAfter, deletionLine, additionLine }) => {
|
|
190
239
|
const lineIndex = diffStyle === "split" ? additionLine?.splitLineIndex ?? deletionLine?.splitLineIndex : additionLine?.unifiedLineIndex ?? deletionLine?.unifiedLineIndex;
|
|
191
240
|
if (lineIndex == null) throw new Error("VirtualizedFileDiff.getNumericScrollAnchor: missing line index data");
|
|
192
|
-
if (collapsedBefore > 0
|
|
193
|
-
|
|
194
|
-
|
|
241
|
+
if (collapsedBefore > 0) {
|
|
242
|
+
const separator = getLeadingHunkSeparatorLayout({
|
|
243
|
+
type: hunkSeparators,
|
|
244
|
+
metrics: this.metrics,
|
|
245
|
+
hunkIndex,
|
|
246
|
+
hunkSpecs: hunk?.hunkSpecs
|
|
247
|
+
});
|
|
248
|
+
if (separator != null) top += separator.totalHeight;
|
|
195
249
|
}
|
|
196
250
|
if (top >= localViewportTop) {
|
|
197
251
|
if (deletionLine != null) anchor = {
|
|
@@ -208,7 +262,13 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
208
262
|
}
|
|
209
263
|
const lineHeight = this.getLineHeight(lineIndex, (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false));
|
|
210
264
|
top += lineHeight;
|
|
211
|
-
if (collapsedAfter > 0
|
|
265
|
+
if (collapsedAfter > 0) {
|
|
266
|
+
const separator = getTrailingHunkSeparatorLayout({
|
|
267
|
+
type: hunkSeparators,
|
|
268
|
+
metrics: this.metrics
|
|
269
|
+
});
|
|
270
|
+
if (separator != null) top += separator.totalHeight;
|
|
271
|
+
}
|
|
212
272
|
return false;
|
|
213
273
|
}
|
|
214
274
|
});
|
|
@@ -233,15 +293,14 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
233
293
|
}
|
|
234
294
|
cleanUp(recycle = false) {
|
|
235
295
|
if (this.fileContainer != null && this.isSimpleMode()) this.getSimpleVirtualizer()?.disconnect(this.fileContainer);
|
|
236
|
-
if (!recycle) this.resetLayoutCache();
|
|
296
|
+
if (!recycle) this.resetLayoutCache({ includeEstimatedHeights: true });
|
|
237
297
|
this.isSetup = false;
|
|
238
298
|
super.cleanUp(recycle);
|
|
239
299
|
}
|
|
240
300
|
expandHunk = (hunkIndex, direction, expansionLineCountOverride) => {
|
|
241
301
|
this.hunksRenderer.expandHunk(hunkIndex, direction, expansionLineCountOverride);
|
|
242
|
-
this.
|
|
302
|
+
this.resetLayoutCache({ includeEstimatedHeights: true });
|
|
243
303
|
this.computeApproximateSize();
|
|
244
|
-
this.renderRange = void 0;
|
|
245
304
|
this.virtualizer.instanceChanged(this, true);
|
|
246
305
|
};
|
|
247
306
|
setVisibility(visible) {
|
|
@@ -271,52 +330,53 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
271
330
|
this.layoutDirty = false;
|
|
272
331
|
return;
|
|
273
332
|
}
|
|
274
|
-
const { disableFileHeader = false,
|
|
275
|
-
const diffStyle = this.getDiffStyle();
|
|
276
|
-
const hunkSeparators = this.getHunkSeparatorType();
|
|
277
|
-
const hunkSeparatorHeight = this.getHunkSeparatorHeight(hunkSeparators);
|
|
278
|
-
const separatorGap = this.getSeparatorGap(hunkSeparators);
|
|
333
|
+
const { disableFileHeader = false, collapsed = false } = this.options;
|
|
279
334
|
const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
280
|
-
const paddingBottom = getVirtualFilePaddingBottom(this.metrics);
|
|
281
335
|
this.height += headerRegion;
|
|
282
336
|
if (collapsed) {
|
|
283
337
|
this.layoutDirty = false;
|
|
284
338
|
return;
|
|
285
339
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
diff: this.fileDiff,
|
|
289
|
-
diffStyle,
|
|
290
|
-
expandedHunks: expandUnchanged ? true : this.hunksRenderer.getExpandedHunksMap(),
|
|
291
|
-
collapsedContextThreshold,
|
|
292
|
-
callback: ({ hunkIndex, hunk, collapsedBefore, collapsedAfter, deletionLine, additionLine }) => {
|
|
293
|
-
const splitLineIndex = additionLine != null ? additionLine.splitLineIndex : deletionLine.splitLineIndex;
|
|
294
|
-
const unifiedLineIndex = additionLine != null ? additionLine.unifiedLineIndex : deletionLine.unifiedLineIndex;
|
|
295
|
-
const hasMetadata = (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);
|
|
296
|
-
const lineIndex = diffStyle === "split" ? splitLineIndex : unifiedLineIndex;
|
|
297
|
-
this.addLayoutCheckpoint(renderedLineIndex, lineIndex, this.height);
|
|
298
|
-
if (collapsedBefore > 0 && this.hasLeadingHunkSeparator(hunkIndex, hunk?.hunkSpecs, hunkSeparators)) {
|
|
299
|
-
if (hunkIndex > 0) this.height += separatorGap;
|
|
300
|
-
this.height += hunkSeparatorHeight + separatorGap;
|
|
301
|
-
}
|
|
302
|
-
this.height += this.getLineHeight(lineIndex, hasMetadata);
|
|
303
|
-
if (collapsedAfter > 0 && this.hasTrailingHunkSeparator(hunkSeparators)) this.height += separatorGap + hunkSeparatorHeight;
|
|
304
|
-
renderedLineIndex++;
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
this.cache.totalLines = renderedLineIndex;
|
|
308
|
-
if (this.fileDiff.hunks.length > 0) this.height += paddingBottom;
|
|
309
|
-
if (this.fileContainer != null && shouldValidateSize && !isFirstCompute) {
|
|
310
|
-
const rect = this.fileContainer.getBoundingClientRect();
|
|
311
|
-
if (rect.height !== this.height) console.log("VirtualizedFileDiff.computeApproximateSize: computed height doesnt match", {
|
|
312
|
-
name: this.fileDiff.name,
|
|
313
|
-
elementHeight: rect.height,
|
|
314
|
-
computedHeight: this.height
|
|
315
|
-
});
|
|
316
|
-
else console.log("VirtualizedFileDiff.computeApproximateSize: computed height IS CORRECT");
|
|
317
|
-
}
|
|
340
|
+
this.height = this.getActiveEstimatedHeight() + this.cache.measuredHeightDeltaTotal;
|
|
341
|
+
if (shouldValidateSize && !isFirstCompute) this.validateComputedHeight();
|
|
318
342
|
this.layoutDirty = false;
|
|
319
343
|
}
|
|
344
|
+
getActiveEstimatedHeight() {
|
|
345
|
+
this.ensureEstimatedDiffHeights();
|
|
346
|
+
const estimatedHeight = this.getDiffStyle() === "split" ? this.cache.estimatedSplitHeight : this.cache.estimatedUnifiedHeight;
|
|
347
|
+
if (estimatedHeight == null) throw new Error("VirtualizedFileDiff.getActiveEstimatedHeight: missing estimated height");
|
|
348
|
+
return estimatedHeight;
|
|
349
|
+
}
|
|
350
|
+
ensureEstimatedDiffHeights() {
|
|
351
|
+
if (this.fileDiff == null) {
|
|
352
|
+
this.cache.estimatedSplitHeight = void 0;
|
|
353
|
+
this.cache.estimatedUnifiedHeight = void 0;
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (this.cache.estimatedSplitHeight != null && this.cache.estimatedUnifiedHeight != null) return;
|
|
357
|
+
const { disableFileHeader = false, expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
|
|
358
|
+
const { splitHeight, unifiedHeight } = computeEstimatedDiffHeights({
|
|
359
|
+
fileDiff: this.fileDiff,
|
|
360
|
+
metrics: this.metrics,
|
|
361
|
+
disableFileHeader,
|
|
362
|
+
hunkSeparators: this.getHunkSeparatorType(),
|
|
363
|
+
expandUnchanged,
|
|
364
|
+
expandedHunks: this.hunksRenderer.getExpandedHunksMap(),
|
|
365
|
+
collapsedContextThreshold
|
|
366
|
+
});
|
|
367
|
+
this.cache.estimatedSplitHeight = splitHeight;
|
|
368
|
+
this.cache.estimatedUnifiedHeight = unifiedHeight;
|
|
369
|
+
}
|
|
370
|
+
validateComputedHeight() {
|
|
371
|
+
if (this.fileContainer == null || this.fileDiff == null) return;
|
|
372
|
+
const rect = this.fileContainer.getBoundingClientRect();
|
|
373
|
+
if (rect.height !== this.height) console.log("VirtualizedFileDiff.computeApproximateSize: computed height doesnt match", {
|
|
374
|
+
name: this.fileDiff.name,
|
|
375
|
+
elementHeight: rect.height,
|
|
376
|
+
computedHeight: this.height
|
|
377
|
+
});
|
|
378
|
+
else console.log("VirtualizedFileDiff.computeApproximateSize: computed height IS CORRECT");
|
|
379
|
+
}
|
|
320
380
|
render({ fileContainer, oldFile, newFile, fileDiff, forceRender = false,...props } = {}) {
|
|
321
381
|
const { forceRenderOverride, isSetup } = this;
|
|
322
382
|
this.forceRenderOverride = void 0;
|
|
@@ -380,31 +440,92 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
380
440
|
getHunkSeparatorType() {
|
|
381
441
|
return getOptionHunkSeparatorType(this.options.hunkSeparators);
|
|
382
442
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
443
|
+
approximateLayoutCheckpoints() {
|
|
444
|
+
if (this.cache.checkpoints.length > 0 || this.fileDiff == null || this.fileDiff.hunks.length === 0 || this.options.collapsed === true) return;
|
|
445
|
+
const { disableFileHeader = false, expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
|
|
446
|
+
const diffStyle = this.getDiffStyle();
|
|
447
|
+
const hunkSeparators = this.getHunkSeparatorType();
|
|
448
|
+
const expandedHunks = expandUnchanged ? true : this.hunksRenderer.getExpandedHunksMap();
|
|
449
|
+
const heightDeltaPrefix = createHeightDeltaPrefix(this.cache.heightDeltas);
|
|
450
|
+
let top = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
451
|
+
let renderedLineIndex = 0;
|
|
452
|
+
const processRows = ({ rowCount, startLineIndex, preSeparatorHeight = 0, postSeparatorHeight = 0, metadataOffsets = [] }) => {
|
|
453
|
+
if (rowCount <= 0) return;
|
|
454
|
+
const blockStart = renderedLineIndex;
|
|
455
|
+
const blockEnd = renderedLineIndex + rowCount;
|
|
456
|
+
let nextCheckpoint = getNextCheckpointIndex(blockStart);
|
|
457
|
+
while (nextCheckpoint < blockEnd) {
|
|
458
|
+
const offset = nextCheckpoint - blockStart;
|
|
459
|
+
const checkpointTop = top + (offset > 0 ? preSeparatorHeight : 0) + offset * this.metrics.lineHeight + countMetadataOffsetsBefore(metadataOffsets, offset) * this.metrics.lineHeight + sumHeightDeltas(heightDeltaPrefix, startLineIndex, startLineIndex + offset);
|
|
460
|
+
this.cache.checkpoints.push({
|
|
461
|
+
renderedLineIndex: nextCheckpoint,
|
|
462
|
+
lineIndex: startLineIndex + offset,
|
|
463
|
+
top: checkpointTop
|
|
464
|
+
});
|
|
465
|
+
nextCheckpoint += LAYOUT_CHECKPOINT_INTERVAL;
|
|
466
|
+
}
|
|
467
|
+
top += preSeparatorHeight + rowCount * this.metrics.lineHeight + metadataOffsets.length * this.metrics.lineHeight + sumHeightDeltas(heightDeltaPrefix, startLineIndex, startLineIndex + rowCount) + postSeparatorHeight;
|
|
468
|
+
renderedLineIndex = blockEnd;
|
|
469
|
+
};
|
|
470
|
+
for (let hunkIndex = 0; hunkIndex < this.fileDiff.hunks.length; hunkIndex++) {
|
|
471
|
+
const hunk = this.fileDiff.hunks[hunkIndex];
|
|
472
|
+
if (hunk == null) throw new Error("VirtualizedFileDiff.approximateLayoutCheckpoints: invalid hunk index");
|
|
473
|
+
const leadingRegion = getExpandedRegion({
|
|
474
|
+
isPartial: this.fileDiff.isPartial,
|
|
475
|
+
rangeSize: hunk.collapsedBefore,
|
|
476
|
+
expandedHunks,
|
|
477
|
+
hunkIndex,
|
|
478
|
+
collapsedContextThreshold
|
|
479
|
+
});
|
|
480
|
+
const leadingSeparatorHeight = leadingRegion.collapsedLines > 0 ? getLeadingHunkSeparatorLayout({
|
|
481
|
+
type: hunkSeparators,
|
|
482
|
+
metrics: this.metrics,
|
|
483
|
+
hunkIndex,
|
|
484
|
+
hunkSpecs: hunk.hunkSpecs
|
|
485
|
+
})?.totalHeight ?? 0 : 0;
|
|
486
|
+
processRows({
|
|
487
|
+
rowCount: leadingRegion.fromStart,
|
|
488
|
+
startLineIndex: (diffStyle === "split" ? hunk.splitLineStart : hunk.unifiedLineStart) - leadingRegion.rangeSize
|
|
489
|
+
});
|
|
490
|
+
let pendingLeadingSeparatorHeight = leadingSeparatorHeight;
|
|
491
|
+
processRows({
|
|
492
|
+
rowCount: leadingRegion.fromEnd,
|
|
493
|
+
startLineIndex: (diffStyle === "split" ? hunk.splitLineStart : hunk.unifiedLineStart) - leadingRegion.fromEnd,
|
|
494
|
+
preSeparatorHeight: pendingLeadingSeparatorHeight
|
|
495
|
+
});
|
|
496
|
+
if (leadingRegion.fromEnd > 0) pendingLeadingSeparatorHeight = 0;
|
|
497
|
+
const trailingRegion = getTrailingExpandedRegion({
|
|
498
|
+
fileDiff: this.fileDiff,
|
|
499
|
+
hunk,
|
|
500
|
+
hunkIndex,
|
|
501
|
+
expandedHunks,
|
|
502
|
+
collapsedContextThreshold
|
|
503
|
+
});
|
|
504
|
+
const trailingSeparatorHeight = trailingRegion != null && trailingRegion.collapsedLines > 0 ? getTrailingHunkSeparatorLayout({
|
|
505
|
+
type: hunkSeparators,
|
|
506
|
+
metrics: this.metrics
|
|
507
|
+
})?.totalHeight ?? 0 : 0;
|
|
508
|
+
const trailingExpandedCount = trailingRegion != null ? trailingRegion.fromStart + trailingRegion.fromEnd : 0;
|
|
509
|
+
const hunkBodyRowCount = diffStyle === "split" ? hunk.splitLineCount : hunk.unifiedLineCount;
|
|
510
|
+
const hunkBodyStartLineIndex = diffStyle === "split" ? hunk.splitLineStart : hunk.unifiedLineStart;
|
|
511
|
+
processRows({
|
|
512
|
+
rowCount: hunkBodyRowCount,
|
|
513
|
+
startLineIndex: hunkBodyStartLineIndex,
|
|
514
|
+
preSeparatorHeight: pendingLeadingSeparatorHeight,
|
|
515
|
+
postSeparatorHeight: trailingExpandedCount === 0 ? trailingSeparatorHeight : 0,
|
|
516
|
+
metadataOffsets: getHunkMetadataOffsets({
|
|
517
|
+
diffStyle,
|
|
518
|
+
hunk,
|
|
519
|
+
rowCount: hunkBodyRowCount
|
|
520
|
+
})
|
|
521
|
+
});
|
|
522
|
+
if (trailingRegion != null && trailingExpandedCount > 0) processRows({
|
|
523
|
+
rowCount: trailingExpandedCount,
|
|
524
|
+
startLineIndex: hunkBodyStartLineIndex + hunkBodyRowCount,
|
|
525
|
+
postSeparatorHeight: trailingSeparatorHeight
|
|
526
|
+
});
|
|
396
527
|
}
|
|
397
|
-
|
|
398
|
-
hasTrailingHunkSeparator(type = this.getHunkSeparatorType()) {
|
|
399
|
-
return type !== "simple" && type !== "metadata";
|
|
400
|
-
}
|
|
401
|
-
addLayoutCheckpoint(renderedLineIndex, lineIndex, top) {
|
|
402
|
-
if (renderedLineIndex % LAYOUT_CHECKPOINT_INTERVAL !== 0) return;
|
|
403
|
-
this.cache.checkpoints.push({
|
|
404
|
-
renderedLineIndex,
|
|
405
|
-
lineIndex,
|
|
406
|
-
top
|
|
407
|
-
});
|
|
528
|
+
this.cache.totalLines = renderedLineIndex;
|
|
408
529
|
}
|
|
409
530
|
getLayoutCheckpointBeforeLineIndex(lineIndex) {
|
|
410
531
|
if (lineIndex <= 0 || this.cache.checkpoints.length === 0) return;
|
|
@@ -442,43 +563,25 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
442
563
|
if (checkpoint.renderedLineIndex % hunkLineCount === 0) return checkpoint;
|
|
443
564
|
}
|
|
444
565
|
}
|
|
445
|
-
getExpandedRegion(isPartial, hunkIndex, rangeSize) {
|
|
446
|
-
if (rangeSize <= 0 || isPartial) return {
|
|
447
|
-
fromStart: 0,
|
|
448
|
-
fromEnd: 0,
|
|
449
|
-
collapsedLines: Math.max(rangeSize, 0),
|
|
450
|
-
renderAll: false
|
|
451
|
-
};
|
|
452
|
-
const { expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
|
|
453
|
-
if (expandUnchanged || rangeSize <= collapsedContextThreshold) return {
|
|
454
|
-
fromStart: rangeSize,
|
|
455
|
-
fromEnd: 0,
|
|
456
|
-
collapsedLines: 0,
|
|
457
|
-
renderAll: true
|
|
458
|
-
};
|
|
459
|
-
const region = this.hunksRenderer.getExpandedHunk(hunkIndex);
|
|
460
|
-
const fromStart = Math.min(Math.max(region.fromStart, 0), rangeSize);
|
|
461
|
-
const fromEnd = Math.min(Math.max(region.fromEnd, 0), rangeSize);
|
|
462
|
-
const expandedCount = fromStart + fromEnd;
|
|
463
|
-
const renderAll = expandedCount >= rangeSize;
|
|
464
|
-
return {
|
|
465
|
-
fromStart,
|
|
466
|
-
fromEnd,
|
|
467
|
-
collapsedLines: Math.max(rangeSize - expandedCount, 0),
|
|
468
|
-
renderAll
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
566
|
getExpandedLineCount(fileDiff, diffStyle) {
|
|
472
567
|
let count = 0;
|
|
473
568
|
if (fileDiff.isPartial) {
|
|
474
569
|
for (const hunk of fileDiff.hunks) count += diffStyle === "split" ? hunk.splitLineCount : hunk.unifiedLineCount;
|
|
475
570
|
return count;
|
|
476
571
|
}
|
|
572
|
+
const { expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
|
|
573
|
+
const expandedHunks = expandUnchanged ? true : this.hunksRenderer.getExpandedHunksMap();
|
|
477
574
|
for (const [hunkIndex, hunk] of fileDiff.hunks.entries()) {
|
|
478
575
|
const hunkCount = diffStyle === "split" ? hunk.splitLineCount : hunk.unifiedLineCount;
|
|
479
576
|
count += hunkCount;
|
|
480
577
|
const collapsedBefore = Math.max(hunk.collapsedBefore, 0);
|
|
481
|
-
const { fromStart, fromEnd, renderAll } =
|
|
578
|
+
const { fromStart, fromEnd, renderAll } = getExpandedRegion({
|
|
579
|
+
isPartial: fileDiff.isPartial,
|
|
580
|
+
rangeSize: collapsedBefore,
|
|
581
|
+
expandedHunks,
|
|
582
|
+
hunkIndex,
|
|
583
|
+
collapsedContextThreshold
|
|
584
|
+
});
|
|
482
585
|
if (collapsedBefore > 0) count += renderAll ? collapsedBefore : fromStart + fromEnd;
|
|
483
586
|
}
|
|
484
587
|
const lastHunk = fileDiff.hunks.at(-1);
|
|
@@ -488,7 +591,13 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
488
591
|
if (lastHunk != null && additionRemaining !== deletionRemaining) throw new Error(`VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`);
|
|
489
592
|
const trailingRangeSize = Math.min(additionRemaining, deletionRemaining);
|
|
490
593
|
if (lastHunk != null && trailingRangeSize > 0) {
|
|
491
|
-
const { fromStart, renderAll } =
|
|
594
|
+
const { fromStart, renderAll } = getExpandedRegion({
|
|
595
|
+
isPartial: fileDiff.isPartial,
|
|
596
|
+
rangeSize: trailingRangeSize,
|
|
597
|
+
expandedHunks,
|
|
598
|
+
hunkIndex: fileDiff.hunks.length,
|
|
599
|
+
collapsedContextThreshold
|
|
600
|
+
});
|
|
492
601
|
count += renderAll ? trailingRangeSize : fromStart;
|
|
493
602
|
}
|
|
494
603
|
}
|
|
@@ -499,9 +608,8 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
499
608
|
const { hunkLineCount, lineHeight } = this.metrics;
|
|
500
609
|
const diffStyle = this.getDiffStyle();
|
|
501
610
|
const hunkSeparators = this.getHunkSeparatorType();
|
|
502
|
-
const hunkSeparatorHeight = this.getHunkSeparatorHeight(hunkSeparators);
|
|
503
611
|
const fileHeight = this.height;
|
|
504
|
-
|
|
612
|
+
let lineCount = this.cache.totalLines > 0 ? this.cache.totalLines : this.getExpandedLineCount(fileDiff, diffStyle);
|
|
505
613
|
const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
506
614
|
const paddingBottom = fileDiff.hunks.length > 0 ? getVirtualFilePaddingBottom(this.metrics) : 0;
|
|
507
615
|
if (fileTop < top - fileHeight || fileTop > bottom) return {
|
|
@@ -516,13 +624,14 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
516
624
|
bufferBefore: 0,
|
|
517
625
|
bufferAfter: 0
|
|
518
626
|
};
|
|
627
|
+
this.approximateLayoutCheckpoints();
|
|
628
|
+
lineCount = this.cache.totalLines > 0 ? this.cache.totalLines : lineCount;
|
|
519
629
|
const estimatedTargetLines = Math.ceil(Math.max(bottom - top, 0) / lineHeight);
|
|
520
630
|
const totalLines = Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount + hunkLineCount;
|
|
521
631
|
const totalHunks = totalLines / hunkLineCount;
|
|
522
632
|
const overflowHunks = totalHunks;
|
|
523
633
|
const hunkOffsets = [];
|
|
524
634
|
const viewportCenter = (top + bottom) / 2;
|
|
525
|
-
const separatorGap = this.getSeparatorGap(hunkSeparators);
|
|
526
635
|
const checkpoint = this.getLayoutCheckpointBeforeTop(Math.max(0, top - fileTop - totalLines * lineHeight * 2), hunkLineCount);
|
|
527
636
|
let absoluteLineTop = fileTop + (checkpoint?.top ?? headerRegion);
|
|
528
637
|
let currentLine = checkpoint?.renderedLineIndex ?? 0;
|
|
@@ -539,7 +648,12 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
539
648
|
const splitLineIndex = additionLine != null ? additionLine.splitLineIndex : deletionLine.splitLineIndex;
|
|
540
649
|
const unifiedLineIndex = additionLine != null ? additionLine.unifiedLineIndex : deletionLine.unifiedLineIndex;
|
|
541
650
|
const hasMetadata = (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);
|
|
542
|
-
const gapAdjustment = collapsedBefore > 0
|
|
651
|
+
const gapAdjustment = (collapsedBefore > 0 ? getLeadingHunkSeparatorLayout({
|
|
652
|
+
type: hunkSeparators,
|
|
653
|
+
metrics: this.metrics,
|
|
654
|
+
hunkIndex,
|
|
655
|
+
hunkSpecs: hunk?.hunkSpecs
|
|
656
|
+
}) : void 0)?.totalHeight ?? 0;
|
|
543
657
|
absoluteLineTop += gapAdjustment;
|
|
544
658
|
const isAtHunkBoundary = currentLine % hunkLineCount === 0;
|
|
545
659
|
const currentHunk = Math.floor(currentLine / hunkLineCount);
|
|
@@ -556,7 +670,10 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
556
670
|
if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
|
|
557
671
|
currentLine++;
|
|
558
672
|
absoluteLineTop += lineHeight$1;
|
|
559
|
-
if (collapsedAfter > 0
|
|
673
|
+
if (collapsedAfter > 0) absoluteLineTop += getTrailingHunkSeparatorLayout({
|
|
674
|
+
type: hunkSeparators,
|
|
675
|
+
metrics: this.metrics
|
|
676
|
+
})?.totalHeight ?? 0;
|
|
560
677
|
return false;
|
|
561
678
|
}
|
|
562
679
|
});
|
|
@@ -582,9 +699,79 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
582
699
|
};
|
|
583
700
|
}
|
|
584
701
|
};
|
|
702
|
+
function createHeightDeltaPrefix(heightDeltas) {
|
|
703
|
+
const entries = Array.from(heightDeltas).sort((a, b) => a[0] - b[0]);
|
|
704
|
+
const lineIndexes = [];
|
|
705
|
+
const prefixTotals = [0];
|
|
706
|
+
let total = 0;
|
|
707
|
+
for (const [lineIndex, delta] of entries) {
|
|
708
|
+
lineIndexes.push(lineIndex);
|
|
709
|
+
total += delta;
|
|
710
|
+
prefixTotals.push(total);
|
|
711
|
+
}
|
|
712
|
+
return {
|
|
713
|
+
lineIndexes,
|
|
714
|
+
prefixTotals
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function sumHeightDeltas({ lineIndexes, prefixTotals }, startLineIndex, endLineIndex) {
|
|
718
|
+
if (startLineIndex >= endLineIndex || lineIndexes.length === 0) return 0;
|
|
719
|
+
const start = lowerBound(lineIndexes, startLineIndex);
|
|
720
|
+
return (prefixTotals[lowerBound(lineIndexes, endLineIndex)] ?? 0) - (prefixTotals[start] ?? 0);
|
|
721
|
+
}
|
|
722
|
+
function lowerBound(values, target) {
|
|
723
|
+
let low = 0;
|
|
724
|
+
let high = values.length;
|
|
725
|
+
while (low < high) {
|
|
726
|
+
const mid = low + high >> 1;
|
|
727
|
+
const value = values[mid];
|
|
728
|
+
if (value == null) throw new Error("VirtualizedFileDiff: invalid prefix index");
|
|
729
|
+
if (value < target) low = mid + 1;
|
|
730
|
+
else high = mid;
|
|
731
|
+
}
|
|
732
|
+
return low;
|
|
733
|
+
}
|
|
734
|
+
function getNextCheckpointIndex(renderedLineIndex) {
|
|
735
|
+
return Math.ceil(renderedLineIndex / LAYOUT_CHECKPOINT_INTERVAL) * LAYOUT_CHECKPOINT_INTERVAL;
|
|
736
|
+
}
|
|
737
|
+
function countMetadataOffsetsBefore(metadataOffsets, offset) {
|
|
738
|
+
let count = 0;
|
|
739
|
+
for (const metadataOffset of metadataOffsets) if (metadataOffset < offset) count++;
|
|
740
|
+
return count;
|
|
741
|
+
}
|
|
742
|
+
function getHunkMetadataOffsets({ diffStyle, hunk, rowCount }) {
|
|
743
|
+
if (rowCount <= 0 || !hunk.noEOFCRAdditions && !hunk.noEOFCRDeletions) return [];
|
|
744
|
+
const lastContent = hunk.hunkContent.at(-1);
|
|
745
|
+
if (lastContent == null) return [];
|
|
746
|
+
if (lastContent.type === "context") return [rowCount - 1];
|
|
747
|
+
const splitCount = Math.max(lastContent.deletions, lastContent.additions);
|
|
748
|
+
const unifiedCount = lastContent.deletions + lastContent.additions;
|
|
749
|
+
if (diffStyle === "split") return splitCount > 0 && (hunk.noEOFCRAdditions || hunk.noEOFCRDeletions) ? [rowCount - 1] : [];
|
|
750
|
+
const offsets = [];
|
|
751
|
+
const contentStartOffset = rowCount - unifiedCount;
|
|
752
|
+
if (lastContent.deletions > 0 && hunk.noEOFCRDeletions) offsets.push(contentStartOffset + lastContent.deletions - 1);
|
|
753
|
+
if (lastContent.additions > 0 && hunk.noEOFCRAdditions) offsets.push(rowCount - 1);
|
|
754
|
+
return offsets;
|
|
755
|
+
}
|
|
756
|
+
function getTrailingExpandedRegion({ fileDiff, hunk, hunkIndex, expandedHunks, collapsedContextThreshold }) {
|
|
757
|
+
if (hunkIndex !== fileDiff.hunks.length - 1 || !hasFinalHunk(fileDiff)) return;
|
|
758
|
+
const additionRemaining = fileDiff.additionLines.length - (hunk.additionLineIndex + hunk.additionCount);
|
|
759
|
+
const deletionRemaining = fileDiff.deletionLines.length - (hunk.deletionLineIndex + hunk.deletionCount);
|
|
760
|
+
if (additionRemaining !== deletionRemaining) throw new Error(`VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`);
|
|
761
|
+
return getExpandedRegion({
|
|
762
|
+
isPartial: fileDiff.isPartial,
|
|
763
|
+
rangeSize: Math.min(additionRemaining, deletionRemaining),
|
|
764
|
+
expandedHunks,
|
|
765
|
+
hunkIndex: fileDiff.hunks.length,
|
|
766
|
+
collapsedContextThreshold
|
|
767
|
+
});
|
|
768
|
+
}
|
|
585
769
|
function hasDiffLayoutOptionChanged(previousOptions, nextOptions) {
|
|
586
770
|
return (previousOptions.diffStyle ?? "split") !== (nextOptions.diffStyle ?? "split") || (previousOptions.overflow ?? "scroll") !== (nextOptions.overflow ?? "scroll") || (previousOptions.collapsed ?? false) !== (nextOptions.collapsed ?? false) || (previousOptions.disableLineNumbers ?? false) !== (nextOptions.disableLineNumbers ?? false) || (previousOptions.disableFileHeader ?? false) !== (nextOptions.disableFileHeader ?? false) || (previousOptions.diffIndicators ?? "bars") !== (nextOptions.diffIndicators ?? "bars") || (previousOptions.hunkSeparators ?? "line-info") !== (nextOptions.hunkSeparators ?? "line-info") || (previousOptions.expandUnchanged ?? false) !== (nextOptions.expandUnchanged ?? false) || (previousOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD) !== (nextOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD) || previousOptions.unsafeCSS !== nextOptions.unsafeCSS;
|
|
587
771
|
}
|
|
772
|
+
function hasDiffEstimateOptionChanged(previousOptions, nextOptions) {
|
|
773
|
+
return (previousOptions.disableFileHeader ?? false) !== (nextOptions.disableFileHeader ?? false) || (previousOptions.hunkSeparators ?? "line-info") !== (nextOptions.hunkSeparators ?? "line-info") || (previousOptions.expandUnchanged ?? false) !== (nextOptions.expandUnchanged ?? false) || (previousOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD) !== (nextOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD);
|
|
774
|
+
}
|
|
588
775
|
function getOptionHunkSeparatorType(hunkSeparators) {
|
|
589
776
|
return typeof hunkSeparators === "function" ? "custom" : hunkSeparators ?? "line-info";
|
|
590
777
|
}
|