@pierre/diffs 1.2.6 → 1.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/components/CodeView.d.ts +0 -1
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +0 -23
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +7 -2
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +36 -2
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +8 -2
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +73 -1
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  14. package/dist/components/UnresolvedFile.js +2 -2
  15. package/dist/components/VirtualizedFile.d.ts +2 -1
  16. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  17. package/dist/components/VirtualizedFile.js +48 -48
  18. package/dist/components/VirtualizedFile.js.map +1 -1
  19. package/dist/components/VirtualizedFileDiff.js +42 -22
  20. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  21. package/dist/components/Virtualizer.d.ts +1 -1
  22. package/dist/components/Virtualizer.d.ts.map +1 -1
  23. package/dist/components/Virtualizer.js +3 -5
  24. package/dist/components/Virtualizer.js.map +1 -1
  25. package/dist/constants.d.ts.map +1 -1
  26. package/dist/editor/command.d.ts +6 -0
  27. package/dist/editor/command.d.ts.map +1 -0
  28. package/dist/editor/command.js +31 -0
  29. package/dist/editor/command.js.map +1 -0
  30. package/dist/editor/css.d.ts +6 -0
  31. package/dist/editor/css.d.ts.map +1 -0
  32. package/dist/editor/css.js +218 -0
  33. package/dist/editor/css.js.map +1 -0
  34. package/dist/editor/editStack.d.ts +66 -0
  35. package/dist/editor/editStack.d.ts.map +1 -0
  36. package/dist/editor/editStack.js +218 -0
  37. package/dist/editor/editStack.js.map +1 -0
  38. package/dist/editor/editor.d.ts +22 -0
  39. package/dist/editor/editor.d.ts.map +1 -0
  40. package/dist/editor/editor.js +1323 -0
  41. package/dist/editor/editor.js.map +1 -0
  42. package/dist/editor/index.d.ts +3 -0
  43. package/dist/editor/index.js +4 -0
  44. package/dist/editor/lineAnnotations.d.ts +8 -0
  45. package/dist/editor/lineAnnotations.d.ts.map +1 -0
  46. package/dist/editor/lineAnnotations.js +32 -0
  47. package/dist/editor/lineAnnotations.js.map +1 -0
  48. package/dist/editor/pieceTable.d.ts +33 -0
  49. package/dist/editor/pieceTable.d.ts.map +1 -0
  50. package/dist/editor/pieceTable.js +590 -0
  51. package/dist/editor/pieceTable.js.map +1 -0
  52. package/dist/editor/platform.d.ts +12 -0
  53. package/dist/editor/platform.d.ts.map +1 -0
  54. package/dist/editor/platform.js +44 -0
  55. package/dist/editor/platform.js.map +1 -0
  56. package/dist/editor/quickEdit.d.ts +29 -0
  57. package/dist/editor/quickEdit.d.ts.map +1 -0
  58. package/dist/editor/quickEdit.js +81 -0
  59. package/dist/editor/quickEdit.js.map +1 -0
  60. package/dist/editor/searchPanel.d.ts +30 -0
  61. package/dist/editor/searchPanel.d.ts.map +1 -0
  62. package/dist/editor/searchPanel.js +219 -0
  63. package/dist/editor/searchPanel.js.map +1 -0
  64. package/dist/editor/selection.d.ts +126 -0
  65. package/dist/editor/selection.d.ts.map +1 -0
  66. package/dist/editor/selection.js +900 -0
  67. package/dist/editor/selection.js.map +1 -0
  68. package/dist/editor/textDocument.d.ts +139 -0
  69. package/dist/editor/textDocument.d.ts.map +1 -0
  70. package/dist/editor/textDocument.js +202 -0
  71. package/dist/editor/textDocument.js.map +1 -0
  72. package/dist/editor/textMeasure.d.ts +32 -0
  73. package/dist/editor/textMeasure.d.ts.map +1 -0
  74. package/dist/editor/textMeasure.js +108 -0
  75. package/dist/editor/textMeasure.js.map +1 -0
  76. package/dist/editor/tokenzier.d.ts +37 -0
  77. package/dist/editor/tokenzier.d.ts.map +1 -0
  78. package/dist/editor/tokenzier.js +348 -0
  79. package/dist/editor/tokenzier.js.map +1 -0
  80. package/dist/editor/utils.d.ts +16 -0
  81. package/dist/editor/utils.d.ts.map +1 -0
  82. package/dist/editor/utils.js +37 -0
  83. package/dist/editor/utils.js.map +1 -0
  84. package/dist/index.d.ts +2 -2
  85. package/dist/index.js +2 -2
  86. package/dist/react/EditorContext.d.ts +16 -0
  87. package/dist/react/EditorContext.d.ts.map +1 -0
  88. package/dist/react/EditorContext.js +26 -0
  89. package/dist/react/EditorContext.js.map +1 -0
  90. package/dist/react/File.d.ts +2 -1
  91. package/dist/react/File.d.ts.map +1 -1
  92. package/dist/react/File.js +3 -2
  93. package/dist/react/File.js.map +1 -1
  94. package/dist/react/FileDiff.d.ts +3 -1
  95. package/dist/react/FileDiff.d.ts.map +1 -1
  96. package/dist/react/FileDiff.js +3 -2
  97. package/dist/react/FileDiff.js.map +1 -1
  98. package/dist/react/MultiFileDiff.d.ts +3 -1
  99. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  100. package/dist/react/MultiFileDiff.js +3 -2
  101. package/dist/react/MultiFileDiff.js.map +1 -1
  102. package/dist/react/PatchDiff.d.ts +3 -1
  103. package/dist/react/PatchDiff.d.ts.map +1 -1
  104. package/dist/react/PatchDiff.js +3 -2
  105. package/dist/react/PatchDiff.js.map +1 -1
  106. package/dist/react/index.d.ts +3 -2
  107. package/dist/react/index.js +2 -1
  108. package/dist/react/jsx.d.ts +0 -1
  109. package/dist/react/jsx.d.ts.map +1 -1
  110. package/dist/react/types.d.ts +1 -0
  111. package/dist/react/types.d.ts.map +1 -1
  112. package/dist/react/types.js +0 -1
  113. package/dist/react/utils/useFileDiffInstance.d.ts +3 -1
  114. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  115. package/dist/react/utils/useFileDiffInstance.js +31 -5
  116. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  117. package/dist/react/utils/useFileInstance.d.ts +4 -1
  118. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  119. package/dist/react/utils/useFileInstance.js +30 -5
  120. package/dist/react/utils/useFileInstance.js.map +1 -1
  121. package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
  122. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  123. package/dist/renderers/DiffHunksRenderer.js +9 -5
  124. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  125. package/dist/renderers/FileRenderer.d.ts +5 -1
  126. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  127. package/dist/renderers/FileRenderer.js +108 -41
  128. package/dist/renderers/FileRenderer.js.map +1 -1
  129. package/dist/ssr/index.d.ts +2 -2
  130. package/dist/style.js +1 -1
  131. package/dist/style.js.map +1 -1
  132. package/dist/types.d.ts +45 -1
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/utils/cleanLastNewline.js +6 -1
  135. package/dist/utils/cleanLastNewline.js.map +1 -1
  136. package/dist/utils/computeEstimatedDiffHeights.js +20 -9
  137. package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
  138. package/dist/utils/computeFileOffsets.d.ts +13 -0
  139. package/dist/utils/computeFileOffsets.d.ts.map +1 -0
  140. package/dist/utils/computeFileOffsets.js +33 -0
  141. package/dist/utils/computeFileOffsets.js.map +1 -0
  142. package/dist/utils/createTransformerWithState.js +9 -0
  143. package/dist/utils/createTransformerWithState.js.map +1 -1
  144. package/dist/utils/iterateOverDiff.js +182 -147
  145. package/dist/utils/iterateOverDiff.js.map +1 -1
  146. package/dist/utils/renderDiffWithHighlighter.js +1 -1
  147. package/dist/utils/renderFileWithHighlighter.js +5 -14
  148. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  149. package/dist/utils/virtualDiffLayout.d.ts +2 -23
  150. package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
  151. package/dist/utils/virtualDiffLayout.js +1 -41
  152. package/dist/utils/virtualDiffLayout.js.map +1 -1
  153. package/dist/worker/WorkerPoolManager.js +1 -1
  154. package/dist/worker/{wasm-BaDzIkIn.js → wasm-D4DU5jgR.js} +2 -2
  155. package/dist/worker/wasm-D4DU5jgR.js.map +1 -0
  156. package/dist/worker/worker-portable.js +349 -363
  157. package/dist/worker/worker-portable.js.map +1 -1
  158. package/dist/worker/worker.js +222 -243
  159. package/dist/worker/worker.js.map +1 -1
  160. package/package.json +9 -1
  161. package/dist/utils/iterateOverFile.d.ts +0 -50
  162. package/dist/utils/iterateOverFile.d.ts.map +0 -1
  163. package/dist/utils/iterateOverFile.js +0 -49
  164. package/dist/utils/iterateOverFile.js.map +0 -1
  165. package/dist/worker/wasm-BaDzIkIn.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualizedFile.d.ts","names":["FileContents","NumericScrollLineAnchor","PendingCodeViewLayoutReset","RenderWindow","StickySpecs","ThemeTypes","VirtualFileMetrics","WorkerPoolManager","CodeView","File","FileOptions","FileRenderProps","Virtualizer","VirtualizedFile","LAnnotation","fileContainer","file","forceRender"],"sources":["../../src/components/VirtualizedFile.d.ts"],"sourcesContent":["import type { FileContents, NumericScrollLineAnchor, PendingCodeViewLayoutReset, RenderWindow, StickySpecs, ThemeTypes, 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 layoutDirty;\n private forceRenderOverride;\n private currentCollapsed;\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 setThemeType(themeType: ThemeTypes): void;\n private resetLayoutCache;\n reconcileHeights(): boolean;\n onRender: (dirty: boolean) => boolean;\n prepareCodeViewItem(file: FileContents, top: number, reset?: PendingCodeViewLayoutReset): number;\n getLinePosition(lineNumber: number): {\n top: number;\n height: number;\n } | undefined;\n getNumericScrollAnchor(localViewportTop: number): NumericScrollLineAnchor | undefined;\n getVirtualizedHeight(): number;\n getAdvancedStickySpecs(windowSpecs?: RenderWindow): 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":";;;;;;;;cAKqBa,iDAAiDJ,KAAKK;;;EAAtDD,SAAAA,IAAAA,EAAAA,MAAe;EAAuCC,GAAAA,EAAAA,MAAAA,GAAAA,SAAAA;EAYtCA,MAAAA,EAAAA,MAAAA;EAAZJ,QAAAA,KAAAA;EAAmDE,QAAAA,SAAAA;EAAuBE,QAAAA,OAAAA;EAATN,QAAAA,WAAAA;EAAiCF,QAAAA,mBAAAA;EAAoCC,QAAAA,gBAAAA;EACvID,WAAAA,CAAAA,OAAAA,EADCI,WACDJ,CADaQ,WACbR,CAAAA,GAAAA,SAAAA,EAAAA,WAAAA,EADoDM,WACpDN,GADkEE,QAClEF,CAD2EQ,WAC3ER,CAAAA,EAAAA,OAAAA,CAAAA,EADmGA,kBACnGA,EAAAA,aAAAA,CAAAA,EADuIC,iBACvID,EAAAA,kBAAAA,CAAAA,EAAAA,OAAAA;EAEYQ,UAAAA,CAAAA,OAAAA,EAFZR,kBAEYQ,EAAAA,KAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAAZJ,aAAAA,CAAAA,SAAAA,EAAAA,MAAAA,EAAAA,eAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,MAAAA;EACIL,UAAAA,CAAAA,OAAAA,EADJK,WACIL,CADQS,WACRT,CAAAA,GAAAA,SAAAA,CAAAA,EAAAA,IAAAA;EAIEL,YAAAA,CAAAA,SAAAA,EAJFK,UAIEL,CAAAA,EAAAA,IAAAA;EAAmCE,QAAAA,gBAAAA;EAKXD,gBAAAA,CAAAA,CAAAA,EAAAA,OAAAA;EAEbE,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,OAAAA,EAAAA,GAAAA,OAAAA;EAAeC,mBAAAA,CAAAA,IAAAA,EAP1BJ,YAO0BI,EAAAA,GAAAA,EAAAA,MAAAA,EAAAA,KAAAA,CAAAA,EAPSF,0BAOTE,CAAAA,EAAAA,MAAAA;EAK3CW,eAAAA,CAAAA,UAAAA,EAAAA,MAAAA,CAAAA,EAAAA;IAAeC,GAAAA,EAAAA,MAAAA;IAAMC,MAAAA,EAAAA,MAAAA;EAAyCH,CAAAA,GAAAA,SAAAA;EAAhBH,sBAAAA,CAAAA,gBAAAA,EAAAA,MAAAA,CAAAA,EAPLV,uBAOKU,GAAAA,SAAAA;EAhCWF,oBAAAA,CAAAA,CAAAA,EAAAA,MAAAA;EAAI,sBAAA,CAAA,WAAA,CAAA,EA2BjCN,YA3BiC,CAAA,EA2BlBC,WA3BkB,GAAA,SAAA;;;;;;;;;;KAgCfO,gBAAgBG"}
1
+ {"version":3,"file":"VirtualizedFile.d.ts","names":["DiffsTextDocument","FileContents","LineAnnotation","NumericScrollLineAnchor","PendingCodeViewLayoutReset","RenderWindow","StickySpecs","ThemeTypes","VirtualFileMetrics","WorkerPoolManager","CodeView","File","FileOptions","FileRenderProps","Virtualizer","VirtualizedFile","LAnnotation","fileContainer","file","forceRender"],"sources":["../../src/components/VirtualizedFile.d.ts"],"sourcesContent":["import type { DiffsTextDocument, FileContents, LineAnnotation, NumericScrollLineAnchor, PendingCodeViewLayoutReset, RenderWindow, StickySpecs, ThemeTypes, 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 layoutDirty;\n private forceRenderOverride;\n private currentCollapsed;\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 setThemeType(themeType: ThemeTypes): void;\n private resetLayoutCache;\n reconcileHeights(): boolean;\n onRender: (dirty: boolean) => boolean;\n prepareCodeViewItem(file: FileContents, top: number, reset?: PendingCodeViewLayoutReset): number;\n getLinePosition(lineNumber: number): {\n top: number;\n height: number;\n } | undefined;\n getNumericScrollAnchor(localViewportTop: number): NumericScrollLineAnchor | undefined;\n getVirtualizedHeight(): number;\n getAdvancedStickySpecs(windowSpecs?: RenderWindow): StickySpecs | undefined;\n cleanUp(recycle?: boolean): void;\n private computeApproximateSize;\n setVisibility(visible: boolean): void;\n rerender(): void;\n applyLayoutChange(textDocument: DiffsTextDocument, newLineAnnotations?: LineAnnotation<LAnnotation>[], shouldUpdateBuffer?: boolean): 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":";;;;;;;;cAKqBe,iDAAiDJ,KAAKK;;;EAAtDD,SAAAA,IAAAA,EAAAA,MAAe;EAAuCC,GAAAA,EAAAA,MAAAA,GAAAA,SAAAA;EAYtCA,MAAAA,EAAAA,MAAAA;EAAZJ,QAAAA,KAAAA;EAAmDE,QAAAA,SAAAA;EAAuBE,QAAAA,OAAAA;EAATN,QAAAA,WAAAA;EAAiCF,QAAAA,mBAAAA;EAAoCC,QAAAA,gBAAAA;EACvID,WAAAA,CAAAA,OAAAA,EADCI,WACDJ,CADaQ,WACbR,CAAAA,GAAAA,SAAAA,EAAAA,WAAAA,EADoDM,WACpDN,GADkEE,QAClEF,CAD2EQ,WAC3ER,CAAAA,EAAAA,OAAAA,CAAAA,EADmGA,kBACnGA,EAAAA,aAAAA,CAAAA,EADuIC,iBACvID,EAAAA,kBAAAA,CAAAA,EAAAA,OAAAA;EAEYQ,UAAAA,CAAAA,OAAAA,EAFZR,kBAEYQ,EAAAA,KAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAAZJ,aAAAA,CAAAA,SAAAA,EAAAA,MAAAA,EAAAA,eAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,MAAAA;EACIL,UAAAA,CAAAA,OAAAA,EADJK,WACIL,CADQS,WACRT,CAAAA,GAAAA,SAAAA,CAAAA,EAAAA,IAAAA;EAIEN,YAAAA,CAAAA,SAAAA,EAJFM,UAIEN,CAAAA,EAAAA,IAAAA;EAAmCG,QAAAA,gBAAAA;EAKXD,gBAAAA,CAAAA,CAAAA,EAAAA,OAAAA;EAEbE,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,OAAAA,EAAAA,GAAAA,OAAAA;EAAeC,mBAAAA,CAAAA,IAAAA,EAP1BL,YAO0BK,EAAAA,GAAAA,EAAAA,MAAAA,EAAAA,KAAAA,CAAAA,EAPSF,0BAOTE,CAAAA,EAAAA,MAAAA;EAKpBN,eAAAA,CAAAA,UAAAA,EAAAA,MAAAA,CAAAA,EAAAA;IAAuDgB,GAAAA,EAAAA,MAAAA;IAAfd,MAAAA,EAAAA,MAAAA;EAC/De,CAAAA,GAAAA,SAAAA;EAAeC,sBAAAA,CAAAA,gBAAAA,EAAAA,MAAAA,CAAAA,EAR0Bf,uBAQ1Be,GAAAA,SAAAA;EAAMC,oBAAAA,CAAAA,CAAAA,EAAAA,MAAAA;EAAyCH,sBAAAA,CAAAA,WAAAA,CAAAA,EANlCX,YAMkCW,CAAAA,EANnBV,WAMmBU,GAAAA,SAAAA;EAAhBH,OAAAA,CAAAA,OAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAjCWF,QAAAA,sBAAAA;EAAI,aAAA,CAAA,OAAA,EAAA,OAAA,CAAA,EAAA,IAAA;;kCAgCtCX,wCAAwCE,eAAec;;;;;;KAChCH,gBAAgBG"}
@@ -1,8 +1,8 @@
1
1
  import { DEFAULT_VIRTUAL_FILE_METRICS } from "../constants.js";
2
2
  import { areObjectsEqual } from "../utils/areObjectsEqual.js";
3
3
  import { areOptionsEqual } from "../utils/areOptionsEqual.js";
4
+ import { areFilesEqual } from "../utils/areFilesEqual.js";
4
5
  import { getVirtualFileHeaderRegion, getVirtualFilePaddingBottom } from "../utils/computeVirtualFileMetrics.js";
5
- import { iterateOverFile } from "../utils/iterateOverFile.js";
6
6
  import { File } from "./File.js";
7
7
 
8
8
  //#region src/components/VirtualizedFile.ts
@@ -121,7 +121,7 @@ var VirtualizedFile = class extends File {
121
121
  getLinePosition(lineNumber) {
122
122
  if (this.file == null) return;
123
123
  const { disableFileHeader = false, collapsed = false } = this.options;
124
- const lastLineIndex = getLastVisibleLineIndex(this.getOrCreateLineCache(this.file));
124
+ const lastLineIndex = this.fileRenderer.getLineCount(this.file) - 1;
125
125
  let top = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
126
126
  if (collapsed || lastLineIndex < 0) return {
127
127
  top,
@@ -146,7 +146,7 @@ var VirtualizedFile = class extends File {
146
146
  if (this.file == null || this.renderRange == null) return;
147
147
  const { disableFileHeader = false, collapsed = false, overflow = "scroll" } = this.options;
148
148
  if (collapsed || this.renderRange.totalLines <= 0) return;
149
- const lastLineIndex = getLastVisibleLineIndex(this.getOrCreateLineCache(this.file));
149
+ const lastLineIndex = this.fileRenderer.getLineCount(this.file) - 1;
150
150
  if (lastLineIndex < 0) return;
151
151
  const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
152
152
  const firstRenderedLineIndex = Math.min(this.renderRange.startingLine, lastLineIndex);
@@ -183,13 +183,8 @@ var VirtualizedFile = class extends File {
183
183
  const renderRange = windowSpecs != null ? this.computeRenderRangeFromWindow(this.file, this.top, windowSpecs) : this.renderRange;
184
184
  if (renderRange == null) return;
185
185
  const { bufferBefore, bufferAfter, totalLines } = renderRange;
186
- let headerOnlyOffset = 0;
187
- if (totalLines === 0) {
188
- const activeWindow = windowSpecs ?? this.virtualizer.getWindowSpecs();
189
- if (this.top < activeWindow.top) headerOnlyOffset = bufferAfter;
190
- }
191
186
  return {
192
- topOffset: this.top + bufferBefore + headerOnlyOffset,
187
+ topOffset: this.top + bufferBefore + (totalLines === 0 ? bufferAfter : 0),
193
188
  height: this.height - (bufferBefore + bufferAfter)
194
189
  };
195
190
  }
@@ -211,7 +206,7 @@ var VirtualizedFile = class extends File {
211
206
  }
212
207
  const { disableFileHeader = false, collapsed = false, overflow = "scroll" } = this.options;
213
208
  const { lineHeight } = this.metrics;
214
- const lines = this.getOrCreateLineCache(this.file);
209
+ const lineCount = this.fileRenderer.getLineCount(this.file);
215
210
  const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
216
211
  const paddingBottom = getVirtualFilePaddingBottom(this.metrics);
217
212
  this.height += headerRegion;
@@ -219,15 +214,12 @@ var VirtualizedFile = class extends File {
219
214
  this.layoutDirty = false;
220
215
  return;
221
216
  }
222
- if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
223
- else iterateOverFile({
224
- lines,
225
- callback: ({ lineIndex }) => {
226
- this.addLayoutCheckpoint(lineIndex, this.height);
227
- this.height += this.getLineHeight(lineIndex, false);
228
- }
229
- });
230
- if (lines.length > 0) this.height += paddingBottom;
217
+ if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += lineCount * lineHeight;
218
+ else for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
219
+ this.addLayoutCheckpoint(lineIndex, this.height);
220
+ this.height += this.getLineHeight(lineIndex, false);
221
+ }
222
+ if (lineCount > 0) this.height += paddingBottom;
231
223
  if (this.fileContainer != null && shouldValidateSize && !isFirstCompute) {
232
224
  const rect = this.fileContainer.getBoundingClientRect();
233
225
  if (rect.height !== this.height) console.log("VirtualizedFile.computeApproximateSize: computed height doesnt match", {
@@ -254,10 +246,22 @@ var VirtualizedFile = class extends File {
254
246
  this.forceRenderOverride = true;
255
247
  this.virtualizer.instanceChanged(this, false);
256
248
  }
249
+ applyLayoutChange(textDocument, newLineAnnotations, shouldUpdateBuffer = false) {
250
+ const previousRenderRange = this.renderRange;
251
+ super.applyLayoutChange(textDocument, newLineAnnotations);
252
+ this.getSimpleVirtualizer()?.markDOMDirty();
253
+ this.resetLayoutCache(true);
254
+ if (shouldUpdateBuffer && previousRenderRange !== void 0 && this.file !== void 0) {
255
+ const windowSpecs = this.virtualizer.getWindowSpecs();
256
+ const renderRange = this.computeRenderRangeFromWindow(this.file, this.top ?? 0, windowSpecs);
257
+ if (renderRange.bufferAfter !== previousRenderRange.bufferAfter) this.updateBuffers(renderRange);
258
+ }
259
+ }
257
260
  render({ fileContainer, file, forceRender = false,...props }) {
261
+ const didFileChange = this.file == null || !areFilesEqual(this.file, file);
258
262
  const { forceRenderOverride, isSetup } = this;
259
263
  this.forceRenderOverride = void 0;
260
- this.file ??= file;
264
+ this.file = file;
261
265
  fileContainer = this.getOrCreateFileContainerNode(fileContainer);
262
266
  if (this.file == null) {
263
267
  console.error("VirtualizedFile.render: attempting to virtually render when we dont have file");
@@ -274,7 +278,13 @@ var VirtualizedFile = class extends File {
274
278
  this.isVisible = virtualizer.isInstanceVisible(this.top ?? 0, this.height);
275
279
  }
276
280
  this.isSetup = true;
277
- } else this.top ??= this.getVirtualizedTop();
281
+ } else {
282
+ this.top ??= this.getVirtualizedTop();
283
+ if (didFileChange && this.isSimpleMode()) {
284
+ this.getSimpleVirtualizer()?.markDOMDirty();
285
+ this.resetLayoutCache(true);
286
+ }
287
+ }
278
288
  if (!this.isVisible && this.isSimpleMode()) return this.renderPlaceholder(this.height);
279
289
  const windowSpecs = this.virtualizer.getWindowSpecs();
280
290
  const fileTop = this.top ?? 0;
@@ -355,8 +365,7 @@ var VirtualizedFile = class extends File {
355
365
  computeRenderRangeFromWindow(file, fileTop, { top, bottom }) {
356
366
  const { disableFileHeader = false, overflow = "scroll" } = this.options;
357
367
  const { hunkLineCount, lineHeight } = this.metrics;
358
- const lines = this.getOrCreateLineCache(file);
359
- const lineCount = lines.length;
368
+ const lineCount = this.fileRenderer.getLineCount(file);
360
369
  const fileHeight = this.height;
361
370
  const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
362
371
  const paddingBottom = lineCount > 0 ? getVirtualFilePaddingBottom(this.metrics) : 0;
@@ -399,28 +408,24 @@ var VirtualizedFile = class extends File {
399
408
  let firstVisibleHunk;
400
409
  let centerHunk;
401
410
  let overflowCounter;
402
- iterateOverFile({
403
- lines,
404
- startingLine: checkpoint?.lineIndex ?? 0,
405
- callback: ({ lineIndex }) => {
406
- const isAtHunkBoundary = currentLine % hunkLineCount === 0;
407
- const currentHunk = Math.floor(currentLine / hunkLineCount);
408
- if (isAtHunkBoundary) {
409
- hunkOffsets[currentHunk] = absoluteLineTop - (fileTop + headerRegion);
410
- if (overflowCounter != null) {
411
- if (overflowCounter <= 0) return true;
412
- overflowCounter--;
413
- }
411
+ const startingLineIndex = checkpoint?.lineIndex ?? 0;
412
+ for (let lineIndex = startingLineIndex; lineIndex < lineCount; lineIndex++) {
413
+ const isAtHunkBoundary = currentLine % hunkLineCount === 0;
414
+ const currentHunk = Math.floor(currentLine / hunkLineCount);
415
+ if (isAtHunkBoundary) {
416
+ hunkOffsets[currentHunk] = absoluteLineTop - (fileTop + headerRegion);
417
+ if (overflowCounter != null) {
418
+ if (overflowCounter <= 0) break;
419
+ overflowCounter--;
414
420
  }
415
- const lineHeight$1 = this.getLineHeight(lineIndex, false);
416
- if (absoluteLineTop > top - lineHeight$1 && absoluteLineTop < bottom) firstVisibleHunk ??= currentHunk;
417
- if (absoluteLineTop + lineHeight$1 > viewportCenter) centerHunk ??= currentHunk;
418
- if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
419
- currentLine++;
420
- absoluteLineTop += lineHeight$1;
421
- return false;
422
421
  }
423
- });
422
+ const lineHeight$1 = this.getLineHeight(lineIndex, false);
423
+ if (absoluteLineTop > top - lineHeight$1 && absoluteLineTop < bottom) firstVisibleHunk ??= currentHunk;
424
+ if (absoluteLineTop + lineHeight$1 > viewportCenter) centerHunk ??= currentHunk;
425
+ if (overflowCounter == null && absoluteLineTop >= bottom && isAtHunkBoundary) overflowCounter = overflowHunks;
426
+ currentLine++;
427
+ absoluteLineTop += lineHeight$1;
428
+ }
424
429
  if (firstVisibleHunk == null) return {
425
430
  startingLine: 0,
426
431
  totalLines: 0,
@@ -443,11 +448,6 @@ var VirtualizedFile = class extends File {
443
448
  };
444
449
  }
445
450
  };
446
- function getLastVisibleLineIndex(lines) {
447
- const lastLine = lines.at(-1);
448
- if (lastLine == null || lastLine === "" || lastLine === "\n" || lastLine === "\r\n" || lastLine === "\r") return lines.length - 2;
449
- return lines.length - 1;
450
- }
451
451
 
452
452
  //#endregion
453
453
  export { VirtualizedFile };
@@ -1 +1 @@
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 PendingCodeViewLayoutReset,\n RenderRange,\n RenderWindow,\n StickySpecs,\n ThemeTypes,\n VirtualFileMetrics,\n} from '../types';\nimport { areObjectsEqual } from '../utils/areObjectsEqual';\nimport { areOptionsEqual } from '../utils/areOptionsEqual';\nimport {\n getVirtualFileHeaderRegion,\n getVirtualFilePaddingBottom,\n} from '../utils/computeVirtualFileMetrics';\nimport { iterateOverFile } from '../utils/iterateOverFile';\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\nfunction hasFileLayoutOptionChanged<LAnnotation>(\n previousOptions: FileOptions<LAnnotation>,\n nextOptions: FileOptions<LAnnotation>\n): boolean {\n return (\n (previousOptions.overflow ?? 'scroll') !==\n (nextOptions.overflow ?? 'scroll') ||\n (previousOptions.collapsed ?? false) !== (nextOptions.collapsed ?? false) ||\n (previousOptions.disableLineNumbers ?? false) !==\n (nextOptions.disableLineNumbers ?? false) ||\n (previousOptions.disableFileHeader ?? false) !==\n (nextOptions.disableFileHeader ?? false) ||\n previousOptions.unsafeCSS !== nextOptions.unsafeCSS\n );\n}\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 layoutDirty = true;\n private forceRenderOverride: true | undefined;\n private currentCollapsed: boolean | 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.resetLayoutCache();\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(options: FileOptions<LAnnotation> | undefined): void {\n if (this.isAdvancedMode()) {\n throw new Error(\n 'VirtualizedFile.setOptions cannot be used inside CodeView. Update CodeView options instead.'\n );\n }\n\n if (options == null) return;\n const { options: previousOptions } = this;\n const optionsChanged = !areOptionsEqual(previousOptions, options);\n const layoutChanged = hasFileLayoutOptionChanged(previousOptions, options);\n\n super.setOptions(options);\n\n if (layoutChanged) {\n this.resetLayoutCache(true);\n }\n // Any option can affect rendered DOM; only layout-affecting options clear\n // the measured height cache above.\n if (optionsChanged) {\n this.forceRenderOverride = true;\n }\n if (optionsChanged) {\n this.virtualizer.instanceChanged(this, layoutChanged);\n }\n }\n\n override setThemeType(themeType: ThemeTypes): void {\n if (this.isAdvancedMode()) {\n throw new Error(\n 'VirtualizedFile.setThemeType cannot be used inside CodeView. Update CodeView options instead.'\n );\n }\n\n super.setThemeType(themeType);\n }\n\n private resetLayoutCache(recompute = false): void {\n this.layoutDirty = true;\n if (this.cache.heights.size > 0) {\n this.cache.heights.clear();\n }\n if (this.cache.checkpoints.length > 0) {\n this.cache.checkpoints.length = 0;\n }\n if (this.renderRange != null) {\n this.renderRange = undefined;\n }\n // NOTE(amadeus): In CodeView we intentionally batch computes to all happen\n // at the same time, so we shouldn't trigger this there.\n if (recompute && this.isSimpleMode()) {\n this.computeApproximateSize();\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(true);\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 // Prepares this item for CodeView layout by binding the latest file, syncing\n // its virtualized top, and returning an approximate height. This method is\n // called while downstream items are being re-positioned, so later changes\n // should keep clean instances on a cached-height fast path.\n public prepareCodeViewItem(\n file: FileContents,\n top: number,\n reset?: PendingCodeViewLayoutReset\n ): number {\n let shouldResetLayoutCache = reset?.resetFileLayoutCache === true;\n if (reset?.metrics != null) {\n this.metrics = reset.metrics;\n shouldResetLayoutCache = true;\n }\n\n const { collapsed = false } = this.options;\n if (this.currentCollapsed !== collapsed) {\n this.currentCollapsed = collapsed;\n shouldResetLayoutCache = true;\n }\n\n if (shouldResetLayoutCache) {\n this.resetLayoutCache();\n }\n\n if (this.file !== file) {\n this.layoutDirty = true;\n }\n this.file = file;\n this.top = top;\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(\n windowSpecs?: RenderWindow\n ): StickySpecs | undefined {\n if (this.top == null || this.file == null) {\n return undefined;\n }\n if (this.options.collapsed === true) {\n return { topOffset: this.top, height: this.height };\n }\n const renderRange =\n windowSpecs != null\n ? this.computeRenderRangeFromWindow(this.file, this.top, windowSpecs)\n : this.renderRange;\n if (renderRange == null) {\n return undefined;\n }\n const { bufferBefore, bufferAfter, totalLines } = renderRange;\n // Rendered items flow contiguously in the sticky container with no buffer\n // spacers, so a header-only item (totalLines === 0, none of its rows fall\n // inside the window) must report where its header actually sits in that\n // flow, which depends on which side of the window its content is on:\n // - content ABOVE the window (item starts above window.top): the header\n // sits at the item's bottom so the following item connects, so offset by\n // bufferAfter.\n // - content BELOW the window (item starts at/after window.top, e.g. a\n // trailing header peeking in at the bottom): the header renders at the\n // item's top with nothing after it, so no offset. Always adding\n // bufferAfter here made getStickyBounds over-measure the sticky\n // container for that trailing case.\n let headerOnlyOffset = 0;\n if (totalLines === 0) {\n const activeWindow = windowSpecs ?? this.virtualizer.getWindowSpecs();\n if (this.top < activeWindow.top) {\n headerOnlyOffset = bufferAfter;\n }\n }\n return {\n topOffset: this.top + bufferBefore + headerOnlyOffset,\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 if (!recycle) {\n this.layoutDirty = true;\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(force = false): void {\n const shouldValidateSize = this.isResizeDebuggingEnabled();\n if (!force && !this.layoutDirty && !shouldValidateSize) {\n return;\n }\n\n const isFirstCompute = this.height === 0;\n this.height = 0;\n this.cache.checkpoints = [];\n if (this.file == null) {\n this.layoutDirty = false;\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 this.layoutDirty = false;\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 (this.fileContainer != null && shouldValidateSize && !isFirstCompute) {\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 this.layoutDirty = false;\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.getLocalTopForInstance(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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"}
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 DiffsTextDocument,\n FileContents,\n LineAnnotation,\n NumericScrollLineAnchor,\n PendingCodeViewLayoutReset,\n RenderRange,\n RenderWindow,\n StickySpecs,\n ThemeTypes,\n VirtualFileMetrics,\n} from '../types';\nimport { areFilesEqual } from '../utils/areFilesEqual';\nimport { areObjectsEqual } from '../utils/areObjectsEqual';\nimport { areOptionsEqual } from '../utils/areOptionsEqual';\nimport {\n getVirtualFileHeaderRegion,\n getVirtualFilePaddingBottom,\n} from '../utils/computeVirtualFileMetrics';\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\nfunction hasFileLayoutOptionChanged<LAnnotation>(\n previousOptions: FileOptions<LAnnotation>,\n nextOptions: FileOptions<LAnnotation>\n): boolean {\n return (\n (previousOptions.overflow ?? 'scroll') !==\n (nextOptions.overflow ?? 'scroll') ||\n (previousOptions.collapsed ?? false) !== (nextOptions.collapsed ?? false) ||\n (previousOptions.disableLineNumbers ?? false) !==\n (nextOptions.disableLineNumbers ?? false) ||\n (previousOptions.disableFileHeader ?? false) !==\n (nextOptions.disableFileHeader ?? false) ||\n previousOptions.unsafeCSS !== nextOptions.unsafeCSS\n );\n}\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 layoutDirty = true;\n private forceRenderOverride: true | undefined;\n private currentCollapsed: boolean | 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.resetLayoutCache();\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(options: FileOptions<LAnnotation> | undefined): void {\n if (this.isAdvancedMode()) {\n throw new Error(\n 'VirtualizedFile.setOptions cannot be used inside CodeView. Update CodeView options instead.'\n );\n }\n\n if (options == null) return;\n const { options: previousOptions } = this;\n const optionsChanged = !areOptionsEqual(previousOptions, options);\n const layoutChanged = hasFileLayoutOptionChanged(previousOptions, options);\n\n super.setOptions(options);\n\n if (layoutChanged) {\n this.resetLayoutCache(true);\n }\n // Any option can affect rendered DOM; only layout-affecting options clear\n // the measured height cache above.\n if (optionsChanged) {\n this.forceRenderOverride = true;\n }\n if (optionsChanged) {\n this.virtualizer.instanceChanged(this, layoutChanged);\n }\n }\n\n override setThemeType(themeType: ThemeTypes): void {\n if (this.isAdvancedMode()) {\n throw new Error(\n 'VirtualizedFile.setThemeType cannot be used inside CodeView. Update CodeView options instead.'\n );\n }\n\n super.setThemeType(themeType);\n }\n\n private resetLayoutCache(recompute = false): void {\n this.layoutDirty = true;\n if (this.cache.heights.size > 0) {\n this.cache.heights.clear();\n }\n if (this.cache.checkpoints.length > 0) {\n this.cache.checkpoints.length = 0;\n }\n if (this.renderRange != null) {\n this.renderRange = undefined;\n }\n // NOTE(amadeus): In CodeView we intentionally batch computes to all happen\n // at the same time, so we shouldn't trigger this there.\n if (recompute && this.isSimpleMode()) {\n this.computeApproximateSize();\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(true);\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 // Prepares this item for CodeView layout by binding the latest file, syncing\n // its virtualized top, and returning an approximate height. This method is\n // called while downstream items are being re-positioned, so later changes\n // should keep clean instances on a cached-height fast path.\n public prepareCodeViewItem(\n file: FileContents,\n top: number,\n reset?: PendingCodeViewLayoutReset\n ): number {\n let shouldResetLayoutCache = reset?.resetFileLayoutCache === true;\n if (reset?.metrics != null) {\n this.metrics = reset.metrics;\n shouldResetLayoutCache = true;\n }\n\n const { collapsed = false } = this.options;\n if (this.currentCollapsed !== collapsed) {\n this.currentCollapsed = collapsed;\n shouldResetLayoutCache = true;\n }\n\n if (shouldResetLayoutCache) {\n this.resetLayoutCache();\n }\n\n if (this.file !== file) {\n this.layoutDirty = true;\n }\n this.file = file;\n this.top = top;\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 lastLineIndex = this.fileRenderer.getLineCount(this.file) - 1;\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 lastLineIndex = this.fileRenderer.getLineCount(this.file) - 1;\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(\n windowSpecs?: RenderWindow\n ): StickySpecs | undefined {\n if (this.top == null || this.file == null) {\n return undefined;\n }\n if (this.options.collapsed === true) {\n return { topOffset: this.top, height: this.height };\n }\n const renderRange =\n windowSpecs != null\n ? this.computeRenderRangeFromWindow(this.file, this.top, windowSpecs)\n : this.renderRange;\n if (renderRange == null) {\n return undefined;\n }\n const { bufferBefore, bufferAfter, totalLines } = 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 if (!recycle) {\n this.layoutDirty = true;\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(force = false): void {\n const shouldValidateSize = this.isResizeDebuggingEnabled();\n if (!force && !this.layoutDirty && !shouldValidateSize) {\n return;\n }\n\n const isFirstCompute = this.height === 0;\n this.height = 0;\n this.cache.checkpoints = [];\n if (this.file == null) {\n this.layoutDirty = false;\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 lineCount = this.fileRenderer.getLineCount(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 this.layoutDirty = false;\n return;\n }\n\n if (overflow === 'scroll' && this.lineAnnotations.length === 0) {\n this.height += lineCount * lineHeight;\n } else {\n for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {\n this.addLayoutCheckpoint(lineIndex, this.height);\n this.height += this.getLineHeight(lineIndex, false);\n }\n }\n\n if (lineCount > 0) {\n this.height += paddingBottom;\n }\n\n if (this.fileContainer != null && shouldValidateSize && !isFirstCompute) {\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 this.layoutDirty = false;\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 applyLayoutChange(\n textDocument: DiffsTextDocument,\n newLineAnnotations?: LineAnnotation<LAnnotation>[],\n shouldUpdateBuffer = false\n ): void {\n const previousRenderRange = this.renderRange;\n super.applyLayoutChange(textDocument, newLineAnnotations);\n this.getSimpleVirtualizer()?.markDOMDirty();\n this.resetLayoutCache(true);\n // Update the buffers caused by the line-count change to ensure the editor\n // scrolls to the correct position before re-rendering\n if (\n shouldUpdateBuffer &&\n previousRenderRange !== undefined &&\n this.file !== undefined\n ) {\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const renderRange = this.computeRenderRangeFromWindow(\n this.file,\n this.top ?? 0,\n windowSpecs\n );\n if (renderRange.bufferAfter !== previousRenderRange.bufferAfter) {\n this.updateBuffers(renderRange);\n }\n }\n }\n\n override render({\n fileContainer,\n file,\n forceRender = false,\n ...props\n }: FileRenderProps<LAnnotation>): boolean {\n const didFileChange = this.file == null || !areFilesEqual(this.file, file);\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 if (didFileChange && this.isSimpleMode()) {\n this.getSimpleVirtualizer()?.markDOMDirty();\n this.resetLayoutCache(true);\n }\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.getLocalTopForInstance(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 lineCount = this.fileRenderer.getLineCount(file);\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 const startingLineIndex = checkpoint?.lineIndex ?? 0;\n for (\n let lineIndex = startingLineIndex;\n lineIndex < lineCount;\n lineIndex++\n ) {\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 break;\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\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"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { areObjectsEqual } from "../utils/areObjectsEqual.js";
3
3
  import { areOptionsEqual } from "../utils/areOptionsEqual.js";
4
4
  import { computeVirtualFileMetrics, getVirtualFileHeaderRegion, getVirtualFilePaddingBottom } from "../utils/computeVirtualFileMetrics.js";
5
5
  import { areDiffTargetsEqual } from "../utils/areDiffTargetsEqual.js";
6
- import { getExpandedRegion, getLeadingHunkSeparatorLayout, getTrailingExpandedRegion, getTrailingHunkSeparatorLayout } from "../utils/virtualDiffLayout.js";
6
+ import { getExpandedRegion, getLeadingHunkSeparatorLayout, getTrailingHunkSeparatorLayout } from "../utils/virtualDiffLayout.js";
7
7
  import { computeEstimatedDiffHeights } from "../utils/computeEstimatedDiffHeights.js";
8
8
  import { iterateOverDiff } from "../utils/iterateOverDiff.js";
9
9
  import { parseDiffFromFile } from "../utils/parseDiffFromFile.js";
@@ -286,13 +286,8 @@ var VirtualizedFileDiff = class extends FileDiff {
286
286
  const renderRange = windowSpecs != null ? this.computeRenderRangeFromWindow(this.fileDiff, this.top, windowSpecs) : this.renderRange;
287
287
  if (renderRange == null) return;
288
288
  const { bufferBefore, bufferAfter, totalLines } = renderRange;
289
- let headerOnlyOffset = 0;
290
- if (totalLines === 0) {
291
- const activeWindow = windowSpecs ?? this.virtualizer.getWindowSpecs();
292
- if (this.top < activeWindow.top) headerOnlyOffset = bufferAfter;
293
- }
294
289
  return {
295
- topOffset: this.top + bufferBefore + headerOnlyOffset,
290
+ topOffset: this.top + bufferBefore + (totalLines === 0 ? bufferAfter : 0),
296
291
  height: this.height - (bufferBefore + bufferAfter)
297
292
  };
298
293
  }
@@ -304,9 +299,8 @@ var VirtualizedFileDiff = class extends FileDiff {
304
299
  }
305
300
  expandHunk = (hunkIndex, direction, expansionLineCountOverride) => {
306
301
  this.hunksRenderer.expandHunk(hunkIndex, direction, expansionLineCountOverride);
307
- this.forceRenderOverride = true;
308
302
  this.resetLayoutCache({ includeEstimatedHeights: true });
309
- if (this.isSimpleMode()) this.computeApproximateSize();
303
+ this.computeApproximateSize();
310
304
  this.virtualizer.instanceChanged(this, true);
311
305
  };
312
306
  setVisibility(visible) {
@@ -449,7 +443,6 @@ var VirtualizedFileDiff = class extends FileDiff {
449
443
  approximateLayoutCheckpoints() {
450
444
  if (this.cache.checkpoints.length > 0 || this.fileDiff == null || this.fileDiff.hunks.length === 0 || this.options.collapsed === true) return;
451
445
  const { disableFileHeader = false, expandUnchanged = false, collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } = this.options;
452
- const finalHunkIndex = this.fileDiff.hunks.length - 1;
453
446
  const diffStyle = this.getDiffStyle();
454
447
  const hunkSeparators = this.getHunkSeparatorType();
455
448
  const expandedHunks = expandUnchanged ? true : this.hunksRenderer.getExpandedHunksMap();
@@ -501,13 +494,13 @@ var VirtualizedFileDiff = class extends FileDiff {
501
494
  preSeparatorHeight: pendingLeadingSeparatorHeight
502
495
  });
503
496
  if (leadingRegion.fromEnd > 0) pendingLeadingSeparatorHeight = 0;
504
- const trailingRegion = hunkIndex === finalHunkIndex ? getTrailingExpandedRegion({
497
+ const trailingRegion = getTrailingExpandedRegion({
505
498
  fileDiff: this.fileDiff,
499
+ hunk,
506
500
  hunkIndex,
507
501
  expandedHunks,
508
- collapsedContextThreshold,
509
- errorPrefix: "VirtualizedFileDiff"
510
- }) : void 0;
502
+ collapsedContextThreshold
503
+ });
511
504
  const trailingSeparatorHeight = trailingRegion != null && trailingRegion.collapsedLines > 0 ? getTrailingHunkSeparatorLayout({
512
505
  type: hunkSeparators,
513
506
  metrics: this.metrics
@@ -591,14 +584,23 @@ var VirtualizedFileDiff = class extends FileDiff {
591
584
  });
592
585
  if (collapsedBefore > 0) count += renderAll ? collapsedBefore : fromStart + fromEnd;
593
586
  }
594
- const trailingRegion = getTrailingExpandedRegion({
595
- fileDiff,
596
- hunkIndex: fileDiff.hunks.length - 1,
597
- expandedHunks,
598
- collapsedContextThreshold,
599
- errorPrefix: "VirtualizedFileDiff"
600
- });
601
- if (trailingRegion != null) count += trailingRegion.fromStart + trailingRegion.fromEnd;
587
+ const lastHunk = fileDiff.hunks.at(-1);
588
+ if (lastHunk != null && hasFinalHunk(fileDiff)) {
589
+ const additionRemaining = fileDiff.additionLines.length - (lastHunk.additionLineIndex + lastHunk.additionCount);
590
+ const deletionRemaining = fileDiff.deletionLines.length - (lastHunk.deletionLineIndex + lastHunk.deletionCount);
591
+ if (lastHunk != null && additionRemaining !== deletionRemaining) throw new Error(`VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`);
592
+ const trailingRangeSize = Math.min(additionRemaining, deletionRemaining);
593
+ if (lastHunk != null && trailingRangeSize > 0) {
594
+ const { fromStart, renderAll } = getExpandedRegion({
595
+ isPartial: fileDiff.isPartial,
596
+ rangeSize: trailingRangeSize,
597
+ expandedHunks,
598
+ hunkIndex: fileDiff.hunks.length,
599
+ collapsedContextThreshold
600
+ });
601
+ count += renderAll ? trailingRangeSize : fromStart;
602
+ }
603
+ }
602
604
  return count;
603
605
  }
604
606
  computeRenderRangeFromWindow(fileDiff, fileTop, { top, bottom }) {
@@ -751,6 +753,19 @@ function getHunkMetadataOffsets({ diffStyle, hunk, rowCount }) {
751
753
  if (lastContent.additions > 0 && hunk.noEOFCRAdditions) offsets.push(rowCount - 1);
752
754
  return offsets;
753
755
  }
756
+ function getTrailingExpandedRegion({ fileDiff, hunk, hunkIndex, expandedHunks, collapsedContextThreshold }) {
757
+ if (hunkIndex !== fileDiff.hunks.length - 1 || !hasFinalHunk(fileDiff)) return;
758
+ const additionRemaining = fileDiff.additionLines.length - (hunk.additionLineIndex + hunk.additionCount);
759
+ const deletionRemaining = fileDiff.deletionLines.length - (hunk.deletionLineIndex + hunk.deletionCount);
760
+ if (additionRemaining !== deletionRemaining) throw new Error(`VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`);
761
+ return getExpandedRegion({
762
+ isPartial: fileDiff.isPartial,
763
+ rangeSize: Math.min(additionRemaining, deletionRemaining),
764
+ expandedHunks,
765
+ hunkIndex: fileDiff.hunks.length,
766
+ collapsedContextThreshold
767
+ });
768
+ }
754
769
  function hasDiffLayoutOptionChanged(previousOptions, nextOptions) {
755
770
  return (previousOptions.diffStyle ?? "split") !== (nextOptions.diffStyle ?? "split") || (previousOptions.overflow ?? "scroll") !== (nextOptions.overflow ?? "scroll") || (previousOptions.collapsed ?? false) !== (nextOptions.collapsed ?? false) || (previousOptions.disableLineNumbers ?? false) !== (nextOptions.disableLineNumbers ?? false) || (previousOptions.disableFileHeader ?? false) !== (nextOptions.disableFileHeader ?? false) || (previousOptions.diffIndicators ?? "bars") !== (nextOptions.diffIndicators ?? "bars") || (previousOptions.hunkSeparators ?? "line-info") !== (nextOptions.hunkSeparators ?? "line-info") || (previousOptions.expandUnchanged ?? false) !== (nextOptions.expandUnchanged ?? false) || (previousOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD) !== (nextOptions.collapsedContextThreshold ?? DEFAULT_COLLAPSED_CONTEXT_THRESHOLD) || previousOptions.unsafeCSS !== nextOptions.unsafeCSS;
756
771
  }
@@ -760,6 +775,11 @@ function hasDiffEstimateOptionChanged(previousOptions, nextOptions) {
760
775
  function getOptionHunkSeparatorType(hunkSeparators) {
761
776
  return typeof hunkSeparators === "function" ? "custom" : hunkSeparators ?? "line-info";
762
777
  }
778
+ function hasFinalHunk(fileDiff) {
779
+ const lastHunk = fileDiff.hunks.at(-1);
780
+ if (lastHunk == null || fileDiff.isPartial || fileDiff.additionLines.length === 0 || fileDiff.deletionLines.length === 0) return false;
781
+ return lastHunk.additionLineIndex + lastHunk.additionCount < fileDiff.additionLines.length || lastHunk.deletionLineIndex + lastHunk.deletionCount < fileDiff.deletionLines.length;
782
+ }
763
783
  function parseLineIndex(lineIndexAttr, diffStyle) {
764
784
  const [unifiedIndex, splitIndex] = lineIndexAttr.split(",").map(Number);
765
785
  return diffStyle === "split" ? splitIndex : unifiedIndex;