@pierre/diffs 1.1.19 → 1.2.0-beta.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/dist/components/CodeView.d.ts +324 -0
- package/dist/components/CodeView.d.ts.map +1 -0
- package/dist/components/CodeView.js +1245 -0
- package/dist/components/CodeView.js.map +1 -0
- package/dist/components/File.d.ts +13 -12
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +68 -28
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +9 -10
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +57 -30
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/FileStream.js +9 -3
- package/dist/components/FileStream.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts +28 -5
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +225 -45
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +28 -5
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +285 -49
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +6 -3
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +4 -6
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/components/VirtulizerDevelopment.d.ts +2 -2
- package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
- package/dist/constants.d.ts +6 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +17 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +11 -10
- package/dist/managers/InteractionManager.d.ts +11 -7
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +38 -25
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/managers/ResizeManager.d.ts +4 -4
- package/dist/managers/ResizeManager.d.ts.map +1 -1
- package/dist/managers/ResizeManager.js +89 -54
- package/dist/managers/ResizeManager.js.map +1 -1
- package/dist/managers/UniversalRenderingManager.d.ts +2 -1
- package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
- package/dist/managers/UniversalRenderingManager.js +13 -16
- package/dist/managers/UniversalRenderingManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +45 -0
- package/dist/react/CodeView.d.ts.map +1 -0
- package/dist/react/CodeView.js +241 -0
- package/dist/react/CodeView.js.map +1 -0
- package/dist/react/File.d.ts +0 -1
- package/dist/react/File.d.ts.map +1 -1
- package/dist/react/File.js +2 -3
- package/dist/react/File.js.map +1 -1
- package/dist/react/FileDiff.d.ts +0 -1
- package/dist/react/FileDiff.d.ts.map +1 -1
- package/dist/react/FileDiff.js +3 -4
- package/dist/react/FileDiff.js.map +1 -1
- package/dist/react/MultiFileDiff.d.ts +0 -1
- package/dist/react/MultiFileDiff.d.ts.map +1 -1
- package/dist/react/MultiFileDiff.js +3 -4
- package/dist/react/MultiFileDiff.js.map +1 -1
- package/dist/react/PatchDiff.d.ts +0 -1
- package/dist/react/PatchDiff.d.ts.map +1 -1
- package/dist/react/PatchDiff.js +3 -4
- package/dist/react/PatchDiff.js.map +1 -1
- package/dist/react/UnresolvedFile.d.ts +0 -1
- package/dist/react/UnresolvedFile.d.ts.map +1 -1
- package/dist/react/UnresolvedFile.js +3 -4
- package/dist/react/UnresolvedFile.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +5 -4
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.d.ts +0 -8
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.d.ts +0 -2
- package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.js +3 -4
- package/dist/react/utils/renderDiffChildren.js.map +1 -1
- package/dist/react/utils/renderFileChildren.d.ts +0 -2
- package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
- package/dist/react/utils/renderFileChildren.js +3 -4
- package/dist/react/utils/renderFileChildren.js.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js +12 -7
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.js +12 -7
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.js +6 -2
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +2 -1
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +35 -20
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +2 -1
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +34 -20
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.js +1 -1
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +98 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/areManagedSnapshotsEqual.d.ts +7 -0
- package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -0
- package/dist/utils/areManagedSnapshotsEqual.js +15 -0
- package/dist/utils/areManagedSnapshotsEqual.js.map +1 -0
- package/dist/utils/areOptionsEqual.d.ts +2 -1
- package/dist/utils/areOptionsEqual.d.ts.map +1 -1
- package/dist/utils/areOptionsEqual.js +1 -1
- package/dist/utils/areOptionsEqual.js.map +1 -1
- package/dist/utils/createFileHeaderElement.d.ts +3 -1
- package/dist/utils/createFileHeaderElement.d.ts.map +1 -1
- package/dist/utils/createFileHeaderElement.js +3 -2
- package/dist/utils/createFileHeaderElement.js.map +1 -1
- package/dist/utils/createWindowFromScrollPosition.d.ts +3 -3
- package/dist/utils/createWindowFromScrollPosition.d.ts.map +1 -1
- package/dist/utils/createWindowFromScrollPosition.js +6 -6
- package/dist/utils/createWindowFromScrollPosition.js.map +1 -1
- package/dist/utils/iterateOverDiff.d.ts +2 -1
- package/dist/utils/iterateOverDiff.d.ts.map +1 -1
- package/dist/utils/iterateOverDiff.js +135 -7
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/parsePatchFiles.js +2 -2
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/renderFileWithHighlighter.js +1 -1
- package/dist/utils/resolveVirtualFileMetrics.d.ts +4 -1
- package/dist/utils/resolveVirtualFileMetrics.d.ts.map +1 -1
- package/dist/utils/resolveVirtualFileMetrics.js +11 -1
- package/dist/utils/resolveVirtualFileMetrics.js.map +1 -1
- package/dist/utils/roundToDevicePixel.d.ts +14 -0
- package/dist/utils/roundToDevicePixel.d.ts.map +1 -0
- package/dist/utils/roundToDevicePixel.js +18 -0
- package/dist/utils/roundToDevicePixel.js.map +1 -0
- package/dist/worker/worker-portable.js +195 -14
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +146 -7
- package/dist/worker/worker.js.map +1 -1
- package/package.json +10 -1
- package/dist/components/AdvancedVirtualizedFileDiff.d.ts +0 -40
- package/dist/components/AdvancedVirtualizedFileDiff.d.ts.map +0 -1
- package/dist/components/AdvancedVirtualizedFileDiff.js +0 -140
- package/dist/components/AdvancedVirtualizedFileDiff.js.map +0 -1
- package/dist/components/AdvancedVirtualizer.d.ts +0 -38
- package/dist/components/AdvancedVirtualizer.d.ts.map +0 -1
- package/dist/components/AdvancedVirtualizer.js +0 -201
- package/dist/components/AdvancedVirtualizer.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { VirtualFileMetrics } from "../types.js";
|
|
1
|
+
import { FileContents, NumericScrollLineAnchor, StickySpecs, VirtualFileMetrics } from "../types.js";
|
|
2
2
|
import { WorkerPoolManager } from "../worker/WorkerPoolManager.js";
|
|
3
3
|
import "../worker/index.js";
|
|
4
4
|
import { File, FileOptions, FileRenderProps } from "./File.js";
|
|
5
5
|
import { Virtualizer } from "./Virtualizer.js";
|
|
6
|
+
import { CodeView } from "./CodeView.js";
|
|
6
7
|
|
|
7
8
|
//#region src/components/VirtualizedFile.d.ts
|
|
8
9
|
declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation> {
|
|
@@ -11,22 +12,44 @@ declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation>
|
|
|
11
12
|
readonly __id: string;
|
|
12
13
|
top: number | undefined;
|
|
13
14
|
height: number;
|
|
14
|
-
private
|
|
15
|
+
private cache;
|
|
15
16
|
private isVisible;
|
|
16
17
|
private isSetup;
|
|
17
|
-
|
|
18
|
+
private forceRenderOverride;
|
|
19
|
+
constructor(options: FileOptions<LAnnotation> | undefined, virtualizer: Virtualizer | CodeView<LAnnotation>, metrics?: VirtualFileMetrics, workerManager?: WorkerPoolManager, isContainerManaged?: boolean);
|
|
20
|
+
setMetrics(metrics: VirtualFileMetrics, force?: boolean): void;
|
|
18
21
|
getLineHeight(lineIndex: number, hasMetadataLine?: boolean): number;
|
|
19
22
|
setOptions(options: FileOptions<LAnnotation> | undefined): void;
|
|
20
|
-
reconcileHeights():
|
|
23
|
+
reconcileHeights(): boolean;
|
|
21
24
|
onRender: (dirty: boolean) => boolean;
|
|
22
|
-
|
|
25
|
+
prepareVirtualizedItem(file: FileContents): number;
|
|
26
|
+
getLinePosition(lineNumber: number): {
|
|
27
|
+
top: number;
|
|
28
|
+
height: number;
|
|
29
|
+
} | undefined;
|
|
30
|
+
getNumericScrollAnchor(localViewportTop: number): NumericScrollLineAnchor | undefined;
|
|
31
|
+
getVirtualizedHeight(): number;
|
|
32
|
+
getAdvancedStickySpecs(): StickySpecs | undefined;
|
|
33
|
+
cleanUp(recycle?: boolean): void;
|
|
23
34
|
private computeApproximateSize;
|
|
24
35
|
setVisibility(visible: boolean): void;
|
|
36
|
+
rerender(): void;
|
|
25
37
|
render({
|
|
26
38
|
fileContainer,
|
|
27
39
|
file,
|
|
40
|
+
forceRender,
|
|
28
41
|
...props
|
|
29
42
|
}: FileRenderProps<LAnnotation>): boolean;
|
|
43
|
+
syncVirtualizedTop(): void;
|
|
44
|
+
protected shouldDisableVirtualizationBuffers(): boolean;
|
|
45
|
+
private isSimpleMode;
|
|
46
|
+
private isAdvancedMode;
|
|
47
|
+
private addLayoutCheckpoint;
|
|
48
|
+
private getLayoutCheckpointBeforeLineIndex;
|
|
49
|
+
private getLayoutCheckpointBeforeTop;
|
|
50
|
+
private getVirtualizedTop;
|
|
51
|
+
private getSimpleVirtualizer;
|
|
52
|
+
private isResizeDebuggingEnabled;
|
|
30
53
|
private computeRenderRangeFromWindow;
|
|
31
54
|
}
|
|
32
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedFile.d.ts","names":["VirtualFileMetrics","WorkerPoolManager","File","FileOptions","FileRenderProps","Virtualizer","VirtualizedFile","LAnnotation","fileContainer","file"],"sources":["../../src/components/VirtualizedFile.d.ts"],"sourcesContent":["import type { VirtualFileMetrics } from '../types';\nimport type { WorkerPoolManager } from '../worker';\nimport { File, type FileOptions, type FileRenderProps } from './File';\nimport type { Virtualizer } from './Virtualizer';\nexport declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation> {\n private virtualizer;\n private metrics;\n readonly __id: string;\n top: number | undefined;\n height: number;\n private
|
|
1
|
+
{"version":3,"file":"VirtualizedFile.d.ts","names":["FileContents","NumericScrollLineAnchor","StickySpecs","VirtualFileMetrics","WorkerPoolManager","CodeView","File","FileOptions","FileRenderProps","Virtualizer","VirtualizedFile","LAnnotation","fileContainer","file","forceRender"],"sources":["../../src/components/VirtualizedFile.d.ts"],"sourcesContent":["import type { FileContents, NumericScrollLineAnchor, StickySpecs, VirtualFileMetrics } from '../types';\nimport type { WorkerPoolManager } from '../worker';\nimport type { CodeView } from './CodeView';\nimport { File, type FileOptions, type FileRenderProps } from './File';\nimport type { Virtualizer } from './Virtualizer';\nexport declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation> {\n private virtualizer;\n private metrics;\n readonly __id: string;\n top: number | undefined;\n height: number;\n private cache;\n private isVisible;\n private isSetup;\n private forceRenderOverride;\n constructor(options: FileOptions<LAnnotation> | undefined, virtualizer: Virtualizer | CodeView<LAnnotation>, metrics?: VirtualFileMetrics, workerManager?: WorkerPoolManager, isContainerManaged?: boolean);\n setMetrics(metrics: VirtualFileMetrics, force?: boolean): void;\n getLineHeight(lineIndex: number, hasMetadataLine?: boolean): number;\n setOptions(options: FileOptions<LAnnotation> | undefined): void;\n reconcileHeights(): boolean;\n onRender: (dirty: boolean) => boolean;\n prepareVirtualizedItem(file: FileContents): number;\n getLinePosition(lineNumber: number): {\n top: number;\n height: number;\n } | undefined;\n getNumericScrollAnchor(localViewportTop: number): NumericScrollLineAnchor | undefined;\n getVirtualizedHeight(): number;\n getAdvancedStickySpecs(): StickySpecs | undefined;\n cleanUp(recycle?: boolean): void;\n private computeApproximateSize;\n setVisibility(visible: boolean): void;\n rerender(): void;\n render({ fileContainer, file, forceRender, ...props }: FileRenderProps<LAnnotation>): boolean;\n syncVirtualizedTop(): void;\n protected shouldDisableVirtualizationBuffers(): boolean;\n private isSimpleMode;\n private isAdvancedMode;\n private addLayoutCheckpoint;\n private getLayoutCheckpointBeforeLineIndex;\n private getLayoutCheckpointBeforeTop;\n private getVirtualizedTop;\n private getSimpleVirtualizer;\n private isResizeDebuggingEnabled;\n private computeRenderRangeFromWindow;\n}\n//# sourceMappingURL=VirtualizedFile.d.ts.map"],"mappings":";;;;;;;;cAKqBU,iDAAiDJ,KAAKK;;;EAAtDD,SAAAA,IAAAA,EAAAA,MAAe;EAAuCC,GAAAA,EAAAA,MAAAA,GAAAA,SAAAA;EAUtCA,MAAAA,EAAAA,MAAAA;EAAZJ,QAAAA,KAAAA;EAAmDE,QAAAA,SAAAA;EAAuBE,QAAAA,OAAAA;EAATN,QAAAA,mBAAAA;EAAiCF,WAAAA,CAAAA,OAAAA,EAAlGI,WAAkGJ,CAAtFQ,WAAsFR,CAAAA,GAAAA,SAAAA,EAAAA,WAAAA,EAA/CM,WAA+CN,GAAjCE,QAAiCF,CAAxBQ,WAAwBR,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,kBAAAA,EAAAA,aAAAA,CAAAA,EAAoCC,iBAApCD,EAAAA,kBAAAA,CAAAA,EAAAA,OAAAA;EAAoCC,UAAAA,CAAAA,OAAAA,EACvID,kBADuIC,EAAAA,KAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EACvID,aAAAA,CAAAA,SAAAA,EAAAA,MAAAA,EAAAA,eAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,MAAAA;EAEYQ,UAAAA,CAAAA,OAAAA,EAAZJ,WAAYI,CAAAA,WAAAA,CAAAA,GAAAA,SAAAA,CAAAA,EAAAA,IAAAA;EAAZJ,gBAAAA,CAAAA,CAAAA,EAAAA,OAAAA;EAGSP,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,OAAAA,EAAAA,GAAAA,OAAAA;EAKqBC,sBAAAA,CAAAA,IAAAA,EALrBD,YAKqBC,CAAAA,EAAAA,MAAAA;EAExBC,eAAAA,CAAAA,UAAAA,EAAAA,MAAAA,CAAAA,EAAAA;IAKjBU,GAAAA,EAAAA,MAAAA;IAAeC,MAAAA,EAAAA,MAAAA;EAAMC,CAAAA,GAAAA,SAAAA;EAAyCH,sBAAAA,CAAAA,gBAAAA,EAAAA,MAAAA,CAAAA,EAPrBV,uBAOqBU,GAAAA,SAAAA;EAAhBH,oBAAAA,CAAAA,CAAAA,EAAAA,MAAAA;EA5BWF,sBAAAA,CAAAA,CAAAA,EAuBxCJ,WAvBwCI,GAAAA,SAAAA;EAAI,OAAA,CAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,IAAA;;;;;;;;;KA4BfE,gBAAgBG"}
|
|
@@ -1,23 +1,37 @@
|
|
|
1
1
|
import { DEFAULT_VIRTUAL_FILE_METRICS } from "../constants.js";
|
|
2
|
+
import { areObjectsEqual } from "../utils/areObjectsEqual.js";
|
|
2
3
|
import { iterateOverFile } from "../utils/iterateOverFile.js";
|
|
4
|
+
import { getVirtualFileHeaderRegion, getVirtualFilePaddingBottom } from "../utils/resolveVirtualFileMetrics.js";
|
|
3
5
|
import { File } from "./File.js";
|
|
4
6
|
|
|
5
7
|
//#region src/components/VirtualizedFile.ts
|
|
8
|
+
const LAYOUT_CHECKPOINT_INTERVAL = 5e3;
|
|
6
9
|
let instanceId = -1;
|
|
7
10
|
var VirtualizedFile = class extends File {
|
|
8
11
|
__id = `virtualized-file:${++instanceId}`;
|
|
9
12
|
top;
|
|
10
13
|
height = 0;
|
|
11
|
-
|
|
14
|
+
cache = {
|
|
15
|
+
heights: /* @__PURE__ */ new Map(),
|
|
16
|
+
checkpoints: []
|
|
17
|
+
};
|
|
12
18
|
isVisible = false;
|
|
13
19
|
isSetup = false;
|
|
20
|
+
forceRenderOverride;
|
|
14
21
|
constructor(options, virtualizer, metrics = DEFAULT_VIRTUAL_FILE_METRICS, workerManager, isContainerManaged = false) {
|
|
15
22
|
super(options, workerManager, isContainerManaged);
|
|
16
23
|
this.virtualizer = virtualizer;
|
|
17
24
|
this.metrics = metrics;
|
|
18
25
|
}
|
|
26
|
+
setMetrics(metrics, force = false) {
|
|
27
|
+
if (!force && areObjectsEqual(this.metrics, metrics)) return;
|
|
28
|
+
this.metrics = metrics;
|
|
29
|
+
this.cache.heights.clear();
|
|
30
|
+
this.cache.checkpoints = [];
|
|
31
|
+
this.renderRange = void 0;
|
|
32
|
+
}
|
|
19
33
|
getLineHeight(lineIndex, hasMetadataLine = false) {
|
|
20
|
-
const cached = this.
|
|
34
|
+
const cached = this.cache.heights.get(lineIndex);
|
|
21
35
|
if (cached != null) return cached;
|
|
22
36
|
const multiplier = hasMetadataLine ? 2 : 1;
|
|
23
37
|
return this.metrics.lineHeight * multiplier;
|
|
@@ -28,24 +42,26 @@ var VirtualizedFile = class extends File {
|
|
|
28
42
|
const previousCollapsed = this.options.collapsed;
|
|
29
43
|
super.setOptions(options);
|
|
30
44
|
if (previousOverflow !== this.options.overflow || previousCollapsed !== this.options.collapsed) {
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
45
|
+
this.cache.heights.clear();
|
|
46
|
+
this.cache.checkpoints = [];
|
|
47
|
+
if (this.isSimpleMode()) this.computeApproximateSize();
|
|
33
48
|
this.renderRange = void 0;
|
|
34
49
|
}
|
|
35
|
-
this.virtualizer.instanceChanged(this);
|
|
50
|
+
if (this.isSimpleMode()) this.virtualizer.instanceChanged(this, true);
|
|
36
51
|
}
|
|
37
52
|
reconcileHeights() {
|
|
53
|
+
let hasHeightChange = false;
|
|
38
54
|
if (this.fileContainer == null || this.file == null) {
|
|
55
|
+
if (this.height !== 0) hasHeightChange = true;
|
|
39
56
|
this.height = 0;
|
|
40
|
-
return;
|
|
57
|
+
return hasHeightChange;
|
|
41
58
|
}
|
|
42
59
|
const { overflow = "scroll" } = this.options;
|
|
43
|
-
this.top = this.
|
|
44
|
-
if (overflow === "scroll" && this.lineAnnotations.length === 0 && !this.
|
|
45
|
-
|
|
46
|
-
if (this.code == null) return;
|
|
60
|
+
this.top = this.getVirtualizedTop();
|
|
61
|
+
if (overflow === "scroll" && this.lineAnnotations.length === 0 && !this.isResizeDebuggingEnabled()) return hasHeightChange;
|
|
62
|
+
if (this.code == null) return hasHeightChange;
|
|
47
63
|
const content = this.code.children[1];
|
|
48
|
-
if (!(content instanceof HTMLElement)) return;
|
|
64
|
+
if (!(content instanceof HTMLElement)) return hasHeightChange;
|
|
49
65
|
for (const line of content.children) {
|
|
50
66
|
if (!(line instanceof HTMLElement)) continue;
|
|
51
67
|
const lineIndexAttr = line.dataset.lineIndex;
|
|
@@ -59,41 +75,120 @@ var VirtualizedFile = class extends File {
|
|
|
59
75
|
}
|
|
60
76
|
const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);
|
|
61
77
|
if (measuredHeight === expectedHeight) continue;
|
|
62
|
-
|
|
63
|
-
if (measuredHeight === this.metrics.lineHeight * (hasMetadata ? 2 : 1)) this.
|
|
64
|
-
else this.
|
|
78
|
+
hasHeightChange = true;
|
|
79
|
+
if (measuredHeight === this.metrics.lineHeight * (hasMetadata ? 2 : 1)) this.cache.heights.delete(lineIndex);
|
|
80
|
+
else this.cache.heights.set(lineIndex, measuredHeight);
|
|
65
81
|
}
|
|
66
|
-
if (
|
|
82
|
+
if (hasHeightChange || this.isResizeDebuggingEnabled()) this.computeApproximateSize();
|
|
83
|
+
return hasHeightChange;
|
|
67
84
|
}
|
|
68
85
|
onRender = (dirty) => {
|
|
69
86
|
if (this.fileContainer == null || this.file == null) return false;
|
|
70
|
-
if (dirty) this.top = this.
|
|
87
|
+
if (dirty) this.top = this.getVirtualizedTop();
|
|
71
88
|
return this.render({ file: this.file });
|
|
72
89
|
};
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
prepareVirtualizedItem(file) {
|
|
91
|
+
this.file = file;
|
|
92
|
+
this.top = this.getVirtualizedTop();
|
|
93
|
+
this.computeApproximateSize();
|
|
94
|
+
return this.height;
|
|
95
|
+
}
|
|
96
|
+
getLinePosition(lineNumber) {
|
|
97
|
+
if (this.file == null) return;
|
|
98
|
+
const { disableFileHeader = false, collapsed = false } = this.options;
|
|
99
|
+
const lastLineIndex = getLastVisibleLineIndex(this.getOrCreateLineCache(this.file));
|
|
100
|
+
let top = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
101
|
+
if (collapsed || lastLineIndex < 0) return {
|
|
102
|
+
top,
|
|
103
|
+
height: 0
|
|
104
|
+
};
|
|
105
|
+
const clampedLineIndex = Math.min(Math.max(lineNumber - 1, 0), lastLineIndex);
|
|
106
|
+
const { overflow = "scroll" } = this.options;
|
|
107
|
+
const { lineHeight } = this.metrics;
|
|
108
|
+
if (overflow === "scroll" && this.lineAnnotations.length === 0) return {
|
|
109
|
+
top: top + clampedLineIndex * lineHeight,
|
|
110
|
+
height: lineHeight
|
|
111
|
+
};
|
|
112
|
+
const checkpoint = this.getLayoutCheckpointBeforeLineIndex(clampedLineIndex);
|
|
113
|
+
top = checkpoint?.top ?? top;
|
|
114
|
+
for (let lineIndex = checkpoint?.lineIndex ?? 0; lineIndex < clampedLineIndex; lineIndex++) top += this.getLineHeight(lineIndex, false);
|
|
115
|
+
return {
|
|
116
|
+
top,
|
|
117
|
+
height: this.getLineHeight(clampedLineIndex, false)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
getNumericScrollAnchor(localViewportTop) {
|
|
121
|
+
if (this.file == null || this.renderRange == null) return;
|
|
122
|
+
const { disableFileHeader = false, collapsed = false, overflow = "scroll" } = this.options;
|
|
123
|
+
if (collapsed || this.renderRange.totalLines <= 0) return;
|
|
124
|
+
const lastLineIndex = getLastVisibleLineIndex(this.getOrCreateLineCache(this.file));
|
|
125
|
+
if (lastLineIndex < 0) return;
|
|
126
|
+
const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
127
|
+
const firstRenderedLineIndex = Math.min(this.renderRange.startingLine, lastLineIndex);
|
|
128
|
+
const lastRenderedLineIndex = Math.min(firstRenderedLineIndex + this.renderRange.totalLines - 1, lastLineIndex);
|
|
129
|
+
if (lastRenderedLineIndex < firstRenderedLineIndex) return;
|
|
130
|
+
if (overflow === "scroll" && this.lineAnnotations.length === 0) {
|
|
131
|
+
const { lineHeight } = this.metrics;
|
|
132
|
+
const firstRenderedLineTop = headerRegion + this.renderRange.bufferBefore;
|
|
133
|
+
const lineIndex = firstRenderedLineIndex + Math.max(Math.ceil((localViewportTop - firstRenderedLineTop) / lineHeight), 0);
|
|
134
|
+
if (lineIndex > lastRenderedLineIndex) return;
|
|
135
|
+
return {
|
|
136
|
+
lineNumber: lineIndex + 1,
|
|
137
|
+
top: headerRegion + lineIndex * lineHeight
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
let top = headerRegion + this.renderRange.bufferBefore;
|
|
141
|
+
for (let lineIndex = firstRenderedLineIndex; lineIndex <= lastRenderedLineIndex; lineIndex++) {
|
|
142
|
+
if (top >= localViewportTop) return {
|
|
143
|
+
lineNumber: lineIndex + 1,
|
|
144
|
+
top
|
|
145
|
+
};
|
|
146
|
+
top += this.getLineHeight(lineIndex);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
getVirtualizedHeight() {
|
|
150
|
+
return this.height;
|
|
151
|
+
}
|
|
152
|
+
getAdvancedStickySpecs() {
|
|
153
|
+
if (this.top == null) return;
|
|
154
|
+
if (this.options.collapsed === true) return {
|
|
155
|
+
topOffset: this.top,
|
|
156
|
+
height: this.height
|
|
157
|
+
};
|
|
158
|
+
if (this.renderRange == null) return;
|
|
159
|
+
const { bufferBefore, bufferAfter, totalLines } = this.renderRange;
|
|
160
|
+
return {
|
|
161
|
+
topOffset: this.top + bufferBefore + (totalLines === 0 ? bufferAfter : 0),
|
|
162
|
+
height: this.height - (bufferBefore + bufferAfter)
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
cleanUp(recycle = false) {
|
|
166
|
+
if (this.fileContainer != null && this.isSimpleMode()) this.getSimpleVirtualizer()?.disconnect(this.fileContainer);
|
|
75
167
|
this.isSetup = false;
|
|
76
|
-
super.cleanUp();
|
|
168
|
+
super.cleanUp(recycle);
|
|
77
169
|
}
|
|
78
170
|
computeApproximateSize() {
|
|
79
171
|
const isFirstCompute = this.height === 0;
|
|
80
172
|
this.height = 0;
|
|
173
|
+
this.cache.checkpoints = [];
|
|
81
174
|
if (this.file == null) return;
|
|
82
175
|
const { disableFileHeader = false, collapsed = false, overflow = "scroll" } = this.options;
|
|
83
|
-
const {
|
|
176
|
+
const { lineHeight } = this.metrics;
|
|
84
177
|
const lines = this.getOrCreateLineCache(this.file);
|
|
85
|
-
|
|
86
|
-
|
|
178
|
+
const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
179
|
+
const paddingBottom = getVirtualFilePaddingBottom(this.metrics);
|
|
180
|
+
this.height += headerRegion;
|
|
87
181
|
if (collapsed) return;
|
|
88
182
|
if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
|
|
89
183
|
else iterateOverFile({
|
|
90
184
|
lines,
|
|
91
185
|
callback: ({ lineIndex }) => {
|
|
186
|
+
this.addLayoutCheckpoint(lineIndex, this.height);
|
|
92
187
|
this.height += this.getLineHeight(lineIndex, false);
|
|
93
188
|
}
|
|
94
189
|
});
|
|
95
|
-
if (lines.length > 0) this.height +=
|
|
96
|
-
if (this.fileContainer != null && this.
|
|
190
|
+
if (lines.length > 0) this.height += paddingBottom;
|
|
191
|
+
if (this.fileContainer != null && this.isResizeDebuggingEnabled() && !isFirstCompute) {
|
|
97
192
|
const rect = this.fileContainer.getBoundingClientRect();
|
|
98
193
|
if (rect.height !== this.height) console.log("VirtualizedFile.computeApproximateSize: computed height doesnt match", {
|
|
99
194
|
name: this.file.name,
|
|
@@ -104,17 +199,23 @@ var VirtualizedFile = class extends File {
|
|
|
104
199
|
}
|
|
105
200
|
}
|
|
106
201
|
setVisibility(visible) {
|
|
107
|
-
if (this.fileContainer == null) return;
|
|
202
|
+
if (this.isAdvancedMode() || this.fileContainer == null) return;
|
|
108
203
|
if (visible && !this.isVisible) {
|
|
109
|
-
this.top = this.
|
|
204
|
+
this.top = this.getVirtualizedTop();
|
|
110
205
|
this.isVisible = true;
|
|
111
206
|
} else if (!visible && this.isVisible) {
|
|
112
207
|
this.isVisible = false;
|
|
113
208
|
this.rerender();
|
|
114
209
|
}
|
|
115
210
|
}
|
|
116
|
-
|
|
117
|
-
|
|
211
|
+
rerender() {
|
|
212
|
+
if (!this.enabled || this.file == null) return;
|
|
213
|
+
this.forceRenderOverride = true;
|
|
214
|
+
this.virtualizer.instanceChanged(this, false);
|
|
215
|
+
}
|
|
216
|
+
render({ fileContainer, file, forceRender = false,...props }) {
|
|
217
|
+
const { forceRenderOverride, isSetup } = this;
|
|
218
|
+
this.forceRenderOverride = void 0;
|
|
118
219
|
this.file ??= file;
|
|
119
220
|
fileContainer = this.getOrCreateFileContainerNode(fileContainer);
|
|
120
221
|
if (this.file == null) {
|
|
@@ -123,33 +224,106 @@ var VirtualizedFile = class extends File {
|
|
|
123
224
|
}
|
|
124
225
|
if (!isSetup) {
|
|
125
226
|
this.computeApproximateSize();
|
|
126
|
-
this.
|
|
127
|
-
this.top ??= this.
|
|
128
|
-
|
|
227
|
+
const virtualizer = this.getSimpleVirtualizer();
|
|
228
|
+
this.top ??= this.getVirtualizedTop();
|
|
229
|
+
if (this.isAdvancedMode()) this.isVisible = true;
|
|
230
|
+
else {
|
|
231
|
+
if (virtualizer == null) throw new Error("VirtualizedFile.render: simple virtualizer is not available");
|
|
232
|
+
virtualizer.connect(fileContainer, this);
|
|
233
|
+
this.isVisible = virtualizer.isInstanceVisible(this.top ?? 0, this.height);
|
|
234
|
+
}
|
|
129
235
|
this.isSetup = true;
|
|
130
|
-
} else this.top ??= this.
|
|
131
|
-
if (!this.isVisible) return this.renderPlaceholder(this.height);
|
|
236
|
+
} else this.top ??= this.getVirtualizedTop();
|
|
237
|
+
if (!this.isVisible && this.isSimpleMode()) return this.renderPlaceholder(this.height);
|
|
132
238
|
const windowSpecs = this.virtualizer.getWindowSpecs();
|
|
133
|
-
const
|
|
239
|
+
const fileTop = this.top ?? 0;
|
|
240
|
+
const renderRange = this.computeRenderRangeFromWindow(this.file, fileTop, windowSpecs);
|
|
134
241
|
return super.render({
|
|
135
242
|
file: this.file,
|
|
136
243
|
fileContainer,
|
|
137
244
|
renderRange,
|
|
245
|
+
forceRender: forceRenderOverride ?? forceRender,
|
|
138
246
|
...props
|
|
139
247
|
});
|
|
140
248
|
}
|
|
249
|
+
syncVirtualizedTop() {
|
|
250
|
+
this.top = this.getVirtualizedTop();
|
|
251
|
+
}
|
|
252
|
+
shouldDisableVirtualizationBuffers() {
|
|
253
|
+
return this.isAdvancedMode() || super.shouldDisableVirtualizationBuffers();
|
|
254
|
+
}
|
|
255
|
+
isSimpleMode() {
|
|
256
|
+
return this.virtualizer.type === "simple";
|
|
257
|
+
}
|
|
258
|
+
isAdvancedMode() {
|
|
259
|
+
return this.virtualizer.type === "advanced";
|
|
260
|
+
}
|
|
261
|
+
addLayoutCheckpoint(lineIndex, top) {
|
|
262
|
+
if (lineIndex % LAYOUT_CHECKPOINT_INTERVAL !== 0) return;
|
|
263
|
+
this.cache.checkpoints.push({
|
|
264
|
+
lineIndex,
|
|
265
|
+
top
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
getLayoutCheckpointBeforeLineIndex(lineIndex) {
|
|
269
|
+
if (lineIndex <= 0 || this.cache.checkpoints.length === 0) return;
|
|
270
|
+
let low = 0;
|
|
271
|
+
let high = this.cache.checkpoints.length - 1;
|
|
272
|
+
let result;
|
|
273
|
+
while (low <= high) {
|
|
274
|
+
const mid = low + high >> 1;
|
|
275
|
+
const checkpoint = this.cache.checkpoints[mid];
|
|
276
|
+
if (checkpoint == null) throw new Error("VirtualizedFile: invalid checkpoint index");
|
|
277
|
+
if (checkpoint.lineIndex <= lineIndex) {
|
|
278
|
+
result = checkpoint;
|
|
279
|
+
low = mid + 1;
|
|
280
|
+
} else high = mid - 1;
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
getLayoutCheckpointBeforeTop(top, hunkLineCount) {
|
|
285
|
+
let low = 0;
|
|
286
|
+
let high = this.cache.checkpoints.length - 1;
|
|
287
|
+
let resultIndex = -1;
|
|
288
|
+
while (low <= high) {
|
|
289
|
+
const mid = low + high >> 1;
|
|
290
|
+
const checkpoint = this.cache.checkpoints[mid];
|
|
291
|
+
if (checkpoint == null) throw new Error("VirtualizedFile: invalid checkpoint index");
|
|
292
|
+
if (checkpoint.top <= top) {
|
|
293
|
+
resultIndex = mid;
|
|
294
|
+
low = mid + 1;
|
|
295
|
+
} else high = mid - 1;
|
|
296
|
+
}
|
|
297
|
+
if (hunkLineCount == null) return resultIndex >= 0 ? this.cache.checkpoints[resultIndex] : void 0;
|
|
298
|
+
for (let index = resultIndex; index >= 0; index--) {
|
|
299
|
+
const checkpoint = this.cache.checkpoints[index];
|
|
300
|
+
if (checkpoint == null) throw new Error("VirtualizedFile: invalid checkpoint index");
|
|
301
|
+
if (checkpoint.lineIndex % hunkLineCount === 0) return checkpoint;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
getVirtualizedTop() {
|
|
305
|
+
if (this.virtualizer.type === "advanced") return this.virtualizer.getTopForInstance(this);
|
|
306
|
+
return this.fileContainer != null ? this.virtualizer.getOffsetInScrollContainer(this.fileContainer) : 0;
|
|
307
|
+
}
|
|
308
|
+
getSimpleVirtualizer() {
|
|
309
|
+
return this.virtualizer.type === "simple" ? this.virtualizer : void 0;
|
|
310
|
+
}
|
|
311
|
+
isResizeDebuggingEnabled() {
|
|
312
|
+
return this.getSimpleVirtualizer()?.config.resizeDebugging ?? false;
|
|
313
|
+
}
|
|
141
314
|
computeRenderRangeFromWindow(file, fileTop, { top, bottom }) {
|
|
142
315
|
const { disableFileHeader = false, overflow = "scroll" } = this.options;
|
|
143
|
-
const {
|
|
316
|
+
const { hunkLineCount, lineHeight } = this.metrics;
|
|
144
317
|
const lines = this.getOrCreateLineCache(file);
|
|
145
318
|
const lineCount = lines.length;
|
|
146
319
|
const fileHeight = this.height;
|
|
147
|
-
const headerRegion = disableFileHeader
|
|
320
|
+
const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
|
|
321
|
+
const paddingBottom = lineCount > 0 ? getVirtualFilePaddingBottom(this.metrics) : 0;
|
|
148
322
|
if (fileTop < top - fileHeight || fileTop > bottom) return {
|
|
149
323
|
startingLine: 0,
|
|
150
324
|
totalLines: 0,
|
|
151
325
|
bufferBefore: 0,
|
|
152
|
-
bufferAfter: fileHeight - headerRegion -
|
|
326
|
+
bufferAfter: fileHeight - headerRegion - paddingBottom
|
|
153
327
|
};
|
|
154
328
|
if (lineCount <= hunkLineCount) return {
|
|
155
329
|
startingLine: 0,
|
|
@@ -178,24 +352,26 @@ var VirtualizedFile = class extends File {
|
|
|
178
352
|
}
|
|
179
353
|
const overflowHunks = totalHunks;
|
|
180
354
|
const hunkOffsets = [];
|
|
181
|
-
|
|
182
|
-
let
|
|
355
|
+
const checkpoint = this.getLayoutCheckpointBeforeTop(Math.max(0, top - fileTop - totalLines * lineHeight * 2), hunkLineCount);
|
|
356
|
+
let absoluteLineTop = fileTop + (checkpoint?.top ?? headerRegion);
|
|
357
|
+
let currentLine = checkpoint?.lineIndex ?? 0;
|
|
183
358
|
let firstVisibleHunk;
|
|
184
359
|
let centerHunk;
|
|
185
360
|
let overflowCounter;
|
|
186
361
|
iterateOverFile({
|
|
187
362
|
lines,
|
|
363
|
+
startingLine: checkpoint?.lineIndex ?? 0,
|
|
188
364
|
callback: ({ lineIndex }) => {
|
|
189
365
|
const isAtHunkBoundary = currentLine % hunkLineCount === 0;
|
|
366
|
+
const currentHunk = Math.floor(currentLine / hunkLineCount);
|
|
190
367
|
if (isAtHunkBoundary) {
|
|
191
|
-
hunkOffsets
|
|
368
|
+
hunkOffsets[currentHunk] = absoluteLineTop - (fileTop + headerRegion);
|
|
192
369
|
if (overflowCounter != null) {
|
|
193
370
|
if (overflowCounter <= 0) return true;
|
|
194
371
|
overflowCounter--;
|
|
195
372
|
}
|
|
196
373
|
}
|
|
197
374
|
const lineHeight$1 = this.getLineHeight(lineIndex, false);
|
|
198
|
-
const currentHunk = Math.floor(currentLine / hunkLineCount);
|
|
199
375
|
if (absoluteLineTop > top - lineHeight$1 && absoluteLineTop < bottom) firstVisibleHunk ??= currentHunk;
|
|
200
376
|
if (absoluteLineTop + lineHeight$1 > viewportCenter) centerHunk ??= currentHunk;
|
|
201
377
|
if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
|
|
@@ -208,12 +384,11 @@ var VirtualizedFile = class extends File {
|
|
|
208
384
|
startingLine: 0,
|
|
209
385
|
totalLines: 0,
|
|
210
386
|
bufferBefore: 0,
|
|
211
|
-
bufferAfter: fileHeight - headerRegion -
|
|
387
|
+
bufferAfter: fileHeight - headerRegion - paddingBottom
|
|
212
388
|
};
|
|
213
|
-
const collectedHunks = hunkOffsets.length;
|
|
214
389
|
centerHunk ??= firstVisibleHunk;
|
|
215
390
|
const idealStartHunk = Math.round(centerHunk - totalHunks / 2);
|
|
216
|
-
const maxStartHunk = Math.max(0,
|
|
391
|
+
const maxStartHunk = Math.max(0, Math.ceil(lineCount / hunkLineCount) - totalHunks);
|
|
217
392
|
const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));
|
|
218
393
|
const startingLine = startHunk * hunkLineCount;
|
|
219
394
|
const clampedTotalLines = idealStartHunk < 0 ? totalLines + idealStartHunk * hunkLineCount : totalLines;
|
|
@@ -223,10 +398,15 @@ var VirtualizedFile = class extends File {
|
|
|
223
398
|
startingLine,
|
|
224
399
|
totalLines: clampedTotalLines,
|
|
225
400
|
bufferBefore,
|
|
226
|
-
bufferAfter: finalHunkIndex < hunkOffsets.length ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] -
|
|
401
|
+
bufferAfter: finalHunkIndex < hunkOffsets.length ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] - paddingBottom : fileHeight - (absoluteLineTop - fileTop) - paddingBottom
|
|
227
402
|
};
|
|
228
403
|
}
|
|
229
404
|
};
|
|
405
|
+
function getLastVisibleLineIndex(lines) {
|
|
406
|
+
const lastLine = lines.at(-1);
|
|
407
|
+
if (lastLine == null || lastLine === "" || lastLine === "\n" || lastLine === "\r\n" || lastLine === "\r") return lines.length - 2;
|
|
408
|
+
return lines.length - 1;
|
|
409
|
+
}
|
|
230
410
|
|
|
231
411
|
//#endregion
|
|
232
412
|
export { VirtualizedFile };
|
|
@@ -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 private isSetup: 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 this.isSetup = false;\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 { isSetup } = this;\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 (!isSetup) {\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 this.isSetup = true;\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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"}
|
|
1
|
+
{"version":3,"file":"VirtualizedFile.js","names":["virtualizer: Virtualizer | CodeView<LAnnotation>","metrics: VirtualFileMetrics","result: FileLayoutCheckpoint | undefined","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 NumericScrollLineAnchor,\n RenderRange,\n RenderWindow,\n StickySpecs,\n VirtualFileMetrics,\n} from '../types';\nimport { areObjectsEqual } from '../utils/areObjectsEqual';\nimport { iterateOverFile } from '../utils/iterateOverFile';\nimport {\n getVirtualFileHeaderRegion,\n getVirtualFilePaddingBottom,\n} from '../utils/resolveVirtualFileMetrics';\nimport type { WorkerPoolManager } from '../worker';\nimport type { CodeView } from './CodeView';\nimport { File, type FileOptions, type FileRenderProps } from './File';\nimport type { Virtualizer } from './Virtualizer';\n\ninterface FileLayoutCheckpoint {\n lineIndex: number;\n top: number;\n}\n\ninterface FileLayoutCache {\n // Sparse map: line index -> measured height. Only stores lines that differ\n // from what is returned by `getLineHeight`.\n heights: Map<number, number>;\n // Sparse measured positions used to resume deep geometry scans near a target\n // line or scroll offset instead of replaying layout from the start.\n checkpoints: FileLayoutCheckpoint[];\n}\n\nconst LAYOUT_CHECKPOINT_INTERVAL = 5_000;\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 private cache: FileLayoutCache = { heights: new Map(), checkpoints: [] };\n private isVisible: boolean = false;\n private isSetup: boolean = false;\n private forceRenderOverride: true | undefined;\n\n constructor(\n options: FileOptions<LAnnotation> | undefined,\n private virtualizer: Virtualizer | CodeView<LAnnotation>,\n private metrics: VirtualFileMetrics = DEFAULT_VIRTUAL_FILE_METRICS,\n workerManager?: WorkerPoolManager,\n isContainerManaged = false\n ) {\n super(options, workerManager, isContainerManaged);\n }\n\n public setMetrics(metrics: VirtualFileMetrics, force = false): void {\n if (!force && areObjectsEqual(this.metrics, metrics)) {\n return;\n }\n\n this.metrics = metrics;\n this.cache.heights.clear();\n this.cache.checkpoints = [];\n this.renderRange = undefined;\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.cache.heights.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.cache.heights.clear();\n this.cache.checkpoints = [];\n // NOTE(amadeus): In CodeView we intentionally batch computes to all\n // happen at the same time, so we shouldn't trigger this here\n if (this.isSimpleMode()) {\n this.computeApproximateSize();\n }\n this.renderRange = undefined;\n }\n // CodeView will mark dirty for us\n if (this.isSimpleMode()) {\n this.virtualizer.instanceChanged(this, true);\n }\n }\n\n // Measure rendered lines and update height cache.\n // Called after render to reconcile estimated vs actual heights.\n public reconcileHeights(): boolean {\n let hasHeightChange = false;\n if (this.fileContainer == null || this.file == null) {\n if (this.height !== 0) {\n hasHeightChange = true;\n }\n this.height = 0;\n return hasHeightChange;\n }\n const { overflow = 'scroll' } = this.options;\n this.top = this.getVirtualizedTop();\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.isResizeDebuggingEnabled()\n ) {\n return hasHeightChange;\n }\n\n // Single code element (no split mode)\n if (this.code == null) {\n return hasHeightChange;\n }\n const content = this.code.children[1]; // Content column (gutter is [0])\n if (!(content instanceof HTMLElement)) {\n return hasHeightChange;\n }\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 hasHeightChange = 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.cache.heights.delete(lineIndex);\n }\n // Non-standard height, cache it\n else {\n this.cache.heights.set(lineIndex, measuredHeight);\n }\n }\n\n if (hasHeightChange || this.isResizeDebuggingEnabled()) {\n this.computeApproximateSize();\n }\n return hasHeightChange;\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.getVirtualizedTop();\n }\n return this.render({ file: this.file });\n };\n\n public prepareVirtualizedItem(file: FileContents): number {\n this.file = file;\n this.top = this.getVirtualizedTop();\n this.computeApproximateSize();\n return this.height;\n }\n\n public getLinePosition(\n lineNumber: number\n ): { top: number; height: number } | undefined {\n if (this.file == null) {\n return undefined;\n }\n\n const { disableFileHeader = false, collapsed = false } = this.options;\n const lines = this.getOrCreateLineCache(this.file);\n const lastLineIndex = getLastVisibleLineIndex(lines);\n let top = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);\n\n if (collapsed || lastLineIndex < 0) {\n return { top, height: 0 };\n }\n\n const clampedLineIndex = Math.min(\n Math.max(lineNumber - 1, 0),\n lastLineIndex\n );\n const { overflow = 'scroll' } = this.options;\n const { lineHeight } = this.metrics;\n\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n return {\n top: top + clampedLineIndex * lineHeight,\n height: lineHeight,\n };\n }\n\n const checkpoint =\n this.getLayoutCheckpointBeforeLineIndex(clampedLineIndex);\n top = checkpoint?.top ?? top;\n for (\n let lineIndex = checkpoint?.lineIndex ?? 0;\n lineIndex < clampedLineIndex;\n lineIndex++\n ) {\n top += this.getLineHeight(lineIndex, false);\n }\n\n return {\n top,\n height: this.getLineHeight(clampedLineIndex, false),\n };\n }\n\n public getNumericScrollAnchor(\n localViewportTop: number\n ): NumericScrollLineAnchor | undefined {\n if (this.file == null || this.renderRange == null) {\n return undefined;\n }\n\n const {\n disableFileHeader = false,\n collapsed = false,\n overflow = 'scroll',\n } = this.options;\n if (collapsed || this.renderRange.totalLines <= 0) {\n return undefined;\n }\n\n const lines = this.getOrCreateLineCache(this.file);\n const lastLineIndex = getLastVisibleLineIndex(lines);\n if (lastLineIndex < 0) {\n return undefined;\n }\n\n const headerRegion = getVirtualFileHeaderRegion(\n this.metrics,\n disableFileHeader\n );\n const firstRenderedLineIndex = Math.min(\n this.renderRange.startingLine,\n lastLineIndex\n );\n const lastRenderedLineIndex = Math.min(\n firstRenderedLineIndex + this.renderRange.totalLines - 1,\n lastLineIndex\n );\n if (lastRenderedLineIndex < firstRenderedLineIndex) {\n return undefined;\n }\n\n // If we don't allow line wrapping and have no annotations, we can just\n // multiply our way to the the correct value\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n const { lineHeight } = this.metrics;\n const firstRenderedLineTop = headerRegion + this.renderRange.bufferBefore;\n const deltaLineCount = Math.max(\n Math.ceil((localViewportTop - firstRenderedLineTop) / lineHeight),\n 0\n );\n const lineIndex = firstRenderedLineIndex + deltaLineCount;\n if (lineIndex > lastRenderedLineIndex) {\n return undefined;\n }\n\n return {\n lineNumber: lineIndex + 1,\n top: headerRegion + lineIndex * lineHeight,\n };\n }\n\n // Otherwise we gotta iterate through the range\n let top = headerRegion + this.renderRange.bufferBefore;\n for (\n let lineIndex = firstRenderedLineIndex;\n lineIndex <= lastRenderedLineIndex;\n lineIndex++\n ) {\n if (top >= localViewportTop) {\n return {\n lineNumber: lineIndex + 1,\n top,\n };\n }\n top += this.getLineHeight(lineIndex);\n }\n\n return undefined;\n }\n\n public getVirtualizedHeight(): number {\n return this.height;\n }\n\n public getAdvancedStickySpecs(): StickySpecs | undefined {\n if (this.top == null) {\n return undefined;\n }\n\n if (this.options.collapsed === true) {\n return {\n topOffset: this.top,\n height: this.height,\n };\n }\n\n if (this.renderRange == null) {\n return undefined;\n }\n\n const { bufferBefore, bufferAfter, totalLines } = this.renderRange;\n return {\n topOffset: this.top + bufferBefore + (totalLines === 0 ? bufferAfter : 0),\n height: this.height - (bufferBefore + bufferAfter),\n };\n }\n\n override cleanUp(recycle = false): void {\n if (this.fileContainer != null && this.isSimpleMode()) {\n this.getSimpleVirtualizer()?.disconnect(this.fileContainer);\n }\n this.isSetup = false;\n super.cleanUp(recycle);\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 this.cache.checkpoints = [];\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 { lineHeight } = this.metrics;\n const lines = this.getOrCreateLineCache(this.file);\n const headerRegion = getVirtualFileHeaderRegion(\n this.metrics,\n disableFileHeader\n );\n const paddingBottom = getVirtualFilePaddingBottom(this.metrics);\n\n this.height += headerRegion;\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.addLayoutCheckpoint(lineIndex, this.height);\n this.height += this.getLineHeight(lineIndex, false);\n },\n });\n }\n\n if (lines.length > 0) {\n this.height += paddingBottom;\n }\n\n if (\n this.fileContainer != null &&\n this.isResizeDebuggingEnabled() &&\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.isAdvancedMode() || this.fileContainer == null) {\n return;\n }\n if (visible && !this.isVisible) {\n this.top = this.getVirtualizedTop();\n this.isVisible = true;\n } else if (!visible && this.isVisible) {\n this.isVisible = false;\n this.rerender();\n }\n }\n\n override rerender(): void {\n if (!this.enabled || this.file == null) {\n return;\n }\n this.forceRenderOverride = true;\n this.virtualizer.instanceChanged(this, false);\n }\n\n override render({\n fileContainer,\n file,\n forceRender = false,\n ...props\n }: FileRenderProps<LAnnotation>): boolean {\n const { forceRenderOverride, isSetup } = this;\n this.forceRenderOverride = undefined;\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 (!isSetup) {\n this.computeApproximateSize();\n const virtualizer = this.getSimpleVirtualizer();\n this.top ??= this.getVirtualizedTop();\n if (this.isAdvancedMode()) {\n this.isVisible = true;\n } else {\n if (virtualizer == null) {\n throw new Error(\n 'VirtualizedFile.render: simple virtualizer is not available'\n );\n }\n virtualizer.connect(fileContainer, this);\n this.isVisible = virtualizer.isInstanceVisible(\n this.top ?? 0,\n this.height\n );\n }\n this.isSetup = true;\n } else {\n this.top ??= this.getVirtualizedTop();\n }\n\n if (!this.isVisible && this.isSimpleMode()) {\n return this.renderPlaceholder(this.height);\n }\n\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const fileTop = this.top ?? 0;\n const renderRange = this.computeRenderRangeFromWindow(\n this.file,\n fileTop,\n windowSpecs\n );\n return super.render({\n file: this.file,\n fileContainer,\n renderRange,\n forceRender: forceRenderOverride ?? forceRender,\n ...props,\n });\n }\n\n public syncVirtualizedTop(): void {\n this.top = this.getVirtualizedTop();\n }\n\n protected override shouldDisableVirtualizationBuffers(): boolean {\n return this.isAdvancedMode() || super.shouldDisableVirtualizationBuffers();\n }\n\n private isSimpleMode(): boolean {\n return this.virtualizer.type === 'simple';\n }\n\n private isAdvancedMode(): boolean {\n return this.virtualizer.type === 'advanced';\n }\n\n private addLayoutCheckpoint(lineIndex: number, top: number): void {\n if (lineIndex % LAYOUT_CHECKPOINT_INTERVAL !== 0) {\n return;\n }\n this.cache.checkpoints.push({ lineIndex, top });\n }\n\n // Find the nearest sparse layout checkpoint at or before a raw file line.\n // Checkpoints store measured `top` offsets every few thousand lines, so a\n // binary search lets deep line-position lookups resume from that checkpoint\n // instead of replaying layout from the start of the file.\n private getLayoutCheckpointBeforeLineIndex(\n lineIndex: number\n ): FileLayoutCheckpoint | undefined {\n if (lineIndex <= 0 || this.cache.checkpoints.length === 0) {\n return undefined;\n }\n\n let low = 0;\n let high = this.cache.checkpoints.length - 1;\n let result: FileLayoutCheckpoint | undefined;\n\n while (low <= high) {\n const mid = (low + high) >> 1;\n const checkpoint = this.cache.checkpoints[mid];\n if (checkpoint == null) {\n throw new Error('VirtualizedFile: invalid checkpoint index');\n }\n if (checkpoint.lineIndex <= lineIndex) {\n result = checkpoint;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n return result;\n }\n\n // Find the nearest sparse layout checkpoint at or before a scroll offset.\n // Render-range scans start from this checkpoint so variable-height files\n // only replay the nearby measured rows. When `hunkLineCount` is provided,\n // step backward to a hunk boundary so hooks that depend on grouped lines\n // still see a complete hunk.\n private getLayoutCheckpointBeforeTop(\n top: number,\n hunkLineCount?: number\n ): FileLayoutCheckpoint | undefined {\n let low = 0;\n let high = this.cache.checkpoints.length - 1;\n let resultIndex = -1;\n\n while (low <= high) {\n const mid = (low + high) >> 1;\n const checkpoint = this.cache.checkpoints[mid];\n if (checkpoint == null) {\n throw new Error('VirtualizedFile: invalid checkpoint index');\n }\n if (checkpoint.top <= top) {\n resultIndex = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n if (hunkLineCount == null) {\n return resultIndex >= 0 ? this.cache.checkpoints[resultIndex] : undefined;\n }\n\n for (let index = resultIndex; index >= 0; index--) {\n const checkpoint = this.cache.checkpoints[index];\n if (checkpoint == null) {\n throw new Error('VirtualizedFile: invalid checkpoint index');\n }\n if (checkpoint.lineIndex % hunkLineCount === 0) {\n return checkpoint;\n }\n }\n\n return undefined;\n }\n\n private getVirtualizedTop(): number {\n if (this.virtualizer.type === 'advanced') {\n return this.virtualizer.getTopForInstance(this);\n }\n return this.fileContainer != null\n ? this.virtualizer.getOffsetInScrollContainer(this.fileContainer)\n : 0;\n }\n\n private getSimpleVirtualizer(): Virtualizer | undefined {\n return this.virtualizer.type === 'simple' ? this.virtualizer : undefined;\n }\n\n private isResizeDebuggingEnabled(): boolean {\n return this.getSimpleVirtualizer()?.config.resizeDebugging ?? false;\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 { hunkLineCount, lineHeight } = this.metrics;\n const lines = this.getOrCreateLineCache(file);\n const lineCount = lines.length;\n const fileHeight = this.height;\n const headerRegion = getVirtualFileHeaderRegion(\n this.metrics,\n disableFileHeader\n );\n const paddingBottom =\n lineCount > 0 ? getVirtualFilePaddingBottom(this.metrics) : 0;\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 - paddingBottom,\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 // Start the scan before the viewport so we collect hunk offsets that may be\n // needed for bufferBefore. This only chooses the scan origin; the returned\n // render range is still computed from the visible window below.\n const checkpoint = this.getLayoutCheckpointBeforeTop(\n Math.max(0, top - fileTop - totalLines * lineHeight * 2),\n hunkLineCount\n );\n\n let absoluteLineTop = fileTop + (checkpoint?.top ?? headerRegion);\n let currentLine = checkpoint?.lineIndex ?? 0;\n let firstVisibleHunk: number | undefined;\n let centerHunk: number | undefined;\n let overflowCounter: number | undefined;\n\n iterateOverFile({\n lines,\n startingLine: checkpoint?.lineIndex ?? 0,\n callback: ({ lineIndex }) => {\n const isAtHunkBoundary = currentLine % hunkLineCount === 0;\n const currentHunk = Math.floor(currentLine / hunkLineCount);\n\n if (isAtHunkBoundary) {\n hunkOffsets[currentHunk] = 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\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 - paddingBottom,\n };\n }\n\n // Calculate balanced startingLine centered around the viewport center\n centerHunk ??= firstVisibleHunk;\n const idealStartHunk = Math.round(centerHunk - totalHunks / 2);\n\n // Clamp startHunk: at the beginning, reduce totalLines; at the end, shift\n // startHunk back\n const maxStartHunk = Math.max(\n 0,\n Math.ceil(lineCount / hunkLineCount) - totalHunks\n );\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 -\n headerRegion -\n hunkOffsets[finalHunkIndex] -\n paddingBottom\n : fileHeight - (absoluteLineTop - fileTop) - paddingBottom;\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n}\n\nfunction getLastVisibleLineIndex(lines: string[]): number {\n const lastLine = lines.at(-1);\n if (\n lastLine == null ||\n lastLine === '' ||\n lastLine === '\\n' ||\n lastLine === '\\r\\n' ||\n lastLine === '\\r'\n ) {\n return lines.length - 2;\n }\n\n return lines.length - 1;\n}\n"],"mappings":"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"}
|