@pierre/diffs 1.2.10 → 1.2.11

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 (46) hide show
  1. package/README.md +6 -6
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +6 -6
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts.map +1 -1
  6. package/dist/components/File.js +6 -1
  7. package/dist/components/File.js.map +1 -1
  8. package/dist/components/FileDiff.d.ts.map +1 -1
  9. package/dist/components/FileDiff.js +1 -1
  10. package/dist/components/FileDiff.js.map +1 -1
  11. package/dist/components/FileStream.js +4 -2
  12. package/dist/components/FileStream.js.map +1 -1
  13. package/dist/components/VirtualizedFile.d.ts +6 -2
  14. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  15. package/dist/components/VirtualizedFile.js +82 -21
  16. package/dist/components/VirtualizedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFileDiff.d.ts +7 -2
  18. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  19. package/dist/components/VirtualizedFileDiff.js +77 -15
  20. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  21. package/dist/managers/InteractionManager.js +1 -1
  22. package/dist/managers/InteractionManager.js.map +1 -1
  23. package/dist/managers/ResizeManager.js +1 -1
  24. package/dist/managers/ResizeManager.js.map +1 -1
  25. package/dist/renderers/DiffHunksRenderer.d.ts +3 -2
  26. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  27. package/dist/renderers/DiffHunksRenderer.js +49 -2
  28. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  29. package/dist/renderers/FileRenderer.js +12 -0
  30. package/dist/renderers/FileRenderer.js.map +1 -1
  31. package/dist/types.d.ts +8 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/utils/includesFileAnnotations.d.ts +17 -0
  34. package/dist/utils/includesFileAnnotations.d.ts.map +1 -0
  35. package/dist/utils/includesFileAnnotations.js +19 -0
  36. package/dist/utils/includesFileAnnotations.js.map +1 -0
  37. package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
  38. package/dist/utils/renderDiffWithHighlighter.js +4 -2
  39. package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
  40. package/dist/utils/renderFileWithHighlighter.js +4 -2
  41. package/dist/utils/renderFileWithHighlighter.js.map +1 -1
  42. package/dist/worker/worker-portable.js +8 -4
  43. package/dist/worker/worker-portable.js.map +1 -1
  44. package/dist/worker/worker.js +8 -4
  45. package/dist/worker/worker.js.map +1 -1
  46. package/package.json +2 -8
