@pierre/diffs 1.1.0-beta.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -18
- package/dist/components/AdvancedVirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/AdvancedVirtualizedFileDiff.js +2 -7
- package/dist/components/AdvancedVirtualizedFileDiff.js.map +1 -1
- package/dist/components/AdvancedVirtualizer.js +1 -1
- package/dist/components/AdvancedVirtualizer.js.map +1 -1
- package/dist/components/File.d.ts +17 -7
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +111 -54
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +32 -14
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +156 -81
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts +60 -0
- package/dist/components/UnresolvedFile.d.ts.map +1 -0
- package/dist/components/UnresolvedFile.js +280 -0
- package/dist/components/UnresolvedFile.js.map +1 -0
- package/dist/components/VirtualizedFile.js +8 -5
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +15 -11
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +3 -1
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +50 -24
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/highlighter/shared_highlighter.d.ts +4 -2
- package/dist/highlighter/shared_highlighter.d.ts.map +1 -1
- package/dist/highlighter/shared_highlighter.js +15 -7
- package/dist/highlighter/shared_highlighter.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.js +8 -6
- package/dist/managers/InteractionManager.d.ts +146 -0
- package/dist/managers/InteractionManager.d.ts.map +1 -0
- package/dist/managers/InteractionManager.js +813 -0
- package/dist/managers/InteractionManager.js.map +1 -0
- package/dist/managers/ResizeManager.d.ts +0 -2
- package/dist/managers/ResizeManager.d.ts.map +1 -1
- package/dist/managers/ResizeManager.js +43 -32
- package/dist/managers/ResizeManager.js.map +1 -1
- package/dist/react/File.d.ts +2 -0
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +3 -1
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +2 -0
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -1
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +2 -0
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -1
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +2 -0
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -1
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/UnresolvedFile.d.ts +36 -0
- package/dist/react/UnresolvedFile.d.ts.map +1 -0
- package/dist/react/UnresolvedFile.js +42 -0
- package/dist/react/UnresolvedFile.js.map +1 -0
- package/dist/react/constants.d.ts +3 -2
- package/dist/react/constants.d.ts.map +1 -1
- package/dist/react/constants.js +3 -2
- package/dist/react/constants.js.map +1 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +3 -2
- package/dist/react/types.d.ts +11 -2
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.d.ts +16 -5
- package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.js +34 -7
- package/dist/react/utils/renderDiffChildren.js.map +1 -1
- package/dist/react/utils/renderFileChildren.d.ts +5 -1
- package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
- package/dist/react/utils/renderFileChildren.js +13 -7
- package/dist/react/utils/renderFileChildren.js.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +1 -2
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +2 -2
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +1 -2
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.d.ts +33 -0
- package/dist/react/utils/useUnresolvedFileInstance.d.ts.map +1 -0
- package/dist/react/utils/useUnresolvedFileInstance.js +87 -0
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -0
- package/dist/renderers/DiffHunksRenderer.d.ts +50 -6
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +145 -45
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.js +1 -1
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +46 -0
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -0
- package/dist/renderers/UnresolvedFileHunksRenderer.js +207 -0
- package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -0
- package/dist/shiki-stream/stream.d.ts +1 -1
- package/dist/shiki-stream/stream.d.ts.map +1 -1
- package/dist/shiki-stream/stream.js.map +1 -1
- package/dist/shiki-stream/tokenizer.d.ts +1 -1
- package/dist/shiki-stream/tokenizer.d.ts.map +1 -1
- package/dist/shiki-stream/tokenizer.js.map +1 -1
- package/dist/shiki-stream/types.d.ts +1 -1
- package/dist/shiki-stream/types.d.ts.map +1 -1
- package/dist/sprite.d.ts +2 -2
- package/dist/sprite.d.ts.map +1 -1
- package/dist/sprite.js +3 -0
- package/dist/sprite.js.map +1 -1
- package/dist/ssr/index.d.ts +3 -3
- package/dist/ssr/index.js +2 -2
- package/dist/ssr/preloadDiffs.d.ts +23 -14
- package/dist/ssr/preloadDiffs.d.ts.map +1 -1
- package/dist/ssr/preloadDiffs.js +40 -14
- package/dist/ssr/preloadDiffs.js.map +1 -1
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +29 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/areMergeConflictActionsEqual.d.ts +7 -0
- package/dist/utils/areMergeConflictActionsEqual.d.ts.map +1 -0
- package/dist/utils/areMergeConflictActionsEqual.js +11 -0
- package/dist/utils/areMergeConflictActionsEqual.js.map +1 -0
- package/dist/utils/arePrePropertiesEqual.js +10 -1
- package/dist/utils/arePrePropertiesEqual.js.map +1 -1
- package/dist/utils/areSelectionPointsEqual.d.ts +7 -0
- package/dist/utils/areSelectionPointsEqual.d.ts.map +1 -0
- package/dist/utils/areSelectionPointsEqual.js +8 -0
- package/dist/utils/areSelectionPointsEqual.js.map +1 -0
- package/dist/utils/areSelectionsEqual.d.ts +1 -1
- package/dist/utils/areSelectionsEqual.d.ts.map +1 -1
- package/dist/utils/areSelectionsEqual.js.map +1 -1
- package/dist/utils/createFileHeaderElement.js +5 -2
- package/dist/utils/createFileHeaderElement.js.map +1 -1
- package/dist/utils/createGutterUtilityContentNode.d.ts +5 -0
- package/dist/utils/createGutterUtilityContentNode.d.ts.map +1 -0
- package/dist/utils/createGutterUtilityContentNode.js +15 -0
- package/dist/utils/createGutterUtilityContentNode.js.map +1 -0
- package/dist/utils/createGutterUtilityElement.d.ts +7 -0
- package/dist/utils/createGutterUtilityElement.d.ts.map +1 -0
- package/dist/utils/createGutterUtilityElement.js +20 -0
- package/dist/utils/createGutterUtilityElement.js.map +1 -0
- package/dist/utils/createPreElement.d.ts +2 -1
- package/dist/utils/createPreElement.d.ts.map +1 -1
- package/dist/utils/createPreElement.js +2 -1
- package/dist/utils/createPreElement.js.map +1 -1
- package/dist/utils/createSeparator.js +1 -1
- package/dist/utils/createSeparator.js.map +1 -1
- package/dist/utils/createWindowFromScrollPosition.js +12 -11
- package/dist/utils/createWindowFromScrollPosition.js.map +1 -1
- package/dist/utils/getHighlighterOptions.d.ts +7 -2
- package/dist/utils/getHighlighterOptions.d.ts.map +1 -1
- package/dist/utils/getHighlighterOptions.js +3 -2
- package/dist/utils/getHighlighterOptions.js.map +1 -1
- package/dist/utils/getMergeConflictActionSlotName.d.ts +16 -0
- package/dist/utils/getMergeConflictActionSlotName.d.ts.map +1 -0
- package/dist/utils/getMergeConflictActionSlotName.js +8 -0
- package/dist/utils/getMergeConflictActionSlotName.js.map +1 -0
- package/dist/utils/getMergeConflictLineTypes.d.ts +15 -0
- package/dist/utils/getMergeConflictLineTypes.d.ts.map +1 -0
- package/dist/utils/getMergeConflictLineTypes.js +81 -0
- package/dist/utils/getMergeConflictLineTypes.js.map +1 -0
- package/dist/utils/getOrCreateCodeNode.d.ts +3 -1
- package/dist/utils/getOrCreateCodeNode.d.ts.map +1 -1
- package/dist/utils/getOrCreateCodeNode.js +5 -3
- package/dist/utils/getOrCreateCodeNode.js.map +1 -1
- package/dist/utils/hast_utils.d.ts +2 -2
- package/dist/utils/hast_utils.d.ts.map +1 -1
- package/dist/utils/hast_utils.js +3 -2
- package/dist/utils/hast_utils.js.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts +26 -0
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -0
- package/dist/utils/parseMergeConflictDiffFromFile.js +143 -0
- package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -0
- package/dist/utils/resolveMergeConflict.d.ts +7 -0
- package/dist/utils/resolveMergeConflict.d.ts.map +1 -0
- package/dist/utils/resolveMergeConflict.js +30 -0
- package/dist/utils/resolveMergeConflict.js.map +1 -0
- package/dist/utils/resolveVirtualFileMetrics.js +1 -0
- package/dist/utils/resolveVirtualFileMetrics.js.map +1 -1
- package/dist/utils/setWrapperNodeProps.d.ts +2 -1
- package/dist/utils/setWrapperNodeProps.d.ts.map +1 -1
- package/dist/utils/setWrapperNodeProps.js +5 -1
- package/dist/utils/setWrapperNodeProps.js.map +1 -1
- package/dist/worker/WorkerPoolManager.d.ts +4 -2
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +16 -9
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +3 -1
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/wasm-BlUZCxHM.js +10 -0
- package/dist/worker/wasm-BlUZCxHM.js.map +1 -0
- package/dist/worker/worker-portable.js +10546 -10106
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +27 -19
- package/dist/worker/worker.js.map +1 -1
- package/package.json +3 -7
- package/dist/managers/LineSelectionManager.d.ts +0 -64
- package/dist/managers/LineSelectionManager.d.ts.map +0 -1
- package/dist/managers/LineSelectionManager.js +0 -270
- package/dist/managers/LineSelectionManager.js.map +0 -1
- package/dist/managers/MouseEventManager.d.ts +0 -71
- package/dist/managers/MouseEventManager.d.ts.map +0 -1
- package/dist/managers/MouseEventManager.js +0 -358
- package/dist/managers/MouseEventManager.js.map +0 -1
- package/dist/themes/pierre-dark.js +0 -1328
- package/dist/themes/pierre-dark.js.map +0 -1
- package/dist/themes/pierre-light.js +0 -1328
- package/dist/themes/pierre-light.js.map +0 -1
- package/dist/utils/createHoverContentNode.d.ts +0 -5
- package/dist/utils/createHoverContentNode.d.ts.map +0 -1
- package/dist/utils/createHoverContentNode.js +0 -15
- package/dist/utils/createHoverContentNode.js.map +0 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { DEFAULT_THEMES } from "../constants.js";
|
|
2
|
+
import { pluckInteractionOptions } from "../managers/InteractionManager.js";
|
|
3
|
+
import { areFilesEqual } from "../utils/areFilesEqual.js";
|
|
4
|
+
import { createAnnotationWrapperNode } from "../utils/createAnnotationWrapperNode.js";
|
|
5
|
+
import { FileDiff } from "./FileDiff.js";
|
|
6
|
+
import { getMergeConflictActionSlotName } from "../utils/getMergeConflictActionSlotName.js";
|
|
7
|
+
import { getMergeConflictActionAnchor, parseMergeConflictDiffFromFile } from "../utils/parseMergeConflictDiffFromFile.js";
|
|
8
|
+
import { UnresolvedFileHunksRenderer } from "../renderers/UnresolvedFileHunksRenderer.js";
|
|
9
|
+
import { areMergeConflictActionsEqual } from "../utils/areMergeConflictActionsEqual.js";
|
|
10
|
+
import { resolveMergeConflict } from "../utils/resolveMergeConflict.js";
|
|
11
|
+
|
|
12
|
+
//#region src/components/UnresolvedFile.ts
|
|
13
|
+
let instanceId = -1;
|
|
14
|
+
var UnresolvedFile = class extends FileDiff {
|
|
15
|
+
__id = `unresolved-file:${++instanceId}`;
|
|
16
|
+
computedCache = {
|
|
17
|
+
file: void 0,
|
|
18
|
+
fileDiff: void 0,
|
|
19
|
+
actions: void 0
|
|
20
|
+
};
|
|
21
|
+
conflictActions = [];
|
|
22
|
+
conflictActionCache = /* @__PURE__ */ new Map();
|
|
23
|
+
constructor(options = { theme: DEFAULT_THEMES }, workerManager, isContainerManaged = false) {
|
|
24
|
+
super(void 0, workerManager, isContainerManaged);
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.setOptions(options);
|
|
27
|
+
}
|
|
28
|
+
setOptions(options) {
|
|
29
|
+
if (options == null) return;
|
|
30
|
+
if (options.onMergeConflictAction != null && options.onMergeConflictResolve != null) throw new Error("UnresolvedFile: onMergeConflictAction and onMergeConflictResolve are mutually exclusive. Use only one callback.");
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.hunksRenderer.setOptions(this.getHunksRendererOptions(options));
|
|
33
|
+
const hunkSeparators = this.options.hunkSeparators ?? "line-info";
|
|
34
|
+
this.interactionManager.setOptions(pluckInteractionOptions(this.options, typeof hunkSeparators === "function" || hunkSeparators === "line-info" || hunkSeparators === "line-info-basic" ? this.expandHunk : void 0, this.getLineIndex, this.handleMergeConflictActionClick));
|
|
35
|
+
}
|
|
36
|
+
createHunksRenderer(options) {
|
|
37
|
+
return new UnresolvedFileHunksRenderer(this.getHunksRendererOptions(options), this.handleHighlightRender, this.workerManager);
|
|
38
|
+
}
|
|
39
|
+
getHunksRendererOptions(options) {
|
|
40
|
+
return {
|
|
41
|
+
...this.options,
|
|
42
|
+
hunkSeparators: typeof options.hunkSeparators === "function" ? "custom" : options.hunkSeparators,
|
|
43
|
+
mergeConflictActionsType: typeof options.mergeConflictActionsType === "function" ? "custom" : options.mergeConflictActionsType
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
applyPreNodeAttributes(pre, result) {
|
|
47
|
+
super.applyPreNodeAttributes(pre, result, { "data-has-merge-conflict": "" });
|
|
48
|
+
}
|
|
49
|
+
cleanUp() {
|
|
50
|
+
this.clearMergeConflictActionCache();
|
|
51
|
+
this.computedCache = {
|
|
52
|
+
file: void 0,
|
|
53
|
+
fileDiff: void 0,
|
|
54
|
+
actions: void 0
|
|
55
|
+
};
|
|
56
|
+
this.conflictActions = [];
|
|
57
|
+
super.cleanUp();
|
|
58
|
+
}
|
|
59
|
+
getOrComputeDiff({ file, fileDiff, actions }) {
|
|
60
|
+
wrapper: if (this.options.onMergeConflictAction != null) {
|
|
61
|
+
if (fileDiff != null !== (actions != null)) throw new Error("UnresolvedFile.getOrComputeDiff: fileDiff and actions must be passed together");
|
|
62
|
+
if (fileDiff != null && actions != null) {
|
|
63
|
+
this.computedCache = {
|
|
64
|
+
file: file ?? this.computedCache.file,
|
|
65
|
+
fileDiff,
|
|
66
|
+
actions
|
|
67
|
+
};
|
|
68
|
+
break wrapper;
|
|
69
|
+
} else if (file != null || this.computedCache.file != null) {
|
|
70
|
+
file ??= this.computedCache.file;
|
|
71
|
+
if (file == null) throw new Error("UnresolvedFile.getOrComputeDiff: file is null, should be impossible");
|
|
72
|
+
if (!areFilesEqual(file, this.computedCache.file) || this.computedCache.fileDiff == null || this.computedCache.actions == null) {
|
|
73
|
+
const computed = parseMergeConflictDiffFromFile(file);
|
|
74
|
+
this.computedCache = {
|
|
75
|
+
file,
|
|
76
|
+
fileDiff: computed.fileDiff,
|
|
77
|
+
actions: computed.actions
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
fileDiff = this.computedCache.fileDiff;
|
|
81
|
+
actions = this.computedCache.actions;
|
|
82
|
+
break wrapper;
|
|
83
|
+
} else {
|
|
84
|
+
fileDiff = this.computedCache.fileDiff;
|
|
85
|
+
actions = this.computedCache.actions;
|
|
86
|
+
break wrapper;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
if (fileDiff != null || actions != null) throw new Error("UnresolvedFile.getOrComputeDiff: fileDiff and actions are only usable in controlled mode, you must pass in `onMergeConflictAction`");
|
|
90
|
+
this.computedCache.file ??= file;
|
|
91
|
+
if (this.computedCache.fileDiff == null && this.computedCache.file != null) {
|
|
92
|
+
const computed = parseMergeConflictDiffFromFile(this.computedCache.file);
|
|
93
|
+
this.computedCache.fileDiff = computed.fileDiff;
|
|
94
|
+
this.computedCache.actions = computed.actions;
|
|
95
|
+
}
|
|
96
|
+
fileDiff = this.computedCache.fileDiff;
|
|
97
|
+
actions = this.computedCache.actions;
|
|
98
|
+
break wrapper;
|
|
99
|
+
}
|
|
100
|
+
if (fileDiff == null || actions == null) return;
|
|
101
|
+
return {
|
|
102
|
+
fileDiff,
|
|
103
|
+
actions
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
hydrate(props) {
|
|
107
|
+
const { file, fileDiff, actions, lineAnnotations,...rest } = props;
|
|
108
|
+
const source = this.getOrComputeDiff({
|
|
109
|
+
file,
|
|
110
|
+
fileDiff,
|
|
111
|
+
actions
|
|
112
|
+
});
|
|
113
|
+
if (source == null) return;
|
|
114
|
+
this.setActiveMergeConflictActions(source.actions);
|
|
115
|
+
super.hydrate({
|
|
116
|
+
...rest,
|
|
117
|
+
fileDiff: source.fileDiff,
|
|
118
|
+
lineAnnotations
|
|
119
|
+
});
|
|
120
|
+
this.renderMergeConflictActionSlots();
|
|
121
|
+
}
|
|
122
|
+
rerender() {
|
|
123
|
+
if (!this.enabled || this.fileDiff == null) return;
|
|
124
|
+
this.render({
|
|
125
|
+
forceRender: true,
|
|
126
|
+
renderRange: this.renderRange
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
render(props = {}) {
|
|
130
|
+
let { file, fileDiff, actions, lineAnnotations,...rest } = props;
|
|
131
|
+
const source = this.getOrComputeDiff({
|
|
132
|
+
file,
|
|
133
|
+
fileDiff,
|
|
134
|
+
actions
|
|
135
|
+
});
|
|
136
|
+
if (source == null) return false;
|
|
137
|
+
this.setActiveMergeConflictActions(source.actions);
|
|
138
|
+
const didRender = super.render({
|
|
139
|
+
...rest,
|
|
140
|
+
fileDiff: source.fileDiff,
|
|
141
|
+
lineAnnotations
|
|
142
|
+
});
|
|
143
|
+
this.renderMergeConflictActionSlots();
|
|
144
|
+
return didRender;
|
|
145
|
+
}
|
|
146
|
+
resolveConflict(conflictIndex, resolution, file = this.computedCache.file) {
|
|
147
|
+
const action = this.conflictActions[conflictIndex];
|
|
148
|
+
if (file == null || action == null) return;
|
|
149
|
+
if (action.conflictIndex !== conflictIndex) {
|
|
150
|
+
console.error({
|
|
151
|
+
conflictIndex,
|
|
152
|
+
action
|
|
153
|
+
});
|
|
154
|
+
throw new Error("UnresolvedFile.resolveConflict: conflictIndex and conflictAction don't match");
|
|
155
|
+
}
|
|
156
|
+
const contents = resolveMergeConflict(file.contents, {
|
|
157
|
+
resolution,
|
|
158
|
+
conflict: action.conflict
|
|
159
|
+
});
|
|
160
|
+
if (contents === file.contents) return;
|
|
161
|
+
return {
|
|
162
|
+
...file,
|
|
163
|
+
contents,
|
|
164
|
+
cacheKey: file.cacheKey != null ? `${file.cacheKey}:mc-${conflictIndex}-${resolution}` : void 0
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
resolveConflictAndRender(conflictIndex, resolution) {
|
|
168
|
+
const action = this.conflictActions[conflictIndex];
|
|
169
|
+
if (action == null) return;
|
|
170
|
+
if (action.conflictIndex !== conflictIndex) {
|
|
171
|
+
console.error({
|
|
172
|
+
conflictIndex,
|
|
173
|
+
action
|
|
174
|
+
});
|
|
175
|
+
throw new Error("UnresolvedFile.resolveConflictAndRender: conflictIndex and conflictAction don't match");
|
|
176
|
+
}
|
|
177
|
+
const payload = {
|
|
178
|
+
resolution,
|
|
179
|
+
conflict: action.conflict
|
|
180
|
+
};
|
|
181
|
+
const nextFile = this.resolveConflict(conflictIndex, resolution);
|
|
182
|
+
if (nextFile == null) return;
|
|
183
|
+
this.computedCache.file = nextFile;
|
|
184
|
+
this.computedCache.fileDiff = void 0;
|
|
185
|
+
this.computedCache.actions = void 0;
|
|
186
|
+
this.render();
|
|
187
|
+
this.options.onMergeConflictResolve?.(nextFile, payload);
|
|
188
|
+
return nextFile;
|
|
189
|
+
}
|
|
190
|
+
setActiveMergeConflictActions(actions) {
|
|
191
|
+
this.conflictActions = actions;
|
|
192
|
+
if (this.hunksRenderer instanceof UnresolvedFileHunksRenderer) this.hunksRenderer.setConflictActions(this.options.mergeConflictActionsType === "none" ? [] : actions);
|
|
193
|
+
}
|
|
194
|
+
handleMergeConflictActionClick = (target) => {
|
|
195
|
+
const action = this.conflictActions[target.conflictIndex];
|
|
196
|
+
if (action == null) return;
|
|
197
|
+
if (action.conflictIndex !== target.conflictIndex) {
|
|
198
|
+
console.error({
|
|
199
|
+
conflictIndex: target.conflictIndex,
|
|
200
|
+
action
|
|
201
|
+
});
|
|
202
|
+
throw new Error("UnresolvedFile.handleMergeConflictActionClick: conflictIndex and conflictAction don't match");
|
|
203
|
+
}
|
|
204
|
+
const payload = {
|
|
205
|
+
resolution: target.resolution,
|
|
206
|
+
conflict: action.conflict
|
|
207
|
+
};
|
|
208
|
+
if (this.options.onMergeConflictAction != null) {
|
|
209
|
+
this.options.onMergeConflictAction(payload, this);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.resolveConflictAndRender(target.conflictIndex, target.resolution);
|
|
213
|
+
};
|
|
214
|
+
renderMergeConflictActionSlots() {
|
|
215
|
+
if (this.isContainerManaged || this.fileContainer == null || typeof this.options.mergeConflictActionsType !== "function" || this.conflictActions.length === 0) {
|
|
216
|
+
this.clearMergeConflictActionCache();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const staleActions = new Map(this.conflictActionCache);
|
|
220
|
+
for (let actionIndex = 0; actionIndex < this.conflictActions.length; actionIndex++) {
|
|
221
|
+
const action = this.conflictActions[actionIndex];
|
|
222
|
+
if (action == null) continue;
|
|
223
|
+
if (action.conflictIndex !== actionIndex) {
|
|
224
|
+
console.error({
|
|
225
|
+
conflictIndex: actionIndex,
|
|
226
|
+
action
|
|
227
|
+
});
|
|
228
|
+
throw new Error("UnresolvedFile.renderMergeConflictActionSlots: conflictIndex and conflictAction don't match");
|
|
229
|
+
}
|
|
230
|
+
const anchor = getMergeConflictActionAnchor(action);
|
|
231
|
+
if (anchor == null) continue;
|
|
232
|
+
const conflictIndex = action.conflictIndex;
|
|
233
|
+
const slotName = getMergeConflictActionSlotName({
|
|
234
|
+
side: anchor.side,
|
|
235
|
+
lineNumber: anchor.lineNumber,
|
|
236
|
+
conflictIndex
|
|
237
|
+
});
|
|
238
|
+
const id = `${actionIndex}-${slotName}`;
|
|
239
|
+
let cache = this.conflictActionCache.get(id);
|
|
240
|
+
if (cache == null || !areMergeConflictActionsEqual(cache.action, action)) {
|
|
241
|
+
cache?.element.remove();
|
|
242
|
+
const rendered = this.renderMergeConflictAction(action);
|
|
243
|
+
if (rendered == null) continue;
|
|
244
|
+
const element = createAnnotationWrapperNode(slotName);
|
|
245
|
+
element.appendChild(rendered);
|
|
246
|
+
this.fileContainer.appendChild(element);
|
|
247
|
+
cache = {
|
|
248
|
+
element,
|
|
249
|
+
action
|
|
250
|
+
};
|
|
251
|
+
this.conflictActionCache.set(id, cache);
|
|
252
|
+
}
|
|
253
|
+
staleActions.delete(id);
|
|
254
|
+
}
|
|
255
|
+
for (const [id, { element }] of staleActions.entries()) {
|
|
256
|
+
this.conflictActionCache.delete(id);
|
|
257
|
+
element.remove();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
renderMergeConflictAction(action) {
|
|
261
|
+
if (typeof this.options.mergeConflictActionsType !== "function") return;
|
|
262
|
+
const rendered = this.options.mergeConflictActionsType(action, this);
|
|
263
|
+
if (rendered == null) return;
|
|
264
|
+
if (rendered instanceof HTMLElement) return rendered;
|
|
265
|
+
if (typeof DocumentFragment !== "undefined" && rendered instanceof DocumentFragment) {
|
|
266
|
+
const wrapper = document.createElement("div");
|
|
267
|
+
wrapper.style.display = "contents";
|
|
268
|
+
wrapper.appendChild(rendered);
|
|
269
|
+
return wrapper;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
clearMergeConflictActionCache() {
|
|
273
|
+
for (const { element } of this.conflictActionCache.values()) element.remove();
|
|
274
|
+
this.conflictActionCache.clear();
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
//#endregion
|
|
279
|
+
export { UnresolvedFile };
|
|
280
|
+
//# sourceMappingURL=UnresolvedFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnresolvedFile.js","names":["options: UnresolvedFileOptions<LAnnotation>","payload: MergeConflictActionPayload"],"sources":["../../src/components/UnresolvedFile.ts"],"sourcesContent":["import { DEFAULT_THEMES } from '../constants';\nimport type { MergeConflictActionTarget } from '../managers/InteractionManager';\nimport { pluckInteractionOptions } from '../managers/InteractionManager';\nimport type { HunksRenderResult } from '../renderers/DiffHunksRenderer';\nimport {\n UnresolvedFileHunksRenderer,\n type UnresolvedFileHunksRendererOptions,\n} from '../renderers/UnresolvedFileHunksRenderer';\nimport type {\n FileContents,\n FileDiffMetadata,\n MergeConflictActionPayload,\n MergeConflictResolution,\n} from '../types';\nimport { areFilesEqual } from '../utils/areFilesEqual';\nimport { areMergeConflictActionsEqual } from '../utils/areMergeConflictActionsEqual';\nimport { createAnnotationWrapperNode } from '../utils/createAnnotationWrapperNode';\nimport { getMergeConflictActionSlotName } from '../utils/getMergeConflictActionSlotName';\nimport {\n getMergeConflictActionAnchor,\n type MergeConflictDiffAction,\n parseMergeConflictDiffFromFile,\n} from '../utils/parseMergeConflictDiffFromFile';\nimport { resolveMergeConflict } from '../utils/resolveMergeConflict';\nimport type { WorkerPoolManager } from '../worker';\nimport {\n FileDiff,\n type FileDiffOptions,\n type FileDiffRenderProps,\n} from './FileDiff';\n\nexport type RenderMergeConflictActions<LAnnotation> = (\n action: MergeConflictDiffAction,\n instance: UnresolvedFile<LAnnotation>\n) => HTMLElement | DocumentFragment | undefined;\n\nexport type MergeConflictActionsTypeOption<LAnnotation> =\n | 'none'\n | 'default'\n | RenderMergeConflictActions<LAnnotation>;\n\nexport interface UnresolvedFileOptions<\n LAnnotation,\n> extends FileDiffOptions<LAnnotation> {\n mergeConflictActionsType?: MergeConflictActionsTypeOption<LAnnotation>;\n onMergeConflictAction?(\n payload: MergeConflictActionPayload,\n instance: UnresolvedFile<LAnnotation>\n ): void;\n onMergeConflictResolve?(\n file: FileContents,\n payload: MergeConflictActionPayload\n ): void;\n}\n\nexport interface UnresolvedFileRenderProps<LAnnotation> extends Omit<\n FileDiffRenderProps<LAnnotation>,\n 'oldFile' | 'newFile'\n> {\n file?: FileContents;\n actions?: MergeConflictDiffAction[];\n}\n\nexport interface UnresolvedFileHydrationProps<LAnnotation> extends Omit<\n UnresolvedFileRenderProps<LAnnotation>,\n 'file'\n> {\n file?: FileContents;\n fileDiff?: FileDiffMetadata;\n actions?: MergeConflictDiffAction[];\n fileContainer: HTMLElement;\n prerenderedHTML?: string;\n}\n\ninterface MergeConflictActionElementCache {\n element: HTMLElement;\n action: MergeConflictDiffAction;\n}\n\ninterface GetOrComputeDiffProps {\n file: FileContents | undefined;\n fileDiff: FileDiffMetadata | undefined;\n actions: MergeConflictDiffAction[] | undefined;\n}\n\ninterface GetOrComputeDiffResult {\n fileDiff: FileDiffMetadata;\n actions: MergeConflictDiffAction[];\n}\n\ntype UnresolvedFileDataCache = GetOrComputeDiffProps;\n\nlet instanceId = -1;\n\nexport class UnresolvedFile<\n LAnnotation = undefined,\n> extends FileDiff<LAnnotation> {\n override readonly __id: string = `unresolved-file:${++instanceId}`;\n protected computedCache: UnresolvedFileDataCache = {\n file: undefined,\n fileDiff: undefined,\n actions: undefined,\n };\n private conflictActions: MergeConflictDiffAction[] = [];\n private conflictActionCache: Map<string, MergeConflictActionElementCache> =\n new Map();\n\n constructor(\n public override options: UnresolvedFileOptions<LAnnotation> = {\n theme: DEFAULT_THEMES,\n },\n workerManager?: WorkerPoolManager | undefined,\n isContainerManaged = false\n ) {\n super(undefined, workerManager, isContainerManaged);\n this.setOptions(options);\n }\n\n override setOptions(\n options: UnresolvedFileOptions<LAnnotation> | undefined\n ): void {\n if (options == null) {\n return;\n }\n\n if (\n options.onMergeConflictAction != null &&\n options.onMergeConflictResolve != null\n ) {\n throw new Error(\n 'UnresolvedFile: onMergeConflictAction and onMergeConflictResolve are mutually exclusive. Use only one callback.'\n );\n }\n\n this.options = options;\n this.hunksRenderer.setOptions(this.getHunksRendererOptions(options));\n\n const hunkSeparators = this.options.hunkSeparators ?? 'line-info';\n this.interactionManager.setOptions(\n pluckInteractionOptions(\n this.options,\n typeof hunkSeparators === 'function' ||\n hunkSeparators === 'line-info' ||\n hunkSeparators === 'line-info-basic'\n ? this.expandHunk\n : undefined,\n this.getLineIndex,\n this.handleMergeConflictActionClick\n )\n );\n }\n\n protected override createHunksRenderer(\n options: UnresolvedFileOptions<LAnnotation>\n ): UnresolvedFileHunksRenderer<LAnnotation> {\n const renderer = new UnresolvedFileHunksRenderer<LAnnotation>(\n this.getHunksRendererOptions(options),\n this.handleHighlightRender,\n this.workerManager\n );\n return renderer;\n }\n\n protected override getHunksRendererOptions(\n options: UnresolvedFileOptions<LAnnotation>\n ): UnresolvedFileHunksRendererOptions {\n return {\n ...this.options,\n hunkSeparators:\n typeof options.hunkSeparators === 'function'\n ? 'custom'\n : options.hunkSeparators,\n mergeConflictActionsType:\n typeof options.mergeConflictActionsType === 'function'\n ? 'custom'\n : options.mergeConflictActionsType,\n };\n }\n\n protected override applyPreNodeAttributes(\n pre: HTMLPreElement,\n result: HunksRenderResult\n ): void {\n super.applyPreNodeAttributes(pre, result, {\n 'data-has-merge-conflict': '',\n });\n }\n\n override cleanUp(): void {\n this.clearMergeConflictActionCache();\n this.computedCache = {\n file: undefined,\n fileDiff: undefined,\n actions: undefined,\n };\n this.conflictActions = [];\n super.cleanUp();\n }\n\n private getOrComputeDiff({\n file,\n fileDiff,\n actions,\n }: GetOrComputeDiffProps): GetOrComputeDiffResult | undefined {\n wrapper: {\n // We are dealing with a controlled component\n if (this.options.onMergeConflictAction != null) {\n const hasFileDiff = fileDiff != null;\n const hasActions = actions != null;\n if (hasFileDiff !== hasActions) {\n throw new Error(\n 'UnresolvedFile.getOrComputeDiff: fileDiff and actions must be passed together'\n );\n }\n // If we were provided a new fileDiff and actions, we are a FULLY\n // controlled component, which means we will not do any computation\n if (fileDiff != null && actions != null) {\n this.computedCache = {\n file: file ?? this.computedCache.file,\n fileDiff,\n actions,\n };\n break wrapper;\n }\n // If we were provided a new file, we should attempt to parse out a new\n // diff/actions if we haven't computed it before\n else if (file != null || this.computedCache.file != null) {\n file ??= this.computedCache.file;\n if (file == null) {\n throw new Error(\n 'UnresolvedFile.getOrComputeDiff: file is null, should be impossible'\n );\n }\n if (\n !areFilesEqual(file, this.computedCache.file) ||\n this.computedCache.fileDiff == null ||\n this.computedCache.actions == null\n ) {\n const computed = parseMergeConflictDiffFromFile(file);\n this.computedCache = {\n file,\n fileDiff: computed.fileDiff,\n actions: computed.actions,\n };\n }\n fileDiff = this.computedCache.fileDiff;\n actions = this.computedCache.actions;\n break wrapper;\n }\n // Otherwise we should fall through and try to use the cache if it exists\n else {\n fileDiff = this.computedCache.fileDiff;\n actions = this.computedCache.actions;\n break wrapper;\n }\n }\n // If we are uncontrolled we only rely on the file and only use the first\n // version, otherwise utilize the cached version\n else {\n if (fileDiff != null || actions != null) {\n throw new Error(\n 'UnresolvedFile.getOrComputeDiff: fileDiff and actions are only usable in controlled mode, you must pass in `onMergeConflictAction`'\n );\n }\n this.computedCache.file ??= file;\n if (\n this.computedCache.fileDiff == null &&\n this.computedCache.file != null\n ) {\n const computed = parseMergeConflictDiffFromFile(\n this.computedCache.file\n );\n this.computedCache.fileDiff = computed.fileDiff;\n this.computedCache.actions = computed.actions;\n }\n // Because we are uncontrolled, the source of truth is the\n // computedCache\n fileDiff = this.computedCache.fileDiff;\n actions = this.computedCache.actions;\n break wrapper;\n }\n }\n if (fileDiff == null || actions == null) {\n return undefined;\n }\n return { fileDiff, actions };\n }\n\n override hydrate(props: UnresolvedFileHydrationProps<LAnnotation>): void {\n const { file, fileDiff, actions, lineAnnotations, ...rest } = props;\n const source = this.getOrComputeDiff({ file, fileDiff, actions });\n if (source == null) {\n return;\n }\n this.setActiveMergeConflictActions(source.actions);\n super.hydrate({\n ...rest,\n fileDiff: source.fileDiff,\n lineAnnotations,\n });\n this.renderMergeConflictActionSlots();\n }\n\n override rerender(): void {\n if (!this.enabled || this.fileDiff == null) {\n return;\n }\n this.render({ forceRender: true, renderRange: this.renderRange });\n }\n\n override render(props: UnresolvedFileRenderProps<LAnnotation> = {}): boolean {\n let { file, fileDiff, actions, lineAnnotations, ...rest } = props;\n const source = this.getOrComputeDiff({ file, fileDiff, actions });\n if (source == null) {\n return false;\n }\n this.setActiveMergeConflictActions(source.actions);\n const didRender = super.render({\n ...rest,\n fileDiff: source.fileDiff,\n lineAnnotations,\n });\n this.renderMergeConflictActionSlots();\n return didRender;\n }\n\n public resolveConflict(\n conflictIndex: number,\n resolution: MergeConflictResolution,\n file: FileContents | undefined = this.computedCache.file\n ): FileContents | undefined {\n const action = this.conflictActions[conflictIndex];\n if (file == null || action == null) {\n return undefined;\n }\n\n if (action.conflictIndex !== conflictIndex) {\n console.error({ conflictIndex, action });\n throw new Error(\n \"UnresolvedFile.resolveConflict: conflictIndex and conflictAction don't match\"\n );\n }\n\n const contents = resolveMergeConflict(file.contents, {\n resolution,\n conflict: action.conflict,\n });\n if (contents === file.contents) {\n return undefined;\n }\n\n return {\n ...file,\n contents,\n cacheKey:\n file.cacheKey != null\n ? `${file.cacheKey}:mc-${conflictIndex}-${resolution}`\n : undefined,\n };\n }\n\n private resolveConflictAndRender(\n conflictIndex: number,\n resolution: MergeConflictResolution\n ): FileContents | undefined {\n const action = this.conflictActions[conflictIndex];\n if (action == null) {\n return undefined;\n }\n if (action.conflictIndex !== conflictIndex) {\n console.error({ conflictIndex, action });\n throw new Error(\n \"UnresolvedFile.resolveConflictAndRender: conflictIndex and conflictAction don't match\"\n );\n }\n const payload: MergeConflictActionPayload = {\n resolution,\n conflict: action.conflict,\n };\n const nextFile = this.resolveConflict(conflictIndex, resolution);\n if (nextFile == null) {\n return undefined;\n }\n\n this.computedCache.file = nextFile;\n // Clear out the diff cache to force a new compute next render\n this.computedCache.fileDiff = undefined;\n this.computedCache.actions = undefined;\n this.render();\n this.options.onMergeConflictResolve?.(nextFile, payload);\n return nextFile;\n }\n\n private setActiveMergeConflictActions(\n actions: MergeConflictDiffAction[]\n ): void {\n this.conflictActions = actions;\n if (this.hunksRenderer instanceof UnresolvedFileHunksRenderer) {\n this.hunksRenderer.setConflictActions(\n this.options.mergeConflictActionsType === 'none' ? [] : actions\n );\n }\n }\n\n private handleMergeConflictActionClick = (\n target: MergeConflictActionTarget\n ): void => {\n const action = this.conflictActions[target.conflictIndex];\n if (action == null) {\n return;\n }\n if (action.conflictIndex !== target.conflictIndex) {\n console.error({ conflictIndex: target.conflictIndex, action });\n throw new Error(\n \"UnresolvedFile.handleMergeConflictActionClick: conflictIndex and conflictAction don't match\"\n );\n }\n const payload: MergeConflictActionPayload = {\n resolution: target.resolution,\n conflict: action.conflict,\n };\n if (this.options.onMergeConflictAction != null) {\n this.options.onMergeConflictAction(payload, this);\n return;\n }\n this.resolveConflictAndRender(target.conflictIndex, target.resolution);\n };\n\n private renderMergeConflictActionSlots(): void {\n if (\n this.isContainerManaged ||\n this.fileContainer == null ||\n typeof this.options.mergeConflictActionsType !== 'function' ||\n this.conflictActions.length === 0\n ) {\n this.clearMergeConflictActionCache();\n return;\n }\n const staleActions = new Map(this.conflictActionCache);\n for (\n let actionIndex = 0;\n actionIndex < this.conflictActions.length;\n actionIndex++\n ) {\n const action = this.conflictActions[actionIndex];\n if (action == null) {\n continue;\n }\n if (action.conflictIndex !== actionIndex) {\n console.error({ conflictIndex: actionIndex, action });\n throw new Error(\n \"UnresolvedFile.renderMergeConflictActionSlots: conflictIndex and conflictAction don't match\"\n );\n }\n const anchor = getMergeConflictActionAnchor(action);\n if (anchor == null) {\n continue;\n }\n const conflictIndex = action.conflictIndex;\n const slotName = getMergeConflictActionSlotName({\n side: anchor.side,\n lineNumber: anchor.lineNumber,\n conflictIndex,\n });\n const id = `${actionIndex}-${slotName}`;\n let cache = this.conflictActionCache.get(id);\n if (\n cache == null ||\n !areMergeConflictActionsEqual(cache.action, action)\n ) {\n cache?.element.remove();\n const rendered = this.renderMergeConflictAction(action);\n if (rendered == null) {\n continue;\n }\n const element = createAnnotationWrapperNode(slotName);\n element.appendChild(rendered);\n this.fileContainer.appendChild(element);\n cache = { element, action };\n this.conflictActionCache.set(id, cache);\n }\n staleActions.delete(id);\n }\n for (const [id, { element }] of staleActions.entries()) {\n this.conflictActionCache.delete(id);\n element.remove();\n }\n }\n\n private renderMergeConflictAction(\n action: MergeConflictDiffAction\n ): HTMLElement | undefined {\n if (typeof this.options.mergeConflictActionsType !== 'function') {\n return undefined;\n }\n const rendered = this.options.mergeConflictActionsType(action, this);\n if (rendered == null) {\n return undefined;\n }\n if (rendered instanceof HTMLElement) {\n return rendered;\n }\n if (\n typeof DocumentFragment !== 'undefined' &&\n rendered instanceof DocumentFragment\n ) {\n const wrapper = document.createElement('div');\n wrapper.style.display = 'contents';\n wrapper.appendChild(rendered);\n return wrapper;\n }\n return undefined;\n }\n\n private clearMergeConflictActionCache(): void {\n for (const { element } of this.conflictActionCache.values()) {\n element.remove();\n }\n this.conflictActionCache.clear();\n }\n}\n"],"mappings":"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"}
|
|
@@ -24,8 +24,9 @@ var VirtualizedFile = class extends File {
|
|
|
24
24
|
setOptions(options) {
|
|
25
25
|
if (options == null) return;
|
|
26
26
|
const previousOverflow = this.options.overflow;
|
|
27
|
+
const previousCollapsed = this.options.collapsed;
|
|
27
28
|
super.setOptions(options);
|
|
28
|
-
if (previousOverflow !== this.options.overflow) {
|
|
29
|
+
if (previousOverflow !== this.options.overflow || previousCollapsed !== this.options.collapsed) {
|
|
29
30
|
this.heightCache.clear();
|
|
30
31
|
this.computeApproximateSize();
|
|
31
32
|
this.renderRange = void 0;
|
|
@@ -73,13 +74,15 @@ var VirtualizedFile = class extends File {
|
|
|
73
74
|
super.cleanUp();
|
|
74
75
|
}
|
|
75
76
|
computeApproximateSize() {
|
|
77
|
+
const isFirstCompute = this.height === 0;
|
|
76
78
|
this.height = 0;
|
|
77
79
|
if (this.file == null) return;
|
|
78
|
-
const { disableFileHeader = false, overflow = "scroll" } = this.options;
|
|
80
|
+
const { disableFileHeader = false, collapsed = false, overflow = "scroll" } = this.options;
|
|
79
81
|
const { diffHeaderHeight, fileGap, lineHeight } = this.metrics;
|
|
80
82
|
const lines = this.getOrCreateLineCache(this.file);
|
|
81
83
|
if (!disableFileHeader) this.height += diffHeaderHeight;
|
|
82
84
|
else this.height += fileGap;
|
|
85
|
+
if (collapsed) return;
|
|
83
86
|
if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
|
|
84
87
|
else iterateOverFile({
|
|
85
88
|
lines,
|
|
@@ -88,7 +91,7 @@ var VirtualizedFile = class extends File {
|
|
|
88
91
|
}
|
|
89
92
|
});
|
|
90
93
|
if (lines.length > 0) this.height += fileGap;
|
|
91
|
-
if (this.fileContainer != null && this.virtualizer.config.resizeDebugging) {
|
|
94
|
+
if (this.fileContainer != null && this.virtualizer.config.resizeDebugging && !isFirstCompute) {
|
|
92
95
|
const rect = this.fileContainer.getBoundingClientRect();
|
|
93
96
|
if (rect.height !== this.height) console.log("VirtualizedFile.computeApproximateSize: computed height doesnt match", {
|
|
94
97
|
name: this.file.name,
|
|
@@ -116,12 +119,12 @@ var VirtualizedFile = class extends File {
|
|
|
116
119
|
console.error("VirtualizedFile.render: attempting to virtually render when we dont have file");
|
|
117
120
|
return false;
|
|
118
121
|
}
|
|
119
|
-
this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
120
122
|
if (isFirstRender) {
|
|
121
123
|
this.computeApproximateSize();
|
|
122
124
|
this.virtualizer.connect(fileContainer, this);
|
|
125
|
+
this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
123
126
|
this.isVisible = this.virtualizer.isInstanceVisible(this.top, this.height);
|
|
124
|
-
}
|
|
127
|
+
} else this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
125
128
|
if (!this.isVisible) return this.renderPlaceholder(this.height);
|
|
126
129
|
const windowSpecs = this.virtualizer.getWindowSpecs();
|
|
127
130
|
const renderRange = this.computeRenderRangeFromWindow(this.file, this.top, windowSpecs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedFile.js","names":["virtualizer: Virtualizer","metrics: VirtualFileMetrics","idealStartHunk","startingLine","clampedTotalLines","bufferBefore","hunkOffsets: number[]","firstVisibleHunk: number | undefined","centerHunk: number | undefined","overflowCounter: number | undefined","lineHeight"],"sources":["../../src/components/VirtualizedFile.ts"],"sourcesContent":["import { DEFAULT_VIRTUAL_FILE_METRICS } from '../constants';\nimport type {\n FileContents,\n RenderRange,\n RenderWindow,\n VirtualFileMetrics,\n} from '../types';\nimport { iterateOverFile } from '../utils/iterateOverFile';\nimport type { WorkerPoolManager } from '../worker';\nimport { File, type FileOptions, type FileRenderProps } from './File';\nimport type { Virtualizer } from './Virtualizer';\n\nlet instanceId = -1;\n\nexport class VirtualizedFile<\n LAnnotation = undefined,\n> extends File<LAnnotation> {\n override readonly __id: string = `virtualized-file:${++instanceId}`;\n\n public top: number | undefined;\n public height: number = 0;\n // Sparse map: line index -> measured height\n // Only stores lines that differ from what is returned from default line\n // height\n private heightCache: Map<number, number> = new Map();\n private isVisible: boolean = false;\n\n constructor(\n options: FileOptions<LAnnotation> | undefined,\n private virtualizer: Virtualizer,\n private metrics: VirtualFileMetrics = DEFAULT_VIRTUAL_FILE_METRICS,\n workerManager?: WorkerPoolManager,\n isContainerManaged = false\n ) {\n super(options, workerManager, isContainerManaged);\n }\n\n // Get the height for a line, using cached value if available.\n // If not cached and hasMetadataLine is true, adds lineHeight for the\n // metadata.\n public getLineHeight(lineIndex: number, hasMetadataLine = false): number {\n const cached = this.heightCache.get(lineIndex);\n if (cached != null) {\n return cached;\n }\n const multiplier = hasMetadataLine ? 2 : 1;\n return this.metrics.lineHeight * multiplier;\n }\n\n // Override setOptions to clear height cache when overflow changes\n override setOptions(options: FileOptions<LAnnotation> | undefined): void {\n if (options == null) return;\n const previousOverflow = this.options.overflow;\n\n super.setOptions(options);\n\n if (previousOverflow !== this.options.overflow) {\n this.heightCache.clear();\n this.computeApproximateSize();\n this.renderRange = undefined;\n }\n this.virtualizer.instanceChanged(this);\n }\n\n // Measure rendered lines and update height cache.\n // Called after render to reconcile estimated vs actual heights.\n public reconcileHeights(): void {\n if (this.fileContainer == null || this.file == null) {\n this.height = 0;\n return;\n }\n const { overflow = 'scroll' } = this.options;\n this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);\n\n // If the file has no annotations and we are using the scroll variant, then\n // we can probably skip everything\n if (\n overflow === 'scroll' &&\n this.lineAnnotations.length === 0 &&\n !this.virtualizer.config.resizeDebugging\n ) {\n return;\n }\n\n let hasLineHeightChange = false;\n\n // Single code element (no split mode)\n if (this.code == null) return;\n const content = this.code.children[1]; // Content column (gutter is [0])\n if (!(content instanceof HTMLElement)) return;\n\n for (const line of content.children) {\n if (!(line instanceof HTMLElement)) continue;\n\n const lineIndexAttr = line.dataset.lineIndex;\n if (lineIndexAttr == null) continue;\n\n const lineIndex = Number(lineIndexAttr);\n let measuredHeight = line.getBoundingClientRect().height;\n let hasMetadata = false;\n\n // Annotations or noNewline metadata increase the size of their attached line\n if (\n line.nextElementSibling instanceof HTMLElement &&\n ('lineAnnotation' in line.nextElementSibling.dataset ||\n 'noNewline' in line.nextElementSibling.dataset)\n ) {\n if ('noNewline' in line.nextElementSibling.dataset) {\n hasMetadata = true;\n }\n measuredHeight +=\n line.nextElementSibling.getBoundingClientRect().height;\n }\n\n const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);\n\n if (measuredHeight === expectedHeight) {\n continue;\n }\n\n hasLineHeightChange = true;\n // Line is back to standard height (e.g., after window resize)\n // Remove from cache\n if (measuredHeight === this.metrics.lineHeight * (hasMetadata ? 2 : 1)) {\n this.heightCache.delete(lineIndex);\n }\n // Non-standard height, cache it\n else {\n this.heightCache.set(lineIndex, measuredHeight);\n }\n }\n\n if (hasLineHeightChange || this.virtualizer.config.resizeDebugging) {\n this.computeApproximateSize();\n }\n }\n\n public onRender = (dirty: boolean): boolean => {\n if (this.fileContainer == null || this.file == null) {\n return false;\n }\n if (dirty) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n return this.render({ file: this.file });\n };\n\n override cleanUp(): void {\n if (this.fileContainer != null) {\n this.virtualizer.disconnect(this.fileContainer);\n }\n super.cleanUp();\n }\n\n // Compute the approximate size of the file using cached line heights.\n // Uses lineHeight for lines without cached measurements.\n private computeApproximateSize(): void {\n this.height = 0;\n if (this.file == null) {\n return;\n }\n\n const { disableFileHeader = false, overflow = 'scroll' } = this.options;\n const { diffHeaderHeight, fileGap, lineHeight } = this.metrics;\n const lines = this.getOrCreateLineCache(this.file);\n\n // Header or initial padding\n if (!disableFileHeader) {\n this.height += diffHeaderHeight;\n } else {\n this.height += fileGap;\n }\n\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n this.height += this.getOrCreateLineCache(this.file).length * lineHeight;\n } else {\n iterateOverFile({\n lines,\n callback: ({ lineIndex }) => {\n this.height += this.getLineHeight(lineIndex, false);\n },\n });\n }\n\n // Bottom padding\n if (lines.length > 0) {\n this.height += fileGap;\n }\n\n if (this.fileContainer != null && this.virtualizer.config.resizeDebugging) {\n const rect = this.fileContainer.getBoundingClientRect();\n if (rect.height !== this.height) {\n console.log(\n 'VirtualizedFile.computeApproximateSize: computed height doesnt match',\n {\n name: this.file.name,\n elementHeight: rect.height,\n computedHeight: this.height,\n }\n );\n } else {\n console.log(\n 'VirtualizedFile.computeApproximateSize: computed height IS CORRECT'\n );\n }\n }\n }\n\n public setVisibility(visible: boolean): void {\n if (this.fileContainer == null) {\n return;\n }\n if (visible && !this.isVisible) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n this.isVisible = true;\n } else if (!visible && this.isVisible) {\n this.isVisible = false;\n this.rerender();\n }\n }\n\n override render({\n fileContainer,\n file,\n ...props\n }: FileRenderProps<LAnnotation>): boolean {\n const isFirstRender = this.fileContainer == null;\n\n this.file ??= file;\n\n fileContainer = this.getOrCreateFileContainerNode(fileContainer);\n\n if (this.file == null) {\n console.error(\n 'VirtualizedFile.render: attempting to virtually render when we dont have file'\n );\n return false;\n }\n\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n if (isFirstRender) {\n this.computeApproximateSize();\n this.virtualizer.connect(fileContainer, this);\n this.isVisible = this.virtualizer.isInstanceVisible(\n this.top,\n this.height\n );\n }\n\n if (!this.isVisible) {\n return this.renderPlaceholder(this.height);\n }\n\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const renderRange = this.computeRenderRangeFromWindow(\n this.file,\n this.top,\n windowSpecs\n );\n return super.render({\n file: this.file,\n fileContainer,\n renderRange,\n ...props,\n });\n }\n\n private computeRenderRangeFromWindow(\n file: FileContents,\n fileTop: number,\n { top, bottom }: RenderWindow\n ): RenderRange {\n const { disableFileHeader = false, overflow = 'scroll' } = this.options;\n const { diffHeaderHeight, fileGap, hunkLineCount, lineHeight } =\n this.metrics;\n const lines = this.getOrCreateLineCache(file);\n const lineCount = lines.length;\n const fileHeight = this.height;\n const headerRegion = disableFileHeader ? fileGap : diffHeaderHeight;\n\n // File is outside render window\n if (fileTop < top - fileHeight || fileTop > bottom) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - fileGap,\n };\n }\n\n // Small file, just render it all\n if (lineCount <= hunkLineCount) {\n return {\n startingLine: 0,\n totalLines: hunkLineCount,\n bufferBefore: 0,\n bufferAfter: 0,\n };\n }\n\n // Calculate totalLines based on viewport size\n const estimatedTargetLines = Math.ceil(\n Math.max(bottom - top, 0) / lineHeight\n );\n const totalLines =\n Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount +\n hunkLineCount * 2;\n const totalHunks = totalLines / hunkLineCount;\n const viewportCenter = (top + bottom) / 2;\n\n // Simple case: overflow scroll with no annotations - pure math!\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n // Find which line is at viewport center\n const centerLine = Math.floor(\n (viewportCenter - (fileTop + headerRegion)) / lineHeight\n );\n const centerHunk = Math.floor(centerLine / hunkLineCount);\n\n // Calculate ideal start centered around viewport\n const idealStartHunk = centerHunk - Math.floor(totalHunks / 2);\n const totalHunksInFile = Math.ceil(lineCount / hunkLineCount);\n const startingLine =\n Math.max(0, Math.min(idealStartHunk, totalHunksInFile)) * hunkLineCount;\n\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n const bufferBefore = startingLine * lineHeight;\n const renderedLines = Math.min(\n clampedTotalLines,\n lineCount - startingLine\n );\n const bufferAfter = Math.max(\n 0,\n (lineCount - startingLine - renderedLines) * lineHeight\n );\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n\n // Complex case: need to account for line annotations or wrap overflow\n const overflowHunks = totalHunks;\n const hunkOffsets: number[] = [];\n\n let absoluteLineTop = fileTop + headerRegion;\n let currentLine = 0;\n let firstVisibleHunk: number | undefined;\n let centerHunk: number | undefined;\n let overflowCounter: number | undefined;\n\n iterateOverFile({\n lines,\n callback: ({ lineIndex }) => {\n const isAtHunkBoundary = currentLine % hunkLineCount === 0;\n\n if (isAtHunkBoundary) {\n hunkOffsets.push(absoluteLineTop - (fileTop + headerRegion));\n\n if (overflowCounter != null) {\n if (overflowCounter <= 0) {\n return true;\n }\n overflowCounter--;\n }\n }\n\n const lineHeight = this.getLineHeight(lineIndex, false);\n const currentHunk = Math.floor(currentLine / hunkLineCount);\n\n // Track visible region\n if (absoluteLineTop > top - lineHeight && absoluteLineTop < bottom) {\n firstVisibleHunk ??= currentHunk;\n }\n\n // Track which hunk contains the viewport center\n if (absoluteLineTop + lineHeight > viewportCenter) {\n centerHunk ??= currentHunk;\n }\n\n // Start overflow when we are out of the viewport at a hunk boundary\n if (\n overflowCounter == null &&\n absoluteLineTop >= bottom &&\n isAtHunkBoundary\n ) {\n overflowCounter = overflowHunks;\n }\n\n currentLine++;\n absoluteLineTop += lineHeight;\n\n return false;\n },\n });\n\n // No visible lines found\n if (firstVisibleHunk == null) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - fileGap,\n };\n }\n\n // Calculate balanced startingLine centered around the viewport center\n const collectedHunks = hunkOffsets.length;\n centerHunk ??= firstVisibleHunk;\n const idealStartHunk = Math.round(centerHunk - totalHunks / 2);\n\n // Clamp startHunk: at the beginning, reduce totalLines; at the end, shift startHunk back\n const maxStartHunk = Math.max(0, collectedHunks - totalHunks);\n const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));\n const startingLine = startHunk * hunkLineCount;\n\n // If we wanted to start before 0, reduce totalLines by the clamped amount\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n // Use hunkOffsets array for efficient buffer calculations\n const bufferBefore = hunkOffsets[startHunk] ?? 0;\n\n // Calculate bufferAfter\n const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;\n const bufferAfter =\n finalHunkIndex < hunkOffsets.length\n ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] - fileGap\n : fileHeight - (absoluteLineTop - fileTop) - fileGap;\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VirtualizedFile.js","names":["virtualizer: Virtualizer","metrics: VirtualFileMetrics","idealStartHunk","startingLine","clampedTotalLines","bufferBefore","hunkOffsets: number[]","firstVisibleHunk: number | undefined","centerHunk: number | undefined","overflowCounter: number | undefined","lineHeight"],"sources":["../../src/components/VirtualizedFile.ts"],"sourcesContent":["import { DEFAULT_VIRTUAL_FILE_METRICS } from '../constants';\nimport type {\n FileContents,\n RenderRange,\n RenderWindow,\n VirtualFileMetrics,\n} from '../types';\nimport { iterateOverFile } from '../utils/iterateOverFile';\nimport type { WorkerPoolManager } from '../worker';\nimport { File, type FileOptions, type FileRenderProps } from './File';\nimport type { Virtualizer } from './Virtualizer';\n\nlet instanceId = -1;\n\nexport class VirtualizedFile<\n LAnnotation = undefined,\n> extends File<LAnnotation> {\n override readonly __id: string = `virtualized-file:${++instanceId}`;\n\n public top: number | undefined;\n public height: number = 0;\n // Sparse map: line index -> measured height\n // Only stores lines that differ from what is returned from default line\n // height\n private heightCache: Map<number, number> = new Map();\n private isVisible: boolean = false;\n\n constructor(\n options: FileOptions<LAnnotation> | undefined,\n private virtualizer: Virtualizer,\n private metrics: VirtualFileMetrics = DEFAULT_VIRTUAL_FILE_METRICS,\n workerManager?: WorkerPoolManager,\n isContainerManaged = false\n ) {\n super(options, workerManager, isContainerManaged);\n }\n\n // Get the height for a line, using cached value if available.\n // If not cached and hasMetadataLine is true, adds lineHeight for the\n // metadata.\n public getLineHeight(lineIndex: number, hasMetadataLine = false): number {\n const cached = this.heightCache.get(lineIndex);\n if (cached != null) {\n return cached;\n }\n const multiplier = hasMetadataLine ? 2 : 1;\n return this.metrics.lineHeight * multiplier;\n }\n\n // Override setOptions to clear height cache when overflow changes\n override setOptions(options: FileOptions<LAnnotation> | undefined): void {\n if (options == null) return;\n const previousOverflow = this.options.overflow;\n const previousCollapsed = this.options.collapsed;\n\n super.setOptions(options);\n\n if (\n previousOverflow !== this.options.overflow ||\n previousCollapsed !== this.options.collapsed\n ) {\n this.heightCache.clear();\n this.computeApproximateSize();\n this.renderRange = undefined;\n }\n this.virtualizer.instanceChanged(this);\n }\n\n // Measure rendered lines and update height cache.\n // Called after render to reconcile estimated vs actual heights.\n public reconcileHeights(): void {\n if (this.fileContainer == null || this.file == null) {\n this.height = 0;\n return;\n }\n const { overflow = 'scroll' } = this.options;\n this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);\n\n // If the file has no annotations and we are using the scroll variant, then\n // we can probably skip everything\n if (\n overflow === 'scroll' &&\n this.lineAnnotations.length === 0 &&\n !this.virtualizer.config.resizeDebugging\n ) {\n return;\n }\n\n let hasLineHeightChange = false;\n\n // Single code element (no split mode)\n if (this.code == null) return;\n const content = this.code.children[1]; // Content column (gutter is [0])\n if (!(content instanceof HTMLElement)) return;\n\n for (const line of content.children) {\n if (!(line instanceof HTMLElement)) continue;\n\n const lineIndexAttr = line.dataset.lineIndex;\n if (lineIndexAttr == null) continue;\n\n const lineIndex = Number(lineIndexAttr);\n let measuredHeight = line.getBoundingClientRect().height;\n let hasMetadata = false;\n\n // Annotations or noNewline metadata increase the size of their attached line\n if (\n line.nextElementSibling instanceof HTMLElement &&\n ('lineAnnotation' in line.nextElementSibling.dataset ||\n 'noNewline' in line.nextElementSibling.dataset)\n ) {\n if ('noNewline' in line.nextElementSibling.dataset) {\n hasMetadata = true;\n }\n measuredHeight +=\n line.nextElementSibling.getBoundingClientRect().height;\n }\n\n const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);\n\n if (measuredHeight === expectedHeight) {\n continue;\n }\n\n hasLineHeightChange = true;\n // Line is back to standard height (e.g., after window resize)\n // Remove from cache\n if (measuredHeight === this.metrics.lineHeight * (hasMetadata ? 2 : 1)) {\n this.heightCache.delete(lineIndex);\n }\n // Non-standard height, cache it\n else {\n this.heightCache.set(lineIndex, measuredHeight);\n }\n }\n\n if (hasLineHeightChange || this.virtualizer.config.resizeDebugging) {\n this.computeApproximateSize();\n }\n }\n\n public onRender = (dirty: boolean): boolean => {\n if (this.fileContainer == null || this.file == null) {\n return false;\n }\n if (dirty) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n return this.render({ file: this.file });\n };\n\n override cleanUp(): void {\n if (this.fileContainer != null) {\n this.virtualizer.disconnect(this.fileContainer);\n }\n super.cleanUp();\n }\n\n // Compute the approximate size of the file using cached line heights.\n // Uses lineHeight for lines without cached measurements.\n private computeApproximateSize(): void {\n const isFirstCompute = this.height === 0;\n this.height = 0;\n if (this.file == null) {\n return;\n }\n\n const {\n disableFileHeader = false,\n collapsed = false,\n overflow = 'scroll',\n } = this.options;\n const { diffHeaderHeight, fileGap, lineHeight } = this.metrics;\n const lines = this.getOrCreateLineCache(this.file);\n\n // Header or initial padding\n if (!disableFileHeader) {\n this.height += diffHeaderHeight;\n } else {\n this.height += fileGap;\n }\n if (collapsed) {\n return;\n }\n\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n this.height += this.getOrCreateLineCache(this.file).length * lineHeight;\n } else {\n iterateOverFile({\n lines,\n callback: ({ lineIndex }) => {\n this.height += this.getLineHeight(lineIndex, false);\n },\n });\n }\n\n // Bottom padding\n if (lines.length > 0) {\n this.height += fileGap;\n }\n\n if (\n this.fileContainer != null &&\n this.virtualizer.config.resizeDebugging &&\n !isFirstCompute\n ) {\n const rect = this.fileContainer.getBoundingClientRect();\n if (rect.height !== this.height) {\n console.log(\n 'VirtualizedFile.computeApproximateSize: computed height doesnt match',\n {\n name: this.file.name,\n elementHeight: rect.height,\n computedHeight: this.height,\n }\n );\n } else {\n console.log(\n 'VirtualizedFile.computeApproximateSize: computed height IS CORRECT'\n );\n }\n }\n }\n\n public setVisibility(visible: boolean): void {\n if (this.fileContainer == null) {\n return;\n }\n if (visible && !this.isVisible) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n this.isVisible = true;\n } else if (!visible && this.isVisible) {\n this.isVisible = false;\n this.rerender();\n }\n }\n\n override render({\n fileContainer,\n file,\n ...props\n }: FileRenderProps<LAnnotation>): boolean {\n const isFirstRender = this.fileContainer == null;\n\n this.file ??= file;\n\n fileContainer = this.getOrCreateFileContainerNode(fileContainer);\n\n if (this.file == null) {\n console.error(\n 'VirtualizedFile.render: attempting to virtually render when we dont have file'\n );\n return false;\n }\n\n if (isFirstRender) {\n this.computeApproximateSize();\n this.virtualizer.connect(fileContainer, this);\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n this.isVisible = this.virtualizer.isInstanceVisible(\n this.top,\n this.height\n );\n } else {\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n }\n\n if (!this.isVisible) {\n return this.renderPlaceholder(this.height);\n }\n\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const renderRange = this.computeRenderRangeFromWindow(\n this.file,\n this.top,\n windowSpecs\n );\n return super.render({\n file: this.file,\n fileContainer,\n renderRange,\n ...props,\n });\n }\n\n private computeRenderRangeFromWindow(\n file: FileContents,\n fileTop: number,\n { top, bottom }: RenderWindow\n ): RenderRange {\n const { disableFileHeader = false, overflow = 'scroll' } = this.options;\n const { diffHeaderHeight, fileGap, hunkLineCount, lineHeight } =\n this.metrics;\n const lines = this.getOrCreateLineCache(file);\n const lineCount = lines.length;\n const fileHeight = this.height;\n const headerRegion = disableFileHeader ? fileGap : diffHeaderHeight;\n\n // File is outside render window\n if (fileTop < top - fileHeight || fileTop > bottom) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - fileGap,\n };\n }\n\n // Small file, just render it all\n if (lineCount <= hunkLineCount) {\n return {\n startingLine: 0,\n totalLines: hunkLineCount,\n bufferBefore: 0,\n bufferAfter: 0,\n };\n }\n\n // Calculate totalLines based on viewport size\n const estimatedTargetLines = Math.ceil(\n Math.max(bottom - top, 0) / lineHeight\n );\n const totalLines =\n Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount +\n hunkLineCount * 2;\n const totalHunks = totalLines / hunkLineCount;\n const viewportCenter = (top + bottom) / 2;\n\n // Simple case: overflow scroll with no annotations - pure math!\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n // Find which line is at viewport center\n const centerLine = Math.floor(\n (viewportCenter - (fileTop + headerRegion)) / lineHeight\n );\n const centerHunk = Math.floor(centerLine / hunkLineCount);\n\n // Calculate ideal start centered around viewport\n const idealStartHunk = centerHunk - Math.floor(totalHunks / 2);\n const totalHunksInFile = Math.ceil(lineCount / hunkLineCount);\n const startingLine =\n Math.max(0, Math.min(idealStartHunk, totalHunksInFile)) * hunkLineCount;\n\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n const bufferBefore = startingLine * lineHeight;\n const renderedLines = Math.min(\n clampedTotalLines,\n lineCount - startingLine\n );\n const bufferAfter = Math.max(\n 0,\n (lineCount - startingLine - renderedLines) * lineHeight\n );\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n\n // Complex case: need to account for line annotations or wrap overflow\n const overflowHunks = totalHunks;\n const hunkOffsets: number[] = [];\n\n let absoluteLineTop = fileTop + headerRegion;\n let currentLine = 0;\n let firstVisibleHunk: number | undefined;\n let centerHunk: number | undefined;\n let overflowCounter: number | undefined;\n\n iterateOverFile({\n lines,\n callback: ({ lineIndex }) => {\n const isAtHunkBoundary = currentLine % hunkLineCount === 0;\n\n if (isAtHunkBoundary) {\n hunkOffsets.push(absoluteLineTop - (fileTop + headerRegion));\n\n if (overflowCounter != null) {\n if (overflowCounter <= 0) {\n return true;\n }\n overflowCounter--;\n }\n }\n\n const lineHeight = this.getLineHeight(lineIndex, false);\n const currentHunk = Math.floor(currentLine / hunkLineCount);\n\n // Track visible region\n if (absoluteLineTop > top - lineHeight && absoluteLineTop < bottom) {\n firstVisibleHunk ??= currentHunk;\n }\n\n // Track which hunk contains the viewport center\n if (absoluteLineTop + lineHeight > viewportCenter) {\n centerHunk ??= currentHunk;\n }\n\n // Start overflow when we are out of the viewport at a hunk boundary\n if (\n overflowCounter == null &&\n absoluteLineTop >= bottom &&\n isAtHunkBoundary\n ) {\n overflowCounter = overflowHunks;\n }\n\n currentLine++;\n absoluteLineTop += lineHeight;\n\n return false;\n },\n });\n\n // No visible lines found\n if (firstVisibleHunk == null) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - fileGap,\n };\n }\n\n // Calculate balanced startingLine centered around the viewport center\n const collectedHunks = hunkOffsets.length;\n centerHunk ??= firstVisibleHunk;\n const idealStartHunk = Math.round(centerHunk - totalHunks / 2);\n\n // Clamp startHunk: at the beginning, reduce totalLines; at the end, shift startHunk back\n const maxStartHunk = Math.max(0, collectedHunks - totalHunks);\n const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));\n const startingLine = startHunk * hunkLineCount;\n\n // If we wanted to start before 0, reduce totalLines by the clamped amount\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n // Use hunkOffsets array for efficient buffer calculations\n const bufferBefore = hunkOffsets[startHunk] ?? 0;\n\n // Calculate bufferAfter\n const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;\n const bufferAfter =\n finalHunkIndex < hunkOffsets.length\n ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] - fileGap\n : fileHeight - (absoluteLineTop - fileTop) - fileGap;\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ declare class VirtualizedFileDiff<LAnnotation = undefined> extends FileDiff<LAnn
|
|
|
19
19
|
reconcileHeights(): void;
|
|
20
20
|
onRender: (dirty: boolean) => boolean;
|
|
21
21
|
cleanUp(): void;
|
|
22
|
-
expandHunk(hunkIndex: number, direction: ExpansionDirections)
|
|
22
|
+
expandHunk: (hunkIndex: number, direction: ExpansionDirections, expansionLineCountOverride?: number | undefined) => void;
|
|
23
23
|
setVisibility(visible: boolean): void;
|
|
24
24
|
private computeApproximateSize;
|
|
25
25
|
render({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedFileDiff.d.ts","names":["ExpansionDirections","VirtualFileMetrics","WorkerPoolManager","FileDiff","FileDiffOptions","FileDiffRenderProps","Virtualizer","VirtualizedFileDiff","LAnnotation","Partial","fileContainer","oldFile","newFile","fileDiff"],"sources":["../../src/components/VirtualizedFileDiff.d.ts"],"sourcesContent":["import type { ExpansionDirections, VirtualFileMetrics } from '../types';\nimport type { WorkerPoolManager } from '../worker';\nimport { FileDiff, type FileDiffOptions, type FileDiffRenderProps } from './FileDiff';\nimport type { Virtualizer } from './Virtualizer';\nexport declare class VirtualizedFileDiff<LAnnotation = undefined> extends FileDiff<LAnnotation> {\n readonly __id: string;\n top: number | undefined;\n height: number;\n private metrics;\n private heightCache;\n private isVisible;\n private virtualizer;\n constructor(options: FileDiffOptions<LAnnotation> | undefined, virtualizer: Virtualizer, metrics?: Partial<VirtualFileMetrics>, workerManager?: WorkerPoolManager, isContainerManaged?: boolean);\n private getLineHeight;\n setOptions(options: FileDiffOptions<LAnnotation> | undefined): void;\n reconcileHeights(): void;\n onRender: (dirty: boolean) => boolean;\n cleanUp(): void;\n expandHunk(hunkIndex: number, direction: ExpansionDirections)
|
|
1
|
+
{"version":3,"file":"VirtualizedFileDiff.d.ts","names":["ExpansionDirections","VirtualFileMetrics","WorkerPoolManager","FileDiff","FileDiffOptions","FileDiffRenderProps","Virtualizer","VirtualizedFileDiff","LAnnotation","Partial","fileContainer","oldFile","newFile","fileDiff"],"sources":["../../src/components/VirtualizedFileDiff.d.ts"],"sourcesContent":["import type { ExpansionDirections, VirtualFileMetrics } from '../types';\nimport type { WorkerPoolManager } from '../worker';\nimport { FileDiff, type FileDiffOptions, type FileDiffRenderProps } from './FileDiff';\nimport type { Virtualizer } from './Virtualizer';\nexport declare class VirtualizedFileDiff<LAnnotation = undefined> extends FileDiff<LAnnotation> {\n readonly __id: string;\n top: number | undefined;\n height: number;\n private metrics;\n private heightCache;\n private isVisible;\n private virtualizer;\n constructor(options: FileDiffOptions<LAnnotation> | undefined, virtualizer: Virtualizer, metrics?: Partial<VirtualFileMetrics>, workerManager?: WorkerPoolManager, isContainerManaged?: boolean);\n private getLineHeight;\n setOptions(options: FileDiffOptions<LAnnotation> | undefined): void;\n reconcileHeights(): void;\n onRender: (dirty: boolean) => boolean;\n cleanUp(): void;\n expandHunk: (hunkIndex: number, direction: ExpansionDirections, expansionLineCountOverride?: number | undefined) => void;\n setVisibility(visible: boolean): void;\n private computeApproximateSize;\n render({ fileContainer, oldFile, newFile, fileDiff, ...props }?: FileDiffRenderProps<LAnnotation>): boolean;\n private getDiffStyle;\n private getExpandedRegion;\n private getExpandedLineCount;\n private computeRenderRangeFromWindow;\n}\n//# sourceMappingURL=VirtualizedFileDiff.d.ts.map"],"mappings":";;;;;;;cAIqBO,qDAAqDJ,SAASK;;;EAA9DD,MAAAA,EAAAA,MAAAA;EAA8DC,QAAAA,OAAAA;EAQ1CA,QAAAA,WAAAA;EAAhBJ,QAAAA,SAAAA;EAAuDE,QAAAA,WAAAA;EAA+BL,WAAAA,CAAAA,OAAAA,EAAtFG,eAAsFH,CAAtEO,WAAsEP,CAAAA,GAAAA,SAAAA,EAAAA,WAAAA,EAA/BK,WAA+BL,EAAAA,OAAAA,CAAAA,EAARQ,OAAQR,CAAAA,kBAAAA,CAAAA,EAAAA,aAAAA,CAAAA,EAAqCC,iBAArCD,EAAAA,kBAAAA,CAAAA,EAAAA,OAAAA;EAARQ,QAAAA,aAAAA;EAA6CP,UAAAA,CAAAA,OAAAA,EAE5HE,eAF4HF,CAE5GM,WAF4GN,CAAAA,GAAAA,SAAAA,CAAAA,EAAAA,IAAAA;EAE5GM,gBAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAAhBJ,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,OAAAA,EAAAA,GAAAA,OAAAA;EAIuBJ,OAAAA,CAAAA,CAAAA,EAAAA,IAAAA;EAGlCU,UAAAA,EAAAA,CAAAA,SAAAA,EAAAA,MAAAA,EAAAA,SAAAA,EAHkCV,mBAGlCU,EAAAA,0BAAAA,CAAAA,EAAAA,MAAAA,GAAAA,SAAAA,EAAAA,GAAAA,IAAAA;EAAeC,aAAAA,CAAAA,OAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAASC,QAAAA,sBAAAA;EAASC,MAAAA,CAAAA;IAAAA,aAAAA;IAAAA,OAAAA;IAAAA,OAAAA;IAAAA,QAAAA;IAAAA,GAAAA;EAAAA,CAAAA,CAAAA,EAAuBR,mBAAvBQ,CAA2CL,WAA3CK,CAAAA,CAAAA,EAAAA,OAAAA;EAA2CL,QAAAA,YAAAA;EAApBH,QAAAA,iBAAAA;EAjBKF,QAAAA,oBAAAA;EAAQ,QAAA,4BAAA"}
|
|
@@ -30,8 +30,9 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
30
30
|
if (options == null) return;
|
|
31
31
|
const previousDiffStyle = this.options.diffStyle;
|
|
32
32
|
const previousOverflow = this.options.overflow;
|
|
33
|
+
const previousCollapsed = this.options.collapsed;
|
|
33
34
|
super.setOptions(options);
|
|
34
|
-
if (previousDiffStyle !== this.options.diffStyle || previousOverflow !== this.options.overflow) {
|
|
35
|
+
if (previousDiffStyle !== this.options.diffStyle || previousOverflow !== this.options.overflow || previousCollapsed !== this.options.collapsed) {
|
|
35
36
|
this.heightCache.clear();
|
|
36
37
|
this.computeApproximateSize();
|
|
37
38
|
this.renderRange = void 0;
|
|
@@ -82,14 +83,15 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
82
83
|
if (this.fileContainer != null) this.virtualizer.disconnect(this.fileContainer);
|
|
83
84
|
super.cleanUp();
|
|
84
85
|
}
|
|
85
|
-
expandHunk(hunkIndex, direction) {
|
|
86
|
-
this.hunksRenderer.expandHunk(hunkIndex, direction);
|
|
86
|
+
expandHunk = (hunkIndex, direction, expansionLineCountOverride) => {
|
|
87
|
+
this.hunksRenderer.expandHunk(hunkIndex, direction, expansionLineCountOverride);
|
|
87
88
|
this.computeApproximateSize();
|
|
88
89
|
this.renderRange = void 0;
|
|
89
90
|
this.virtualizer.instanceChanged(this);
|
|
90
|
-
}
|
|
91
|
+
};
|
|
91
92
|
setVisibility(visible) {
|
|
92
93
|
if (this.fileContainer == null) return;
|
|
94
|
+
this.renderRange = void 0;
|
|
93
95
|
if (visible && !this.isVisible) {
|
|
94
96
|
this.top = this.virtualizer.getOffsetInScrollContainer(this.fileContainer);
|
|
95
97
|
this.isVisible = true;
|
|
@@ -99,14 +101,16 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
computeApproximateSize() {
|
|
104
|
+
const isFirstCompute = this.height === 0;
|
|
102
105
|
this.height = 0;
|
|
103
106
|
if (this.fileDiff == null) return;
|
|
104
|
-
const { disableFileHeader = false, expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, hunkSeparators = "line-info" } = this.options;
|
|
107
|
+
const { disableFileHeader = false, expandUnchanged = false, collapsed = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, hunkSeparators = "line-info" } = this.options;
|
|
105
108
|
const { diffHeaderHeight, fileGap, hunkSeparatorHeight } = this.metrics;
|
|
106
109
|
const diffStyle = this.getDiffStyle();
|
|
107
|
-
const separatorGap = hunkSeparators
|
|
110
|
+
const separatorGap = hunkSeparators !== "simple" && hunkSeparators !== "metadata" && hunkSeparators !== "line-info-basic" ? fileGap : 0;
|
|
108
111
|
if (!disableFileHeader) this.height += diffHeaderHeight;
|
|
109
112
|
else if (hunkSeparators !== "simple" && hunkSeparators !== "metadata") this.height += fileGap;
|
|
113
|
+
if (collapsed) return;
|
|
110
114
|
iterateOverDiff({
|
|
111
115
|
diff: this.fileDiff,
|
|
112
116
|
diffStyle,
|
|
@@ -125,7 +129,7 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
125
129
|
}
|
|
126
130
|
});
|
|
127
131
|
if (this.fileDiff.hunks.length > 0) this.height += fileGap;
|
|
128
|
-
if (this.fileContainer != null && this.virtualizer.config.resizeDebugging) {
|
|
132
|
+
if (this.fileContainer != null && this.virtualizer.config.resizeDebugging && !isFirstCompute) {
|
|
129
133
|
const rect = this.fileContainer.getBoundingClientRect();
|
|
130
134
|
if (rect.height !== this.height) console.log("VirtualizedFileDiff.computeApproximateSize: computed height doesnt match", {
|
|
131
135
|
name: this.fileDiff.name,
|
|
@@ -143,12 +147,12 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
143
147
|
console.error("VirtualizedFileDiff.render: attempting to virtually render when we dont have the correct data");
|
|
144
148
|
return false;
|
|
145
149
|
}
|
|
146
|
-
this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
147
150
|
if (isFirstRender) {
|
|
148
151
|
this.computeApproximateSize();
|
|
149
152
|
this.virtualizer.connect(fileContainer, this);
|
|
153
|
+
this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
150
154
|
this.isVisible = this.virtualizer.isInstanceVisible(this.top, this.height);
|
|
151
|
-
}
|
|
155
|
+
} else this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);
|
|
152
156
|
if (!this.isVisible) return this.renderPlaceholder(this.height);
|
|
153
157
|
const windowSpecs = this.virtualizer.getWindowSpecs();
|
|
154
158
|
const renderRange = this.computeRenderRangeFromWindow(this.fileDiff, this.top, windowSpecs);
|
|
@@ -241,7 +245,7 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
241
245
|
const overflowHunks = totalHunks;
|
|
242
246
|
const hunkOffsets = [];
|
|
243
247
|
const viewportCenter = (top + bottom) / 2;
|
|
244
|
-
const separatorGap = hunkSeparators === "simple" || hunkSeparators === "metadata" ? 0 : fileGap;
|
|
248
|
+
const separatorGap = hunkSeparators === "simple" || hunkSeparators === "metadata" || hunkSeparators === "line-info-basic" ? 0 : fileGap;
|
|
245
249
|
let absoluteLineTop = fileTop + headerRegion;
|
|
246
250
|
let currentLine = 0;
|
|
247
251
|
let firstVisibleHunk;
|
|
@@ -274,7 +278,7 @@ var VirtualizedFileDiff = class extends FileDiff {
|
|
|
274
278
|
if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
|
|
275
279
|
currentLine++;
|
|
276
280
|
absoluteLineTop += lineHeight$1;
|
|
277
|
-
if (collapsedAfter > 0 && hunkSeparators !== "simple") absoluteLineTop += hunkSeparatorHeight +
|
|
281
|
+
if (collapsedAfter > 0 && hunkSeparators !== "simple") absoluteLineTop += hunkSeparatorHeight + separatorGap;
|
|
278
282
|
return false;
|
|
279
283
|
}
|
|
280
284
|
});
|