@@ -1 +1 @@
1
- {"version":3,"file":"FileStream.js","names":["options: FileStreamOptions"],"sources":["../../src/components/FileStream.ts"],"sourcesContent":["import { DEFAULT_THEMES, DIFFS_TAG_NAME } from '../constants';\nimport { getSharedHighlighter } from '../highlighter/shared_highlighter';\nimport { queueRender } from '../managers/UniversalRenderingManager';\nimport { CodeToTokenTransformStream, type RecallToken } from '../shiki-stream';\nimport type {\n AppliedThemeStyleCache,\n BaseCodeOptions,\n DiffsHighlighter,\n SupportedLanguages,\n ThemedToken,\n ThemeTypes,\n} from '../types';\nimport { createSpanFromToken } from '../utils/createSpanNodeFromToken';\nimport { wrapThemeCSS } from '../utils/cssWrappers';\nimport { formatCSSVariablePrefix } from '../utils/formatCSSVariablePrefix';\nimport { getHighlighterOptions } from '../utils/getHighlighterOptions';\nimport { getHighlighterThemeStyles } from '../utils/getHighlighterThemeStyles';\nimport { getOrCreateCodeNode } from '../utils/getOrCreateCodeNode';\nimport { upsertHostThemeStyle } from '../utils/hostTheme';\nimport { getMeasuredScrollbarGutter } from '../utils/scrollbarGutter';\nimport { setPreNodeProperties } from '../utils/setWrapperNodeProps';\n\nexport interface FileStreamOptions extends BaseCodeOptions {\n lang?: SupportedLanguages;\n startingLineIndex?: number;\n\n onPreRender?(instance: FileStream): unknown;\n onPostRender?(instance: FileStream): unknown;\n\n onStreamStart?(controller: WritableStreamDefaultController): unknown;\n onStreamWrite?(token: ThemedToken | RecallToken): unknown;\n onStreamClose?(): unknown;\n onStreamAbort?(reason: unknown): unknown;\n}\n\nlet instanceId = -1;\n\nexport class FileStream {\n readonly __id: string = `file-stream:${++instanceId}`;\n\n private highlighter: DiffsHighlighter | undefined;\n private stream: ReadableStream<string> | undefined;\n private abortController: AbortController | undefined;\n private fileContainer: HTMLElement | undefined;\n private pre: HTMLPreElement | undefined;\n private code: HTMLElement | undefined;\n private gutterElement: HTMLElement | undefined;\n private contentElement: HTMLElement | undefined;\n private themeCSSStyle: HTMLStyleElement | undefined;\n private appliedThemeCSS: AppliedThemeStyleCache | undefined;\n private currentRowCount = 0;\n\n constructor(public options: FileStreamOptions = { theme: DEFAULT_THEMES }) {\n this.currentLineIndex = this.options.startingLineIndex ?? 1;\n }\n\n cleanUp(): void {\n this.abortController?.abort();\n this.abortController = undefined;\n }\n\n setThemeType(themeType: ThemeTypes): void {\n if ((this.options.themeType ?? 'system') === themeType) {\n return;\n }\n this.options = { ...this.options, themeType };\n if (\n typeof this.options.theme === 'string' ||\n this.fileContainer == null ||\n this.appliedThemeCSS == null\n ) {\n return;\n }\n this.applyThemeState(\n this.fileContainer,\n this.appliedThemeCSS.themeStyles,\n themeType,\n this.appliedThemeCSS.baseThemeType\n );\n }\n\n private async initializeHighlighter(): Promise<DiffsHighlighter> {\n this.highlighter = await getSharedHighlighter(\n getHighlighterOptions(this.options.lang, this.options)\n );\n return this.highlighter;\n }\n\n private queuedSetupArgs: [ReadableStream<string>, HTMLElement] | undefined;\n async setup(\n _source: ReadableStream<string>,\n _wrapper: HTMLElement\n ): Promise<void> {\n const isSettingUp = this.queuedSetupArgs != null;\n this.queuedSetupArgs = [_source, _wrapper];\n if (isSettingUp) {\n // TODO(amadeus): Make it so that this function can be properly\n // awaitable, maybe?\n return;\n }\n this.highlighter ??= await this.initializeHighlighter();\n\n const [source, wrapper] = this.queuedSetupArgs;\n this.queuedSetupArgs = undefined;\n\n const stream = source;\n\n this.setupStream(stream, wrapper, this.highlighter);\n }\n\n private setupStream(\n stream: ReadableStream<string>,\n wrapper: HTMLElement,\n highlighter: DiffsHighlighter\n ): void {\n const {\n disableLineNumbers = false,\n overflow = 'scroll',\n theme = DEFAULT_THEMES,\n themeType = 'system',\n } = this.options;\n const fileContainer = this.getOrCreateFileContainer();\n if (fileContainer.parentElement == null) {\n wrapper.appendChild(fileContainer);\n }\n this.pre ??= document.createElement('pre');\n if (this.pre.parentElement == null) {\n fileContainer.shadowRoot?.appendChild(this.pre);\n }\n const baseThemeType =\n typeof theme === 'string' ? highlighter.getTheme(theme).type : undefined;\n const themeStyles = getHighlighterThemeStyles({ theme, highlighter });\n this.applyThemeState(fileContainer, themeStyles, themeType, baseThemeType);\n const pre = setPreNodeProperties(this.pre, {\n type: 'file',\n diffIndicators: 'none',\n disableBackground: true,\n disableLineNumbers,\n overflow,\n split: false,\n totalLines: 0,\n });\n pre.textContent = '';\n\n this.pre = pre;\n this.code = getOrCreateCodeNode({ code: this.code, pre });\n this.gutterElement = undefined;\n this.contentElement = undefined;\n this.currentRowCount = 0;\n this.currentLineElement = undefined;\n this.currentLineIndex = this.options.startingLineIndex ?? 1;\n this.abortController?.abort();\n this.abortController = new AbortController();\n const { onStreamStart, onStreamClose, onStreamAbort } = this.options;\n // Cancel the prior source so upstream producers stop generating tokens.\n // Swallow AbortError / locked-stream rejections since we're tearing down.\n this.stream?.cancel().catch(() => {});\n this.stream = stream;\n this.stream\n .pipeThrough(\n typeof theme === 'string'\n ? new CodeToTokenTransformStream({\n ...this.options,\n theme,\n highlighter,\n allowRecalls: true,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n })\n : new CodeToTokenTransformStream({\n ...this.options,\n themes: theme,\n highlighter,\n allowRecalls: true,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n })\n )\n .pipeTo(\n new WritableStream({\n start(controller) {\n onStreamStart?.(controller);\n },\n close() {\n onStreamClose?.();\n },\n abort(reason) {\n onStreamAbort?.(reason);\n },\n write: this.handleWrite,\n }),\n { signal: this.abortController.signal }\n )\n .catch((error) => {\n // Ignore AbortError - it's expected when cleaning up\n if (error.name !== 'AbortError') {\n console.error('FileStream pipe error:', error);\n }\n });\n }\n\n private queuedTokens: (ThemedToken | RecallToken)[] = [];\n private handleWrite = (token: ThemedToken | RecallToken) => {\n // If we've recalled tokens we haven't rendered yet, we can just yeet them\n // and never apply them\n if ('recall' in token && this.queuedTokens.length >= token.recall) {\n this.queuedTokens.length = this.queuedTokens.length - token.recall;\n } else {\n this.queuedTokens.push(token);\n }\n queueRender(this.render);\n this.options.onStreamWrite?.(token);\n };\n\n private currentLineIndex: number;\n private currentLineElement: HTMLElement | undefined;\n private render = () => {\n this.options.onPreRender?.(this);\n const { gutter, content } = this.getOrCreateStreamColumns();\n const gutterFragment = document.createDocumentFragment();\n const contentFragment = document.createDocumentFragment();\n for (const token of this.queuedTokens) {\n if ('recall' in token) {\n if (this.currentLineElement == null) {\n throw new Error(\n 'FileStream.render: no current line element, shouldnt be possible to get here'\n );\n }\n if (token.recall > this.currentLineElement.childNodes.length) {\n throw new Error(\n `FileStream.render: Token recall exceed the current line, there's probably a bug...`\n );\n }\n for (let i = 0; i < token.recall; i++) {\n this.currentLineElement.lastChild?.remove();\n }\n } else {\n const span = createSpanFromToken(token);\n if (this.currentLineElement == null) {\n const { gutterLine, contentLine } = this.createLine();\n gutterFragment.appendChild(gutterLine);\n contentFragment.appendChild(contentLine);\n }\n this.currentLineElement?.appendChild(span);\n if (token.content === '\\n') {\n this.currentLineIndex++;\n const { gutterLine, contentLine } = this.createLine();\n gutterFragment.appendChild(gutterLine);\n contentFragment.appendChild(contentLine);\n }\n }\n }\n if (gutterFragment.childNodes.length > 0) {\n gutter.appendChild(gutterFragment);\n }\n if (contentFragment.childNodes.length > 0) {\n content.appendChild(contentFragment);\n }\n this.queuedTokens.length = 0;\n this.options.onPostRender?.(this);\n };\n\n private getOrCreateStreamColumns(): {\n gutter: HTMLElement;\n content: HTMLElement;\n } {\n if (this.code == null) {\n throw new Error('FileStream: expected code element to exist');\n }\n if (this.gutterElement != null && this.contentElement != null) {\n return { gutter: this.gutterElement, content: this.contentElement };\n }\n const gutter = document.createElement('div');\n gutter.dataset.gutter = '';\n const content = document.createElement('div');\n content.dataset.content = '';\n this.code.appendChild(gutter);\n this.code.appendChild(content);\n this.gutterElement = gutter;\n this.contentElement = content;\n return { gutter, content };\n }\n\n private updateRowSpan(): void {\n if (this.gutterElement != null) {\n this.gutterElement.style.gridRow = `span ${this.currentRowCount}`;\n }\n if (this.contentElement != null) {\n this.contentElement.style.gridRow = `span ${this.currentRowCount}`;\n }\n }\n\n private createLine(): { gutterLine: HTMLElement; contentLine: HTMLElement } {\n const lineNumber = this.currentLineIndex;\n const lineIndex = `${lineNumber - 1}`;\n const gutterLine = document.createElement('div');\n gutterLine.dataset.columnNumber = `${lineNumber}`;\n gutterLine.dataset.lineType = 'context';\n gutterLine.dataset.lineIndex = lineIndex;\n\n const numberContent = document.createElement('span');\n numberContent.dataset.lineNumberContent = '';\n numberContent.textContent = `${lineNumber}`;\n gutterLine.appendChild(numberContent);\n\n const contentLine = document.createElement('div');\n contentLine.dataset.line = `${lineNumber}`;\n contentLine.dataset.lineType = 'context';\n contentLine.dataset.lineIndex = lineIndex;\n\n this.currentRowCount += 1;\n this.updateRowSpan();\n this.currentLineElement = contentLine;\n return { gutterLine, contentLine };\n }\n\n private getOrCreateFileContainer(fileContainer?: HTMLElement): HTMLElement {\n if (\n (fileContainer != null && fileContainer === this.fileContainer) ||\n (fileContainer == null && this.fileContainer != null)\n ) {\n return this.fileContainer;\n }\n if (\n this.fileContainer != null &&\n fileContainer != null &&\n fileContainer !== this.fileContainer\n ) {\n this.themeCSSStyle = undefined;\n this.appliedThemeCSS = undefined;\n }\n this.fileContainer =\n fileContainer ?? document.createElement(DIFFS_TAG_NAME);\n return this.fileContainer;\n }\n\n private applyThemeState(\n container: HTMLElement,\n themeStyles: string,\n themeType: ThemeTypes,\n baseThemeType?: 'light' | 'dark'\n ): void {\n const shadowRoot =\n container.shadowRoot ?? container.attachShadow({ mode: 'open' });\n const effectiveThemeType = baseThemeType ?? themeType;\n const currentTheme = this.options.theme ?? DEFAULT_THEMES;\n const theme =\n typeof currentTheme === 'string' ? currentTheme : { ...currentTheme };\n const scrollbarGutter = getMeasuredScrollbarGutter(shadowRoot);\n if (\n this.themeCSSStyle?.parentNode === shadowRoot &&\n this.appliedThemeCSS?.themeStyles === themeStyles &&\n this.appliedThemeCSS.themeType === effectiveThemeType &&\n this.appliedThemeCSS.scrollbarGutter === scrollbarGutter\n ) {\n this.appliedThemeCSS.theme = theme;\n return;\n }\n this.themeCSSStyle = upsertHostThemeStyle({\n shadowRoot,\n currentNode: this.themeCSSStyle,\n themeCSS: wrapThemeCSS(themeStyles, effectiveThemeType, scrollbarGutter),\n });\n this.appliedThemeCSS =\n this.themeCSSStyle != null\n ? {\n theme,\n themeStyles,\n themeType: effectiveThemeType,\n baseThemeType,\n scrollbarGutter,\n }\n : undefined;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FileStream.js","names":["options: FileStreamOptions"],"sources":["../../src/components/FileStream.ts"],"sourcesContent":["import { DEFAULT_THEMES, DIFFS_TAG_NAME } from '../constants';\nimport { getSharedHighlighter } from '../highlighter/shared_highlighter';\nimport { queueRender } from '../managers/UniversalRenderingManager';\nimport { CodeToTokenTransformStream, type RecallToken } from '../shiki-stream';\nimport type {\n AppliedThemeStyleCache,\n BaseCodeOptions,\n DiffsHighlighter,\n SupportedLanguages,\n ThemedToken,\n ThemeTypes,\n} from '../types';\nimport { createSpanFromToken } from '../utils/createSpanNodeFromToken';\nimport { wrapThemeCSS } from '../utils/cssWrappers';\nimport { formatCSSVariablePrefix } from '../utils/formatCSSVariablePrefix';\nimport { getHighlighterOptions } from '../utils/getHighlighterOptions';\nimport { getHighlighterThemeStyles } from '../utils/getHighlighterThemeStyles';\nimport { getOrCreateCodeNode } from '../utils/getOrCreateCodeNode';\nimport { upsertHostThemeStyle } from '../utils/hostTheme';\nimport { getMeasuredScrollbarGutter } from '../utils/scrollbarGutter';\nimport { setPreNodeProperties } from '../utils/setWrapperNodeProps';\n\nexport interface FileStreamOptions extends BaseCodeOptions {\n lang?: SupportedLanguages;\n startingLineIndex?: number;\n\n onPreRender?(instance: FileStream): unknown;\n onPostRender?(instance: FileStream): unknown;\n\n onStreamStart?(controller: WritableStreamDefaultController): unknown;\n onStreamWrite?(token: ThemedToken | RecallToken): unknown;\n onStreamClose?(): unknown;\n onStreamAbort?(reason: unknown): unknown;\n}\n\nlet instanceId = -1;\n\nexport class FileStream {\n readonly __id: string = `file-stream:${++instanceId}`;\n\n private highlighter: DiffsHighlighter | undefined;\n private stream: ReadableStream<string> | undefined;\n private abortController: AbortController | undefined;\n private fileContainer: HTMLElement | undefined;\n private pre: HTMLPreElement | undefined;\n private code: HTMLElement | undefined;\n private gutterElement: HTMLElement | undefined;\n private contentElement: HTMLElement | undefined;\n private themeCSSStyle: HTMLStyleElement | undefined;\n private appliedThemeCSS: AppliedThemeStyleCache | undefined;\n private currentRowCount = 0;\n\n constructor(public options: FileStreamOptions = { theme: DEFAULT_THEMES }) {\n this.currentLineIndex = this.options.startingLineIndex ?? 1;\n }\n\n cleanUp(): void {\n this.abortController?.abort();\n this.abortController = undefined;\n }\n\n setThemeType(themeType: ThemeTypes): void {\n if ((this.options.themeType ?? 'system') === themeType) {\n return;\n }\n this.options = { ...this.options, themeType };\n if (\n typeof this.options.theme === 'string' ||\n this.fileContainer == null ||\n this.appliedThemeCSS == null\n ) {\n return;\n }\n this.applyThemeState(\n this.fileContainer,\n this.appliedThemeCSS.themeStyles,\n themeType,\n this.appliedThemeCSS.baseThemeType\n );\n }\n\n private async initializeHighlighter(): Promise<DiffsHighlighter> {\n this.highlighter = await getSharedHighlighter(\n getHighlighterOptions(this.options.lang, this.options)\n );\n return this.highlighter;\n }\n\n private queuedSetupArgs: [ReadableStream<string>, HTMLElement] | undefined;\n async setup(\n _source: ReadableStream<string>,\n _wrapper: HTMLElement\n ): Promise<void> {\n const isSettingUp = this.queuedSetupArgs != null;\n this.queuedSetupArgs = [_source, _wrapper];\n if (isSettingUp) {\n // TODO(amadeus): Make it so that this function can be properly\n // awaitable, maybe?\n return;\n }\n this.highlighter ??= await this.initializeHighlighter();\n\n const [source, wrapper] = this.queuedSetupArgs;\n this.queuedSetupArgs = undefined;\n\n const stream = source;\n\n this.setupStream(stream, wrapper, this.highlighter);\n }\n\n private setupStream(\n stream: ReadableStream<string>,\n wrapper: HTMLElement,\n highlighter: DiffsHighlighter\n ): void {\n const {\n disableLineNumbers = false,\n overflow = 'scroll',\n theme = DEFAULT_THEMES,\n themeType = 'system',\n } = this.options;\n const fileContainer = this.getOrCreateFileContainer();\n if (fileContainer.parentElement == null) {\n wrapper.appendChild(fileContainer);\n }\n this.pre ??= document.createElement('pre');\n if (this.pre.parentElement == null) {\n fileContainer.shadowRoot?.appendChild(this.pre);\n }\n const baseThemeType =\n typeof theme === 'string' ? highlighter.getTheme(theme).type : undefined;\n const themeStyles = getHighlighterThemeStyles({ theme, highlighter });\n this.applyThemeState(fileContainer, themeStyles, themeType, baseThemeType);\n const pre = setPreNodeProperties(this.pre, {\n type: 'file',\n diffIndicators: 'none',\n disableBackground: true,\n disableLineNumbers,\n overflow,\n split: false,\n totalLines: 0,\n });\n pre.textContent = '';\n\n this.pre = pre;\n this.code = getOrCreateCodeNode({ code: this.code, pre });\n this.gutterElement = undefined;\n this.contentElement = undefined;\n this.currentRowCount = 0;\n this.currentLineElement = undefined;\n this.currentLineIndex = this.options.startingLineIndex ?? 1;\n this.abortController?.abort();\n this.abortController = new AbortController();\n const { onStreamStart, onStreamClose, onStreamAbort } = this.options;\n // Cancel the prior source so upstream producers stop generating tokens.\n // Swallow AbortError / locked-stream rejections since we're tearing down.\n this.stream?.cancel().catch(() => {});\n this.stream = stream;\n this.stream\n // tokenizeTimeLimit: 0 — never trade silently-wrong token colors for\n // latency; see renderFileWithHighlighter for the full rationale.\n .pipeThrough(\n typeof theme === 'string'\n ? new CodeToTokenTransformStream({\n ...this.options,\n theme,\n highlighter,\n allowRecalls: true,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n })\n : new CodeToTokenTransformStream({\n ...this.options,\n themes: theme,\n highlighter,\n allowRecalls: true,\n defaultColor: false,\n cssVariablePrefix: formatCSSVariablePrefix('token'),\n tokenizeTimeLimit: 0,\n })\n )\n .pipeTo(\n new WritableStream({\n start(controller) {\n onStreamStart?.(controller);\n },\n close() {\n onStreamClose?.();\n },\n abort(reason) {\n onStreamAbort?.(reason);\n },\n write: this.handleWrite,\n }),\n { signal: this.abortController.signal }\n )\n .catch((error) => {\n // Ignore AbortError - it's expected when cleaning up\n if (error.name !== 'AbortError') {\n console.error('FileStream pipe error:', error);\n }\n });\n }\n\n private queuedTokens: (ThemedToken | RecallToken)[] = [];\n private handleWrite = (token: ThemedToken | RecallToken) => {\n // If we've recalled tokens we haven't rendered yet, we can just yeet them\n // and never apply them\n if ('recall' in token && this.queuedTokens.length >= token.recall) {\n this.queuedTokens.length = this.queuedTokens.length - token.recall;\n } else {\n this.queuedTokens.push(token);\n }\n queueRender(this.render);\n this.options.onStreamWrite?.(token);\n };\n\n private currentLineIndex: number;\n private currentLineElement: HTMLElement | undefined;\n private render = () => {\n this.options.onPreRender?.(this);\n const { gutter, content } = this.getOrCreateStreamColumns();\n const gutterFragment = document.createDocumentFragment();\n const contentFragment = document.createDocumentFragment();\n for (const token of this.queuedTokens) {\n if ('recall' in token) {\n if (this.currentLineElement == null) {\n throw new Error(\n 'FileStream.render: no current line element, shouldnt be possible to get here'\n );\n }\n if (token.recall > this.currentLineElement.childNodes.length) {\n throw new Error(\n `FileStream.render: Token recall exceed the current line, there's probably a bug...`\n );\n }\n for (let i = 0; i < token.recall; i++) {\n this.currentLineElement.lastChild?.remove();\n }\n } else {\n const span = createSpanFromToken(token);\n if (this.currentLineElement == null) {\n const { gutterLine, contentLine } = this.createLine();\n gutterFragment.appendChild(gutterLine);\n contentFragment.appendChild(contentLine);\n }\n this.currentLineElement?.appendChild(span);\n if (token.content === '\\n') {\n this.currentLineIndex++;\n const { gutterLine, contentLine } = this.createLine();\n gutterFragment.appendChild(gutterLine);\n contentFragment.appendChild(contentLine);\n }\n }\n }\n if (gutterFragment.childNodes.length > 0) {\n gutter.appendChild(gutterFragment);\n }\n if (contentFragment.childNodes.length > 0) {\n content.appendChild(contentFragment);\n }\n this.queuedTokens.length = 0;\n this.options.onPostRender?.(this);\n };\n\n private getOrCreateStreamColumns(): {\n gutter: HTMLElement;\n content: HTMLElement;\n } {\n if (this.code == null) {\n throw new Error('FileStream: expected code element to exist');\n }\n if (this.gutterElement != null && this.contentElement != null) {\n return { gutter: this.gutterElement, content: this.contentElement };\n }\n const gutter = document.createElement('div');\n gutter.dataset.gutter = '';\n const content = document.createElement('div');\n content.dataset.content = '';\n this.code.appendChild(gutter);\n this.code.appendChild(content);\n this.gutterElement = gutter;\n this.contentElement = content;\n return { gutter, content };\n }\n\n private updateRowSpan(): void {\n if (this.gutterElement != null) {\n this.gutterElement.style.gridRow = `span ${this.currentRowCount}`;\n }\n if (this.contentElement != null) {\n this.contentElement.style.gridRow = `span ${this.currentRowCount}`;\n }\n }\n\n private createLine(): { gutterLine: HTMLElement; contentLine: HTMLElement } {\n const lineNumber = this.currentLineIndex;\n const lineIndex = `${lineNumber - 1}`;\n const gutterLine = document.createElement('div');\n gutterLine.dataset.columnNumber = `${lineNumber}`;\n gutterLine.dataset.lineType = 'context';\n gutterLine.dataset.lineIndex = lineIndex;\n\n const numberContent = document.createElement('span');\n numberContent.dataset.lineNumberContent = '';\n numberContent.textContent = `${lineNumber}`;\n gutterLine.appendChild(numberContent);\n\n const contentLine = document.createElement('div');\n contentLine.dataset.line = `${lineNumber}`;\n contentLine.dataset.lineType = 'context';\n contentLine.dataset.lineIndex = lineIndex;\n\n this.currentRowCount += 1;\n this.updateRowSpan();\n this.currentLineElement = contentLine;\n return { gutterLine, contentLine };\n }\n\n private getOrCreateFileContainer(fileContainer?: HTMLElement): HTMLElement {\n if (\n (fileContainer != null && fileContainer === this.fileContainer) ||\n (fileContainer == null && this.fileContainer != null)\n ) {\n return this.fileContainer;\n }\n if (\n this.fileContainer != null &&\n fileContainer != null &&\n fileContainer !== this.fileContainer\n ) {\n this.themeCSSStyle = undefined;\n this.appliedThemeCSS = undefined;\n }\n this.fileContainer =\n fileContainer ?? document.createElement(DIFFS_TAG_NAME);\n return this.fileContainer;\n }\n\n private applyThemeState(\n container: HTMLElement,\n themeStyles: string,\n themeType: ThemeTypes,\n baseThemeType?: 'light' | 'dark'\n ): void {\n const shadowRoot =\n container.shadowRoot ?? container.attachShadow({ mode: 'open' });\n const effectiveThemeType = baseThemeType ?? themeType;\n const currentTheme = this.options.theme ?? DEFAULT_THEMES;\n const theme =\n typeof currentTheme === 'string' ? currentTheme : { ...currentTheme };\n const scrollbarGutter = getMeasuredScrollbarGutter(shadowRoot);\n if (\n this.themeCSSStyle?.parentNode === shadowRoot &&\n this.appliedThemeCSS?.themeStyles === themeStyles &&\n this.appliedThemeCSS.themeType === effectiveThemeType &&\n this.appliedThemeCSS.scrollbarGutter === scrollbarGutter\n ) {\n this.appliedThemeCSS.theme = theme;\n return;\n }\n this.themeCSSStyle = upsertHostThemeStyle({\n shadowRoot,\n currentNode: this.themeCSSStyle,\n themeCSS: wrapThemeCSS(themeStyles, effectiveThemeType, scrollbarGutter),\n });\n this.appliedThemeCSS =\n this.themeCSSStyle != null\n ? {\n theme,\n themeStyles,\n themeType: effectiveThemeType,\n baseThemeType,\n scrollbarGutter,\n }\n : undefined;\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { FileContents, NumericScrollLineAnchor, PendingCodeViewLayoutReset, RenderWindow, StickySpecs, ThemeTypes, VirtualFileMetrics } from "../types.js";
1
+ import { FileContents, LineAnnotation, NumericScrollLineAnchor, PendingCodeViewLayoutReset, RenderWindow, StickySpecs, ThemeTypes, 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";
@@ -20,13 +20,16 @@ declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation>
20
20
  private currentCollapsed;
21
21
  constructor(options: FileOptions<LAnnotation> | undefined, virtualizer: Virtualizer | CodeView<LAnnotation>, metrics?: VirtualFileMetrics, workerManager?: WorkerPoolManager, isContainerManaged?: boolean);
22
22
  setMetrics(metrics: VirtualFileMetrics, force?: boolean): void;
23
+ setLineAnnotations(lineAnnotations: LineAnnotation<LAnnotation>[]): void;
24
+ private syncLineAnnotations;
25
+ private hasLineAnnotations;
23
26
  getLineHeight(lineIndex: number, hasMetadataLine?: boolean): number;
24
27
  setOptions(options: FileOptions<LAnnotation> | undefined): void;
25
28
  setThemeType(themeType: ThemeTypes): void;
26
29
  private resetLayoutCache;
27
30
  reconcileHeights(): boolean;
28
31
  onRender: (dirty: boolean) => boolean;
29
- prepareCodeViewItem(file: FileContents, top: number, reset?: PendingCodeViewLayoutReset): number;
32
+ prepareCodeViewItem(file: FileContents, top: number, reset?: PendingCodeViewLayoutReset, lineAnnotations?: LineAnnotation<LAnnotation>[]): number;
30
33
  getLinePosition(lineNumber: number): {
31
34
  top: number;
32
35
  height: number;
@@ -42,6 +45,7 @@ declare class VirtualizedFile<LAnnotation = undefined> extends File<LAnnotation>
42
45
  fileContainer,
43
46
  file,
44
47
  forceRender,
48
+ lineAnnotations,
45
49
  ...props
46
50
  }: FileRenderProps<LAnnotation>): boolean;
47
51
  syncVirtualizedTop(): void;
@@ -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":["FileContents","LineAnnotation","NumericScrollLineAnchor","PendingCodeViewLayoutReset","RenderWindow","StickySpecs","ThemeTypes","VirtualFileMetrics","WorkerPoolManager","CodeView","File","FileOptions","FileRenderProps","Virtualizer","VirtualizedFile","LAnnotation","fileContainer","file","forceRender","lineAnnotations"],"sources":["../../src/components/VirtualizedFile.d.ts"],"sourcesContent":["import type { 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 setLineAnnotations(lineAnnotations: LineAnnotation<LAnnotation>[]): void;\n private syncLineAnnotations;\n private hasLineAnnotations;\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, lineAnnotations?: LineAnnotation<LAnnotation>[]): 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, lineAnnotations, ...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":";;;;;;;;cAKqBc,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;EAC+BQ,UAAAA,CAAAA,OAAAA,EAD/BR,kBAC+BQ,EAAAA,KAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAAfd,kBAAAA,CAAAA,eAAAA,EAAAA,cAAAA,CAAec,WAAfd,CAAAA,EAAAA,CAAAA,EAAAA,IAAAA;EAIJc,QAAAA,mBAAAA;EAAZJ,QAAAA,kBAAAA;EACIL,aAAAA,CAAAA,SAAAA,EAAAA,MAAAA,EAAAA,eAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,MAAAA;EAIEN,UAAAA,CAAAA,OAAAA,EALNW,WAKMX,CALMe,WAKNf,CAAAA,GAAAA,SAAAA,CAAAA,EAAAA,IAAAA;EAAmCG,YAAAA,CAAAA,SAAAA,EAJrCG,UAIqCH,CAAAA,EAAAA,IAAAA;EAA6DY,QAAAA,gBAAAA;EAAfd,gBAAAA,CAAAA,CAAAA,EAAAA,OAAAA;EAKzDC,QAAAA,EAAAA,CAAAA,KAAAA,EAAAA,OAAAA,EAAAA,GAAAA,OAAAA;EAEbE,mBAAAA,CAAAA,IAAAA,EAPXJ,YAOWI,EAAAA,GAAAA,EAAAA,MAAAA,EAAAA,KAAAA,CAAAA,EAPwBD,0BAOxBC,EAAAA,eAAAA,CAAAA,EAPsEH,cAOtEG,CAPqFW,WAOrFX,CAAAA,EAAAA,CAAAA,EAAAA,MAAAA;EAAeC,eAAAA,CAAAA,UAAAA,EAAAA,MAAAA,CAAAA,EAAAA;IAK3CW,GAAAA,EAAAA,MAAAA;IAAeC,MAAAA,EAAAA,MAAAA;EAAMC,CAAAA,GAAAA,SAAAA;EAAaC,sBAAAA,CAAAA,gBAAAA,EAAAA,MAAAA,CAAAA,EAPOjB,uBAOPiB,GAAAA,SAAAA;EAA6CJ,oBAAAA,CAAAA,CAAAA,EAAAA,MAAAA;EAAhBH,sBAAAA,CAAAA,WAAAA,CAAAA,EALnCR,YAKmCQ,CAAAA,EALpBP,WAKoBO,GAAAA,SAAAA;EAnCNF,OAAAA,CAAAA,OAAAA,CAAAA,EAAAA,OAAAA,CAAAA,EAAAA,IAAAA;EAAI,QAAA,sBAAA;;;;;;;;;KAmCEE,gBAAgBG"}
@@ -2,6 +2,7 @@ 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
4
  import { getVirtualFileHeaderRegion, getVirtualFilePaddingBottom } from "../utils/computeVirtualFileMetrics.js";
5
+ import { FILE_ANNOTATION_DOM_KEY, FILE_ANNOTATION_LINE_NUMBER, includesFileAnnotations, shouldRenderFileAnnotations } from "../utils/includesFileAnnotations.js";
5
6
  import { iterateOverFile } from "../utils/iterateOverFile.js";
6
7
  import { File } from "./File.js";
7
8
 
@@ -17,7 +18,8 @@ var VirtualizedFile = class extends File {
17
18
  height = 0;
18
19
  cache = {
19
20
  heights: /* @__PURE__ */ new Map(),
20
- checkpoints: []
21
+ checkpoints: [],
22
+ fileAnnotationHeight: 0
21
23
  };
22
24
  isVisible = false;
23
25
  isSetup = false;
@@ -34,6 +36,18 @@ var VirtualizedFile = class extends File {
34
36
  this.metrics = metrics;
35
37
  this.resetLayoutCache();
36
38
  }
39
+ setLineAnnotations(lineAnnotations) {
40
+ if (this.syncLineAnnotations(lineAnnotations)) this.resetLayoutCache();
41
+ }
42
+ syncLineAnnotations(lineAnnotations) {
43
+ if (lineAnnotations == null || lineAnnotations === this.lineAnnotations) return false;
44
+ if (lineAnnotations.length === 0 && this.lineAnnotations.length === 0) return false;
45
+ super.setLineAnnotations(lineAnnotations);
46
+ return true;
47
+ }
48
+ hasLineAnnotations() {
49
+ return this.lineAnnotations.some((annotation) => annotation.lineNumber > FILE_ANNOTATION_LINE_NUMBER);
50
+ }
37
51
  getLineHeight(lineIndex, hasMetadataLine = false) {
38
52
  const cached = this.cache.heights.get(lineIndex);
39
53
  if (cached != null) return cached;
@@ -57,6 +71,7 @@ var VirtualizedFile = class extends File {
57
71
  }
58
72
  resetLayoutCache(recompute = false) {
59
73
  this.layoutDirty = true;
74
+ this.cache.fileAnnotationHeight = 0;
60
75
  if (this.cache.heights.size > 0) this.cache.heights.clear();
61
76
  if (this.cache.checkpoints.length > 0) this.cache.checkpoints.length = 0;
62
77
  if (this.renderRange != null) this.renderRange = void 0;
@@ -75,6 +90,17 @@ var VirtualizedFile = class extends File {
75
90
  if (this.code == null) return hasHeightChange;
76
91
  const content = this.code.children[1];
77
92
  if (!(content instanceof HTMLElement)) return hasHeightChange;
93
+ const hasFileAnnotations = includesFileAnnotations(this.lineAnnotations);
94
+ if (this.renderRange != null && hasFileAnnotations && shouldRenderFileAnnotations(this.renderRange)) {
95
+ const nextFileAnnotationHeight = measureFileAnnotationHeight(content) ?? 0;
96
+ if (nextFileAnnotationHeight !== this.cache.fileAnnotationHeight) {
97
+ this.cache.fileAnnotationHeight = nextFileAnnotationHeight;
98
+ hasHeightChange = true;
99
+ }
100
+ } else if (!hasFileAnnotations && this.cache.fileAnnotationHeight !== 0) {
101
+ this.cache.fileAnnotationHeight = 0;
102
+ hasHeightChange = true;
103
+ }
78
104
  for (const line of content.children) {
79
105
  if (!(line instanceof HTMLElement)) continue;
80
106
  const lineIndexAttr = line.dataset.lineIndex;
@@ -100,8 +126,9 @@ var VirtualizedFile = class extends File {
100
126
  if (dirty) this.top = this.getVirtualizedTop();
101
127
  return this.render({ file: this.file });
102
128
  };
103
- prepareCodeViewItem(file, top, reset) {
104
- let shouldResetLayoutCache = reset?.resetFileLayoutCache === true;
129
+ prepareCodeViewItem(file, top, reset, lineAnnotations) {
130
+ const annotationsChanged = this.syncLineAnnotations(lineAnnotations);
131
+ let shouldResetLayoutCache = reset?.resetFileLayoutCache === true || annotationsChanged;
105
132
  if (reset?.metrics != null) {
106
133
  this.metrics = reset.metrics;
107
134
  shouldResetLayoutCache = true;
@@ -119,7 +146,7 @@ var VirtualizedFile = class extends File {
119
146
  return this.height;
120
147
  }
121
148
  getLinePosition(lineNumber) {
122
- if (this.file == null) return;
149
+ if (this.file == null || lineNumber < 1) return;
123
150
  const { disableFileHeader = false, collapsed = false } = this.options;
124
151
  const lastLineIndex = getLastVisibleLineIndex(this.getOrCreateLineCache(this.file));
125
152
  let top = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
@@ -130,7 +157,8 @@ var VirtualizedFile = class extends File {
130
157
  const clampedLineIndex = Math.min(Math.max(lineNumber - 1, 0), lastLineIndex);
131
158
  const { overflow = "scroll" } = this.options;
132
159
  const { lineHeight } = this.metrics;
133
- if (overflow === "scroll" && this.lineAnnotations.length === 0) return {
160
+ top += this.cache.fileAnnotationHeight;
161
+ if (overflow === "scroll" && !this.hasLineAnnotations()) return {
134
162
  top: top + clampedLineIndex * lineHeight,
135
163
  height: lineHeight
136
164
  };
@@ -152,17 +180,18 @@ var VirtualizedFile = class extends File {
152
180
  const firstRenderedLineIndex = Math.min(this.renderRange.startingLine, lastLineIndex);
153
181
  const lastRenderedLineIndex = Math.min(firstRenderedLineIndex + this.renderRange.totalLines - 1, lastLineIndex);
154
182
  if (lastRenderedLineIndex < firstRenderedLineIndex) return;
155
- if (overflow === "scroll" && this.lineAnnotations.length === 0) {
183
+ const { fileAnnotationHeight } = this.cache;
184
+ if (overflow === "scroll" && !this.hasLineAnnotations()) {
156
185
  const { lineHeight } = this.metrics;
157
- const firstRenderedLineTop = headerRegion + this.renderRange.bufferBefore;
186
+ const firstRenderedLineTop = headerRegion + (firstRenderedLineIndex === 0 ? fileAnnotationHeight : this.renderRange.bufferBefore);
158
187
  const lineIndex = firstRenderedLineIndex + Math.max(Math.ceil((localViewportTop - firstRenderedLineTop) / lineHeight), 0);
159
188
  if (lineIndex > lastRenderedLineIndex) return;
160
189
  return {
161
190
  lineNumber: lineIndex + 1,
162
- top: headerRegion + lineIndex * lineHeight
191
+ top: headerRegion + fileAnnotationHeight + lineIndex * lineHeight
163
192
  };
164
193
  }
165
- let top = headerRegion + this.renderRange.bufferBefore;
194
+ let top = headerRegion + (firstRenderedLineIndex === 0 ? fileAnnotationHeight : this.renderRange.bufferBefore);
166
195
  for (let lineIndex = firstRenderedLineIndex; lineIndex <= lastRenderedLineIndex; lineIndex++) {
167
196
  if (top >= localViewportTop) return {
168
197
  lineNumber: lineIndex + 1,
@@ -195,7 +224,7 @@ var VirtualizedFile = class extends File {
195
224
  }
196
225
  cleanUp(recycle = false) {
197
226
  if (this.fileContainer != null && this.isSimpleMode()) this.getSimpleVirtualizer()?.disconnect(this.fileContainer);
198
- if (!recycle) this.layoutDirty = true;
227
+ if (!recycle) this.resetLayoutCache();
199
228
  this.isSetup = false;
200
229
  super.cleanUp(recycle);
201
230
  }
@@ -219,7 +248,8 @@ var VirtualizedFile = class extends File {
219
248
  this.layoutDirty = false;
220
249
  return;
221
250
  }
222
- if (overflow === "scroll" && this.lineAnnotations.length === 0) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
251
+ this.height += this.cache.fileAnnotationHeight;
252
+ if (overflow === "scroll" && !this.hasLineAnnotations()) this.height += this.getOrCreateLineCache(this.file).length * lineHeight;
223
253
  else iterateOverFile({
224
254
  lines,
225
255
  callback: ({ lineIndex }) => {
@@ -254,9 +284,11 @@ var VirtualizedFile = class extends File {
254
284
  this.forceRenderOverride = true;
255
285
  this.virtualizer.instanceChanged(this, false);
256
286
  }
257
- render({ fileContainer, file, forceRender = false,...props }) {
287
+ render({ fileContainer, file, forceRender = false, lineAnnotations,...props }) {
258
288
  const { forceRenderOverride, isSetup } = this;
259
289
  this.forceRenderOverride = void 0;
290
+ const annotationsChanged = this.syncLineAnnotations(lineAnnotations);
291
+ if (annotationsChanged) this.resetLayoutCache();
260
292
  this.file ??= file;
261
293
  fileContainer = this.getOrCreateFileContainerNode(fileContainer);
262
294
  if (this.file == null) {
@@ -283,7 +315,8 @@ var VirtualizedFile = class extends File {
283
315
  file: this.file,
284
316
  fileContainer,
285
317
  renderRange,
286
- forceRender: forceRenderOverride ?? forceRender,
318
+ lineAnnotations,
319
+ forceRender: (forceRenderOverride ?? forceRender) || annotationsChanged,
287
320
  ...props
288
321
  });
289
322
  }
@@ -360,6 +393,12 @@ var VirtualizedFile = class extends File {
360
393
  const fileHeight = this.height;
361
394
  const headerRegion = getVirtualFileHeaderRegion(this.metrics, disableFileHeader);
362
395
  const paddingBottom = lineCount > 0 ? getVirtualFilePaddingBottom(this.metrics) : 0;
396
+ const { fileAnnotationHeight } = this.cache;
397
+ const codeRegionTop = headerRegion + fileAnnotationHeight;
398
+ const codeRowsHeight = Math.max(0, fileHeight - headerRegion - fileAnnotationHeight - paddingBottom);
399
+ const hasFileAnnotations = includesFileAnnotations(this.lineAnnotations);
400
+ const fileAnnotationTop = fileTop + headerRegion;
401
+ const measuredFileAnnotationVisible = fileAnnotationHeight > 0 && hasFileAnnotations && fileAnnotationTop < bottom && fileAnnotationTop + fileAnnotationHeight > top;
363
402
  if (fileTop < top - fileHeight || fileTop > bottom) return {
364
403
  startingLine: 0,
365
404
  totalLines: 0,
@@ -376,13 +415,21 @@ var VirtualizedFile = class extends File {
376
415
  const totalLines = Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount + hunkLineCount * 2;
377
416
  const totalHunks = totalLines / hunkLineCount;
378
417
  const viewportCenter = (top + bottom) / 2;
379
- if (overflow === "scroll" && this.lineAnnotations.length === 0) {
380
- const centerLine = Math.floor((viewportCenter - (fileTop + headerRegion)) / lineHeight);
418
+ if (overflow === "scroll" && !this.hasLineAnnotations()) {
419
+ const sourceRowsTop = fileTop + codeRegionTop;
420
+ const sourceRowsBottom = sourceRowsTop + codeRowsHeight;
421
+ if (!measuredFileAnnotationVisible && !(sourceRowsTop < bottom && sourceRowsBottom > top)) return {
422
+ startingLine: 0,
423
+ totalLines: 0,
424
+ bufferBefore: 0,
425
+ bufferAfter: fileHeight - headerRegion - paddingBottom
426
+ };
427
+ const centerLine = Math.floor(measuredFileAnnotationVisible && viewportCenter < fileTop + codeRegionTop ? 0 : (viewportCenter - (fileTop + codeRegionTop)) / lineHeight);
381
428
  const idealStartHunk$1 = Math.floor(centerLine / hunkLineCount) - Math.floor(totalHunks / 2);
382
429
  const totalHunksInFile = Math.ceil(lineCount / hunkLineCount);
383
430
  const startingLine$1 = Math.max(0, Math.min(idealStartHunk$1, totalHunksInFile)) * hunkLineCount;
384
431
  const clampedTotalLines$1 = idealStartHunk$1 < 0 ? totalLines + idealStartHunk$1 * hunkLineCount : totalLines;
385
- const bufferBefore$1 = startingLine$1 * lineHeight;
432
+ const bufferBefore$1 = startingLine$1 === 0 ? 0 : fileAnnotationHeight + startingLine$1 * lineHeight;
386
433
  const renderedLines = Math.min(clampedTotalLines$1, lineCount - startingLine$1);
387
434
  return {
388
435
  startingLine: startingLine$1,
@@ -394,7 +441,7 @@ var VirtualizedFile = class extends File {
394
441
  const overflowHunks = totalHunks;
395
442
  const hunkOffsets = [];
396
443
  const checkpoint = this.getLayoutCheckpointBeforeTop(Math.max(0, top - fileTop - totalLines * lineHeight * 2), hunkLineCount);
397
- let absoluteLineTop = fileTop + (checkpoint?.top ?? headerRegion);
444
+ let absoluteLineTop = fileTop + (checkpoint?.top ?? codeRegionTop);
398
445
  let currentLine = checkpoint?.lineIndex ?? 0;
399
446
  let firstVisibleHunk;
400
447
  let centerHunk;
@@ -406,7 +453,7 @@ var VirtualizedFile = class extends File {
406
453
  const isAtHunkBoundary = currentLine % hunkLineCount === 0;
407
454
  const currentHunk = Math.floor(currentLine / hunkLineCount);
408
455
  if (isAtHunkBoundary) {
409
- hunkOffsets[currentHunk] = absoluteLineTop - (fileTop + headerRegion);
456
+ hunkOffsets[currentHunk] = absoluteLineTop - (fileTop + codeRegionTop);
410
457
  if (overflowCounter != null) {
411
458
  if (overflowCounter <= 0) return true;
412
459
  overflowCounter--;
@@ -421,7 +468,10 @@ var VirtualizedFile = class extends File {
421
468
  return false;
422
469
  }
423
470
  });
424
- if (firstVisibleHunk == null) return {
471
+ if (firstVisibleHunk == null) if (measuredFileAnnotationVisible) {
472
+ firstVisibleHunk = 0;
473
+ centerHunk = 0;
474
+ } else return {
425
475
  startingLine: 0,
426
476
  totalLines: 0,
427
477
  bufferBefore: 0,
@@ -433,16 +483,27 @@ var VirtualizedFile = class extends File {
433
483
  const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));
434
484
  const startingLine = startHunk * hunkLineCount;
435
485
  const clampedTotalLines = idealStartHunk < 0 ? totalLines + idealStartHunk * hunkLineCount : totalLines;
436
- const bufferBefore = hunkOffsets[startHunk] ?? 0;
486
+ const codeBufferBefore = hunkOffsets[startHunk] ?? 0;
487
+ const bufferBefore = startingLine === 0 ? 0 : fileAnnotationHeight + codeBufferBefore;
437
488
  const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;
489
+ const bufferAfter = finalHunkIndex < hunkOffsets.length ? codeRowsHeight - hunkOffsets[finalHunkIndex] : codeRowsHeight - (absoluteLineTop - fileTop - codeRegionTop);
438
490
  return {
439
491
  startingLine,
440
492
  totalLines: clampedTotalLines,
441
493
  bufferBefore,
442
- bufferAfter: finalHunkIndex < hunkOffsets.length ? fileHeight - headerRegion - hunkOffsets[finalHunkIndex] - paddingBottom : fileHeight - (absoluteLineTop - fileTop) - paddingBottom
494
+ bufferAfter: Math.max(0, bufferAfter)
443
495
  };
444
496
  }
445
497
  };
498
+ function measureFileAnnotationHeight(content) {
499
+ let height;
500
+ for (const child of content.children) {
501
+ if (!(child instanceof HTMLElement)) continue;
502
+ if (child.dataset.lineAnnotation !== FILE_ANNOTATION_DOM_KEY) continue;
503
+ height = Math.max(height ?? 0, child.getBoundingClientRect().height);
504
+ }
505
+ return height;
506
+ }
446
507
  function getLastVisibleLineIndex(lines) {
447
508
  const lastLine = lines.at(-1);
448
509
  if (lastLine == null || lastLine === "" || lastLine === "\n" || lastLine === "\r\n" || lastLine === "\r") return lines.length - 2;
@@ -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":";;;;;;;;AAqCA,MAAM,6BAA6B;AAEnC,IAAI,aAAa;AAEjB,SAAS,2BACP,iBACA,aACS;AACT,SACG,gBAAgB,YAAY,eAC1B,YAAY,YAAY,cAC1B,gBAAgB,aAAa,YAAY,YAAY,aAAa,WAClE,gBAAgB,sBAAsB,YACpC,YAAY,sBAAsB,WACpC,gBAAgB,qBAAqB,YACnC,YAAY,qBAAqB,UACpC,gBAAgB,cAAc,YAAY;;AAI9C,IAAa,kBAAb,cAEU,KAAkB;CAC1B,AAAkB,OAAe,oBAAoB,EAAE;CAEvD,AAAO;CACP,AAAO,SAAiB;CACxB,AAAQ,QAAyB;EAAE,yBAAS,IAAI,KAAK;EAAE,aAAa,EAAE;EAAE;CACxE,AAAQ,YAAqB;CAC7B,AAAQ,UAAmB;CAC3B,AAAQ,cAAc;CACtB,AAAQ;CACR,AAAQ;CAER,YACE,SACA,AAAQA,aACR,AAAQC,UAA8B,8BACtC,eACA,qBAAqB,OACrB;AACA,QAAM,SAAS,eAAe,mBAAmB;EALzC;EACA;;CAOV,AAAO,WAAW,SAA6B,QAAQ,OAAa;AAClE,MAAI,CAAC,SAAS,gBAAgB,KAAK,SAAS,QAAQ,CAClD;AAGF,OAAK,UAAU;AACf,OAAK,kBAAkB;;CAMzB,AAAO,cAAc,WAAmB,kBAAkB,OAAe;EACvE,MAAM,SAAS,KAAK,MAAM,QAAQ,IAAI,UAAU;AAChD,MAAI,UAAU,KACZ,QAAO;EAET,MAAM,aAAa,kBAAkB,IAAI;AACzC,SAAO,KAAK,QAAQ,aAAa;;CAGnC,AAAS,WAAW,SAAqD;AACvE,MAAI,KAAK,gBAAgB,CACvB,OAAM,IAAI,MACR,8FACD;AAGH,MAAI,WAAW,KAAM;EACrB,MAAM,EAAE,SAAS,oBAAoB;EACrC,MAAM,iBAAiB,CAAC,gBAAgB,iBAAiB,QAAQ;EACjE,MAAM,gBAAgB,2BAA2B,iBAAiB,QAAQ;AAE1E,QAAM,WAAW,QAAQ;AAEzB,MAAI,cACF,MAAK,iBAAiB,KAAK;AAI7B,MAAI,eACF,MAAK,sBAAsB;AAE7B,MAAI,eACF,MAAK,YAAY,gBAAgB,MAAM,cAAc;;CAIzD,AAAS,aAAa,WAA6B;AACjD,MAAI,KAAK,gBAAgB,CACvB,OAAM,IAAI,MACR,gGACD;AAGH,QAAM,aAAa,UAAU;;CAG/B,AAAQ,iBAAiB,YAAY,OAAa;AAChD,OAAK,cAAc;AACnB,MAAI,KAAK,MAAM,QAAQ,OAAO,EAC5B,MAAK,MAAM,QAAQ,OAAO;AAE5B,MAAI,KAAK,MAAM,YAAY,SAAS,EAClC,MAAK,MAAM,YAAY,SAAS;AAElC,MAAI,KAAK,eAAe,KACtB,MAAK,cAAc;AAIrB,MAAI,aAAa,KAAK,cAAc,CAClC,MAAK,wBAAwB;;CAMjC,AAAO,mBAA4B;EACjC,IAAI,kBAAkB;AACtB,MAAI,KAAK,iBAAiB,QAAQ,KAAK,QAAQ,MAAM;AACnD,OAAI,KAAK,WAAW,EAClB,mBAAkB;AAEpB,QAAK,SAAS;AACd,UAAO;;EAET,MAAM,EAAE,WAAW,aAAa,KAAK;AACrC,OAAK,MAAM,KAAK,mBAAmB;AAInC,MACE,aAAa,YACb,KAAK,gBAAgB,WAAW,KAChC,CAAC,KAAK,0BAA0B,CAEhC,QAAO;AAIT,MAAI,KAAK,QAAQ,KACf,QAAO;EAET,MAAM,UAAU,KAAK,KAAK,SAAS;AACnC,MAAI,EAAE,mBAAmB,aACvB,QAAO;AAGT,OAAK,MAAM,QAAQ,QAAQ,UAAU;AACnC,OAAI,EAAE,gBAAgB,aAAc;GAEpC,MAAM,gBAAgB,KAAK,QAAQ;AACnC,OAAI,iBAAiB,KAAM;GAE3B,MAAM,YAAY,OAAO,cAAc;GACvC,IAAI,iBAAiB,KAAK,uBAAuB,CAAC;GAClD,IAAI,cAAc;AAGlB,OACE,KAAK,8BAA8B,gBAClC,oBAAoB,KAAK,mBAAmB,WAC3C,eAAe,KAAK,mBAAmB,UACzC;AACA,QAAI,eAAe,KAAK,mBAAmB,QACzC,eAAc;AAEhB,sBACE,KAAK,mBAAmB,uBAAuB,CAAC;;GAGpD,MAAM,iBAAiB,KAAK,cAAc,WAAW,YAAY;AAEjE,OAAI,mBAAmB,eACrB;AAGF,qBAAkB;AAGlB,OAAI,mBAAmB,KAAK,QAAQ,cAAc,cAAc,IAAI,GAClE,MAAK,MAAM,QAAQ,OAAO,UAAU;OAIpC,MAAK,MAAM,QAAQ,IAAI,WAAW,eAAe;;AAIrD,MAAI,mBAAmB,KAAK,0BAA0B,CACpD,MAAK,uBAAuB,KAAK;AAEnC,SAAO;;CAGT,AAAO,YAAY,UAA4B;AAC7C,MAAI,KAAK,iBAAiB,QAAQ,KAAK,QAAQ,KAC7C,QAAO;AAET,MAAI,MACF,MAAK,MAAM,KAAK,mBAAmB;AAErC,SAAO,KAAK,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;;CAOzC,AAAO,oBACL,MACA,KACA,OACQ;EACR,IAAI,yBAAyB,OAAO,yBAAyB;AAC7D,MAAI,OAAO,WAAW,MAAM;AAC1B,QAAK,UAAU,MAAM;AACrB,4BAAyB;;EAG3B,MAAM,EAAE,YAAY,UAAU,KAAK;AACnC,MAAI,KAAK,qBAAqB,WAAW;AACvC,QAAK,mBAAmB;AACxB,4BAAyB;;AAG3B,MAAI,uBACF,MAAK,kBAAkB;AAGzB,MAAI,KAAK,SAAS,KAChB,MAAK,cAAc;AAErB,OAAK,OAAO;AACZ,OAAK,MAAM;AACX,OAAK,wBAAwB;AAC7B,SAAO,KAAK;;CAGd,AAAO,gBACL,YAC6C;AAC7C,MAAI,KAAK,QAAQ,KACf;EAGF,MAAM,EAAE,oBAAoB,OAAO,YAAY,UAAU,KAAK;EAE9D,MAAM,gBAAgB,wBADR,KAAK,qBAAqB,KAAK,KAAK,CACE;EACpD,IAAI,MAAM,2BAA2B,KAAK,SAAS,kBAAkB;AAErE,MAAI,aAAa,gBAAgB,EAC/B,QAAO;GAAE;GAAK,QAAQ;GAAG;EAG3B,MAAM,mBAAmB,KAAK,IAC5B,KAAK,IAAI,aAAa,GAAG,EAAE,EAC3B,cACD;EACD,MAAM,EAAE,WAAW,aAAa,KAAK;EACrC,MAAM,EAAE,eAAe,KAAK;AAE5B,MAAI,aAAa,YAAY,KAAK,gBAAgB,WAAW,EAC3D,QAAO;GACL,KAAK,MAAM,mBAAmB;GAC9B,QAAQ;GACT;EAGH,MAAM,aACJ,KAAK,mCAAmC,iBAAiB;AAC3D,QAAM,YAAY,OAAO;AACzB,OACE,IAAI,YAAY,YAAY,aAAa,GACzC,YAAY,kBACZ,YAEA,QAAO,KAAK,cAAc,WAAW,MAAM;AAG7C,SAAO;GACL;GACA,QAAQ,KAAK,cAAc,kBAAkB,MAAM;GACpD;;CAGH,AAAO,uBACL,kBACqC;AACrC,MAAI,KAAK,QAAQ,QAAQ,KAAK,eAAe,KAC3C;EAGF,MAAM,EACJ,oBAAoB,OACpB,YAAY,OACZ,WAAW,aACT,KAAK;AACT,MAAI,aAAa,KAAK,YAAY,cAAc,EAC9C;EAIF,MAAM,gBAAgB,wBADR,KAAK,qBAAqB,KAAK,KAAK,CACE;AACpD,MAAI,gBAAgB,EAClB;EAGF,MAAM,eAAe,2BACnB,KAAK,SACL,kBACD;EACD,MAAM,yBAAyB,KAAK,IAClC,KAAK,YAAY,cACjB,cACD;EACD,MAAM,wBAAwB,KAAK,IACjC,yBAAyB,KAAK,YAAY,aAAa,GACvD,cACD;AACD,MAAI,wBAAwB,uBAC1B;AAKF,MAAI,aAAa,YAAY,KAAK,gBAAgB,WAAW,GAAG;GAC9D,MAAM,EAAE,eAAe,KAAK;GAC5B,MAAM,uBAAuB,eAAe,KAAK,YAAY;GAK7D,MAAM,YAAY,yBAJK,KAAK,IAC1B,KAAK,MAAM,mBAAmB,wBAAwB,WAAW,EACjE,EACD;AAED,OAAI,YAAY,sBACd;AAGF,UAAO;IACL,YAAY,YAAY;IACxB,KAAK,eAAe,YAAY;IACjC;;EAIH,IAAI,MAAM,eAAe,KAAK,YAAY;AAC1C,OACE,IAAI,YAAY,wBAChB,aAAa,uBACb,aACA;AACA,OAAI,OAAO,iBACT,QAAO;IACL,YAAY,YAAY;IACxB;IACD;AAEH,UAAO,KAAK,cAAc,UAAU;;;CAMxC,AAAO,uBAA+B;AACpC,SAAO,KAAK;;CAGd,AAAO,uBACL,aACyB;AACzB,MAAI,KAAK,OAAO,QAAQ,KAAK,QAAQ,KACnC;AAEF,MAAI,KAAK,QAAQ,cAAc,KAC7B,QAAO;GAAE,WAAW,KAAK;GAAK,QAAQ,KAAK;GAAQ;EAErD,MAAM,cACJ,eAAe,OACX,KAAK,6BAA6B,KAAK,MAAM,KAAK,KAAK,YAAY,GACnE,KAAK;AACX,MAAI,eAAe,KACjB;EAEF,MAAM,EAAE,cAAc,aAAa,eAAe;EAalD,IAAI,mBAAmB;AACvB,MAAI,eAAe,GAAG;GACpB,MAAM,eAAe,eAAe,KAAK,YAAY,gBAAgB;AACrE,OAAI,KAAK,MAAM,aAAa,IAC1B,oBAAmB;;AAGvB,SAAO;GACL,WAAW,KAAK,MAAM,eAAe;GACrC,QAAQ,KAAK,UAAU,eAAe;GACvC;;CAGH,AAAS,QAAQ,UAAU,OAAa;AACtC,MAAI,KAAK,iBAAiB,QAAQ,KAAK,cAAc,CACnD,MAAK,sBAAsB,EAAE,WAAW,KAAK,cAAc;AAE7D,MAAI,CAAC,QACH,MAAK,cAAc;AAErB,OAAK,UAAU;AACf,QAAM,QAAQ,QAAQ;;CAKxB,AAAQ,uBAAuB,QAAQ,OAAa;EAClD,MAAM,qBAAqB,KAAK,0BAA0B;AAC1D,MAAI,CAAC,SAAS,CAAC,KAAK,eAAe,CAAC,mBAClC;EAGF,MAAM,iBAAiB,KAAK,WAAW;AACvC,OAAK,SAAS;AACd,OAAK,MAAM,cAAc,EAAE;AAC3B,MAAI,KAAK,QAAQ,MAAM;AACrB,QAAK,cAAc;AACnB;;EAGF,MAAM,EACJ,oBAAoB,OACpB,YAAY,OACZ,WAAW,aACT,KAAK;EACT,MAAM,EAAE,eAAe,KAAK;EAC5B,MAAM,QAAQ,KAAK,qBAAqB,KAAK,KAAK;EAClD,MAAM,eAAe,2BACnB,KAAK,SACL,kBACD;EACD,MAAM,gBAAgB,4BAA4B,KAAK,QAAQ;AAE/D,OAAK,UAAU;AACf,MAAI,WAAW;AACb,QAAK,cAAc;AACnB;;AAGF,MAAI,aAAa,YAAY,KAAK,gBAAgB,WAAW,EAC3D,MAAK,UAAU,KAAK,qBAAqB,KAAK,KAAK,CAAC,SAAS;MAE7D,iBAAgB;GACd;GACA,WAAW,EAAE,gBAAgB;AAC3B,SAAK,oBAAoB,WAAW,KAAK,OAAO;AAChD,SAAK,UAAU,KAAK,cAAc,WAAW,MAAM;;GAEtD,CAAC;AAGJ,MAAI,MAAM,SAAS,EACjB,MAAK,UAAU;AAGjB,MAAI,KAAK,iBAAiB,QAAQ,sBAAsB,CAAC,gBAAgB;GACvE,MAAM,OAAO,KAAK,cAAc,uBAAuB;AACvD,OAAI,KAAK,WAAW,KAAK,OACvB,SAAQ,IACN,wEACA;IACE,MAAM,KAAK,KAAK;IAChB,eAAe,KAAK;IACpB,gBAAgB,KAAK;IACtB,CACF;OAED,SAAQ,IACN,qEACD;;AAGL,OAAK,cAAc;;CAGrB,AAAO,cAAc,SAAwB;AAC3C,MAAI,KAAK,gBAAgB,IAAI,KAAK,iBAAiB,KACjD;AAEF,MAAI,WAAW,CAAC,KAAK,WAAW;AAC9B,QAAK,MAAM,KAAK,mBAAmB;AACnC,QAAK,YAAY;aACR,CAAC,WAAW,KAAK,WAAW;AACrC,QAAK,YAAY;AACjB,QAAK,UAAU;;;CAInB,AAAS,WAAiB;AACxB,MAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,KAChC;AAEF,OAAK,sBAAsB;AAC3B,OAAK,YAAY,gBAAgB,MAAM,MAAM;;CAG/C,AAAS,OAAO,EACd,eACA,MACA,cAAc,MACd,GAAG,SACqC;EACxC,MAAM,EAAE,qBAAqB,YAAY;AACzC,OAAK,sBAAsB;AAE3B,OAAK,SAAS;AAEd,kBAAgB,KAAK,6BAA6B,cAAc;AAEhE,MAAI,KAAK,QAAQ,MAAM;AACrB,WAAQ,MACN,gFACD;AACD,UAAO;;AAGT,MAAI,CAAC,SAAS;AACZ,QAAK,wBAAwB;GAC7B,MAAM,cAAc,KAAK,sBAAsB;AAC/C,QAAK,QAAQ,KAAK,mBAAmB;AACrC,OAAI,KAAK,gBAAgB,CACvB,MAAK,YAAY;QACZ;AACL,QAAI,eAAe,KACjB,OAAM,IAAI,MACR,8DACD;AAEH,gBAAY,QAAQ,eAAe,KAAK;AACxC,SAAK,YAAY,YAAY,kBAC3B,KAAK,OAAO,GACZ,KAAK,OACN;;AAEH,QAAK,UAAU;QAEf,MAAK,QAAQ,KAAK,mBAAmB;AAGvC,MAAI,CAAC,KAAK,aAAa,KAAK,cAAc,CACxC,QAAO,KAAK,kBAAkB,KAAK,OAAO;EAG5C,MAAM,cAAc,KAAK,YAAY,gBAAgB;EACrD,MAAM,UAAU,KAAK,OAAO;EAC5B,MAAM,cAAc,KAAK,6BACvB,KAAK,MACL,SACA,YACD;AACD,SAAO,MAAM,OAAO;GAClB,MAAM,KAAK;GACX;GACA;GACA,aAAa,uBAAuB;GACpC,GAAG;GACJ,CAAC;;CAGJ,AAAO,qBAA2B;AAChC,OAAK,MAAM,KAAK,mBAAmB;;CAGrC,AAAmB,qCAA8C;AAC/D,SAAO,KAAK,gBAAgB,IAAI,MAAM,oCAAoC;;CAG5E,AAAQ,eAAwB;AAC9B,SAAO,KAAK,YAAY,SAAS;;CAGnC,AAAQ,iBAA0B;AAChC,SAAO,KAAK,YAAY,SAAS;;CAGnC,AAAQ,oBAAoB,WAAmB,KAAmB;AAChE,MAAI,YAAY,+BAA+B,EAC7C;AAEF,OAAK,MAAM,YAAY,KAAK;GAAE;GAAW;GAAK,CAAC;;CAOjD,AAAQ,mCACN,WACkC;AAClC,MAAI,aAAa,KAAK,KAAK,MAAM,YAAY,WAAW,EACtD;EAGF,IAAI,MAAM;EACV,IAAI,OAAO,KAAK,MAAM,YAAY,SAAS;EAC3C,IAAIC;AAEJ,SAAO,OAAO,MAAM;GAClB,MAAM,MAAO,MAAM,QAAS;GAC5B,MAAM,aAAa,KAAK,MAAM,YAAY;AAC1C,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,4CAA4C;AAE9D,OAAI,WAAW,aAAa,WAAW;AACrC,aAAS;AACT,UAAM,MAAM;SAEZ,QAAO,MAAM;;AAIjB,SAAO;;CAQT,AAAQ,6BACN,KACA,eACkC;EAClC,IAAI,MAAM;EACV,IAAI,OAAO,KAAK,MAAM,YAAY,SAAS;EAC3C,IAAI,cAAc;AAElB,SAAO,OAAO,MAAM;GAClB,MAAM,MAAO,MAAM,QAAS;GAC5B,MAAM,aAAa,KAAK,MAAM,YAAY;AAC1C,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,4CAA4C;AAE9D,OAAI,WAAW,OAAO,KAAK;AACzB,kBAAc;AACd,UAAM,MAAM;SAEZ,QAAO,MAAM;;AAIjB,MAAI,iBAAiB,KACnB,QAAO,eAAe,IAAI,KAAK,MAAM,YAAY,eAAe;AAGlE,OAAK,IAAI,QAAQ,aAAa,SAAS,GAAG,SAAS;GACjD,MAAM,aAAa,KAAK,MAAM,YAAY;AAC1C,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,4CAA4C;AAE9D,OAAI,WAAW,YAAY,kBAAkB,EAC3C,QAAO;;;CAOb,AAAQ,oBAA4B;AAClC,MAAI,KAAK,YAAY,SAAS,WAC5B,QAAO,KAAK,YAAY,uBAAuB,KAAK;AAEtD,SAAO,KAAK,iBAAiB,OACzB,KAAK,YAAY,2BAA2B,KAAK,cAAc,GAC/D;;CAGN,AAAQ,uBAAgD;AACtD,SAAO,KAAK,YAAY,SAAS,WAAW,KAAK,cAAc;;CAGjE,AAAQ,2BAAoC;AAC1C,SAAO,KAAK,sBAAsB,EAAE,OAAO,mBAAmB;;CAGhE,AAAQ,6BACN,MACA,SACA,EAAE,KAAK,UACM;EACb,MAAM,EAAE,oBAAoB,OAAO,WAAW,aAAa,KAAK;EAChE,MAAM,EAAE,eAAe,eAAe,KAAK;EAC3C,MAAM,QAAQ,KAAK,qBAAqB,KAAK;EAC7C,MAAM,YAAY,MAAM;EACxB,MAAM,aAAa,KAAK;EACxB,MAAM,eAAe,2BACnB,KAAK,SACL,kBACD;EACD,MAAM,gBACJ,YAAY,IAAI,4BAA4B,KAAK,QAAQ,GAAG;AAG9D,MAAI,UAAU,MAAM,cAAc,UAAU,OAC1C,QAAO;GACL,cAAc;GACd,YAAY;GACZ,cAAc;GACd,aAAa,aAAa,eAAe;GAC1C;AAIH,MAAI,aAAa,cACf,QAAO;GACL,cAAc;GACd,YAAY;GACZ,cAAc;GACd,aAAa;GACd;EAIH,MAAM,uBAAuB,KAAK,KAChC,KAAK,IAAI,SAAS,KAAK,EAAE,GAAG,WAC7B;EACD,MAAM,aACJ,KAAK,KAAK,uBAAuB,cAAc,GAAG,gBAClD,gBAAgB;EAClB,MAAM,aAAa,aAAa;EAChC,MAAM,kBAAkB,MAAM,UAAU;AAGxC,MAAI,aAAa,YAAY,KAAK,gBAAgB,WAAW,GAAG;GAE9D,MAAM,aAAa,KAAK,OACrB,kBAAkB,UAAU,iBAAiB,WAC/C;GAID,MAAMC,mBAHa,KAAK,MAAM,aAAa,cAAc,GAGrB,KAAK,MAAM,aAAa,EAAE;GAC9D,MAAM,mBAAmB,KAAK,KAAK,YAAY,cAAc;GAC7D,MAAMC,iBACJ,KAAK,IAAI,GAAG,KAAK,IAAID,kBAAgB,iBAAiB,CAAC,GAAG;GAE5D,MAAME,sBACJF,mBAAiB,IACb,aAAaA,mBAAiB,gBAC9B;GAEN,MAAMG,iBAAeF,iBAAe;GACpC,MAAM,gBAAgB,KAAK,IACzBC,qBACA,YAAYD,eACb;AAMD,UAAO;IACL;IACA,YAAYC;IACZ;IACA,aATkB,KAAK,IACvB,IACC,YAAYD,iBAAe,iBAAiB,WAC9C;IAOA;;EAIH,MAAM,gBAAgB;EACtB,MAAMG,cAAwB,EAAE;EAIhC,MAAM,aAAa,KAAK,6BACtB,KAAK,IAAI,GAAG,MAAM,UAAU,aAAa,aAAa,EAAE,EACxD,cACD;EAED,IAAI,kBAAkB,WAAW,YAAY,OAAO;EACpD,IAAI,cAAc,YAAY,aAAa;EAC3C,IAAIC;EACJ,IAAIC;EACJ,IAAIC;AAEJ,kBAAgB;GACd;GACA,cAAc,YAAY,aAAa;GACvC,WAAW,EAAE,gBAAgB;IAC3B,MAAM,mBAAmB,cAAc,kBAAkB;IACzD,MAAM,cAAc,KAAK,MAAM,cAAc,cAAc;AAE3D,QAAI,kBAAkB;AACpB,iBAAY,eAAe,mBAAmB,UAAU;AAExD,SAAI,mBAAmB,MAAM;AAC3B,UAAI,mBAAmB,EACrB,QAAO;AAET;;;IAIJ,MAAMC,eAAa,KAAK,cAAc,WAAW,MAAM;AAGvD,QAAI,kBAAkB,MAAMA,gBAAc,kBAAkB,OAC1D,sBAAqB;AAIvB,QAAI,kBAAkBA,eAAa,eACjC,gBAAe;AAIjB,QACE,mBAAmB,QACnB,mBAAmB,UACnB,iBAEA,mBAAkB;AAGpB;AACA,uBAAmBA;AAEnB,WAAO;;GAEV,CAAC;AAGF,MAAI,oBAAoB,KACtB,QAAO;GACL,cAAc;GACd,YAAY;GACZ,cAAc;GACd,aAAa,aAAa,eAAe;GAC1C;AAIH,iBAAe;EACf,MAAM,iBAAiB,KAAK,MAAM,aAAa,aAAa,EAAE;EAI9D,MAAM,eAAe,KAAK,IACxB,GACA,KAAK,KAAK,YAAY,cAAc,GAAG,WACxC;EACD,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,gBAAgB,aAAa,CAAC;EACrE,MAAM,eAAe,YAAY;EAGjC,MAAM,oBACJ,iBAAiB,IACb,aAAa,iBAAiB,gBAC9B;EAGN,MAAM,eAAe,YAAY,cAAc;EAG/C,MAAM,iBAAiB,YAAY,oBAAoB;AASvD,SAAO;GACL;GACA,YAAY;GACZ;GACA,aAXA,iBAAiB,YAAY,SACzB,aACA,eACA,YAAY,kBACZ,gBACA,cAAc,kBAAkB,WAAW;GAOhD;;;AAIL,SAAS,wBAAwB,OAAyB;CACxD,MAAM,WAAW,MAAM,GAAG,GAAG;AAC7B,KACE,YAAY,QACZ,aAAa,MACb,aAAa,QACb,aAAa,UACb,aAAa,KAEb,QAAO,MAAM,SAAS;AAGxB,QAAO,MAAM,SAAS"}
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","height: number | undefined"],"sources":["../../src/components/VirtualizedFile.ts"],"sourcesContent":["import { DEFAULT_VIRTUAL_FILE_METRICS } from '../constants';\nimport type {\n FileContents,\n LineAnnotation,\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 {\n FILE_ANNOTATION_DOM_KEY,\n FILE_ANNOTATION_LINE_NUMBER,\n includesFileAnnotations,\n shouldRenderFileAnnotations,\n} from '../utils/includesFileAnnotations';\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 // Measured height for the file-level annotation row. Starts at 0 so\n // unmeasured annotations behave like all other unmeasured annotations.\n fileAnnotationHeight: number;\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 = {\n heights: new Map(),\n checkpoints: [],\n fileAnnotationHeight: 0,\n };\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 override setLineAnnotations(\n lineAnnotations: LineAnnotation<LAnnotation>[]\n ): void {\n if (this.syncLineAnnotations(lineAnnotations)) {\n this.resetLayoutCache();\n }\n }\n\n private syncLineAnnotations(\n lineAnnotations: LineAnnotation<LAnnotation>[] | undefined\n ): boolean {\n if (lineAnnotations == null || lineAnnotations === this.lineAnnotations) {\n return false;\n }\n if (lineAnnotations.length === 0 && this.lineAnnotations.length === 0) {\n return false;\n }\n\n super.setLineAnnotations(lineAnnotations);\n return true;\n }\n\n private hasLineAnnotations(): boolean {\n return this.lineAnnotations.some(\n (annotation) => annotation.lineNumber > FILE_ANNOTATION_LINE_NUMBER\n );\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 this.cache.fileAnnotationHeight = 0;\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 const hasFileAnnotations = includesFileAnnotations(this.lineAnnotations);\n if (\n this.renderRange != null &&\n hasFileAnnotations &&\n shouldRenderFileAnnotations(this.renderRange)\n ) {\n const fileAnnotationHeight = measureFileAnnotationHeight(content);\n const nextFileAnnotationHeight = fileAnnotationHeight ?? 0;\n if (nextFileAnnotationHeight !== this.cache.fileAnnotationHeight) {\n this.cache.fileAnnotationHeight = nextFileAnnotationHeight;\n hasHeightChange = true;\n }\n } else if (!hasFileAnnotations && this.cache.fileAnnotationHeight !== 0) {\n this.cache.fileAnnotationHeight = 0;\n hasHeightChange = true;\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 lineAnnotations?: LineAnnotation<LAnnotation>[]\n ): number {\n const annotationsChanged = this.syncLineAnnotations(lineAnnotations);\n let shouldResetLayoutCache =\n reset?.resetFileLayoutCache === true || annotationsChanged;\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 || lineNumber < 1) {\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 top += this.cache.fileAnnotationHeight;\n\n if (overflow === 'scroll' && !this.hasLineAnnotations()) {\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 const { fileAnnotationHeight } = this.cache;\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.hasLineAnnotations()) {\n const { lineHeight } = this.metrics;\n const firstRenderedLineTop =\n headerRegion +\n (firstRenderedLineIndex === 0\n ? fileAnnotationHeight\n : 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 + fileAnnotationHeight + lineIndex * lineHeight,\n };\n }\n\n // Otherwise we gotta iterate through the range\n let top =\n headerRegion +\n (firstRenderedLineIndex === 0\n ? fileAnnotationHeight\n : 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.resetLayoutCache();\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 this.height += this.cache.fileAnnotationHeight;\n\n if (overflow === 'scroll' && !this.hasLineAnnotations()) {\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 lineAnnotations,\n ...props\n }: FileRenderProps<LAnnotation>): boolean {\n const { forceRenderOverride, isSetup } = this;\n this.forceRenderOverride = undefined;\n const annotationsChanged = this.syncLineAnnotations(lineAnnotations);\n if (annotationsChanged) {\n this.resetLayoutCache();\n }\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 lineAnnotations,\n forceRender: (forceRenderOverride ?? forceRender) || annotationsChanged,\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 const { fileAnnotationHeight } = this.cache;\n const codeRegionTop = headerRegion + fileAnnotationHeight;\n const codeRowsHeight = Math.max(\n 0,\n fileHeight - headerRegion - fileAnnotationHeight - paddingBottom\n );\n const hasFileAnnotations = includesFileAnnotations(this.lineAnnotations);\n const fileAnnotationTop = fileTop + headerRegion;\n const measuredFileAnnotationVisible =\n fileAnnotationHeight > 0 &&\n hasFileAnnotations &&\n fileAnnotationTop < bottom &&\n fileAnnotationTop + fileAnnotationHeight > top;\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 // Simple case: overflow scroll with no annotations - pure math!\n if (overflow === 'scroll' && !this.hasLineAnnotations()) {\n const sourceRowsTop = fileTop + codeRegionTop;\n const sourceRowsBottom = sourceRowsTop + codeRowsHeight;\n const sourceRowsVisible =\n sourceRowsTop < bottom && sourceRowsBottom > top;\n if (!measuredFileAnnotationVisible && !sourceRowsVisible) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - paddingBottom,\n };\n }\n\n // Find which line is at viewport center\n const centerLine = Math.floor(\n measuredFileAnnotationVisible &&\n viewportCenter < fileTop + codeRegionTop\n ? 0\n : (viewportCenter - (fileTop + codeRegionTop)) / 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 =\n startingLine === 0\n ? 0\n : fileAnnotationHeight + 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 ?? codeRegionTop);\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] =\n absoluteLineTop - (fileTop + codeRegionTop);\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 if (measuredFileAnnotationVisible) {\n firstVisibleHunk = 0;\n centerHunk = 0;\n } else {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: fileHeight - headerRegion - paddingBottom,\n };\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 codeBufferBefore = hunkOffsets[startHunk] ?? 0;\n const bufferBefore =\n startingLine === 0 ? 0 : fileAnnotationHeight + codeBufferBefore;\n\n // Calculate bufferAfter\n const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;\n const bufferAfter =\n finalHunkIndex < hunkOffsets.length\n ? codeRowsHeight - hunkOffsets[finalHunkIndex]\n : codeRowsHeight - (absoluteLineTop - fileTop - codeRegionTop);\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter: Math.max(0, bufferAfter),\n };\n }\n}\n\nfunction measureFileAnnotationHeight(content: HTMLElement): number | undefined {\n let height: number | undefined;\n for (const child of content.children) {\n if (!(child instanceof HTMLElement)) {\n continue;\n }\n if (child.dataset.lineAnnotation !== FILE_ANNOTATION_DOM_KEY) {\n continue;\n }\n height = Math.max(height ?? 0, child.getBoundingClientRect().height);\n }\n return height;\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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"}