@pierre/diffs 1.2.0-beta.3 → 1.2.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeView.d.ts +18 -10
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +124 -32
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +3 -2
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +13 -4
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +3 -2
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +13 -4
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/FileStream.js +6 -3
- package/dist/components/FileStream.js.map +1 -1
- package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
- package/dist/components/web-components.js +4 -0
- package/dist/components/web-components.js.map +1 -1
- package/dist/constants.d.ts +6 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/managers/InteractionManager.d.ts +22 -10
- package/dist/managers/InteractionManager.d.ts.map +1 -1
- package/dist/managers/InteractionManager.js +223 -28
- package/dist/managers/InteractionManager.js.map +1 -1
- package/dist/react/constants.d.ts.map +1 -1
- package/dist/react/constants.js +3 -1
- package/dist/react/constants.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.d.ts +2 -2
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.d.ts +2 -2
- package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileDiffInstance.js.map +1 -1
- package/dist/react/utils/useFileInstance.d.ts +2 -2
- package/dist/react/utils/useFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useFileInstance.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.d.ts +2 -2
- package/dist/react/utils/useUnresolvedFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +23 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/areSelectionsEqual.d.ts +1 -1
- package/dist/utils/areSelectionsEqual.d.ts.map +1 -1
- package/dist/utils/areSelectionsEqual.js.map +1 -1
- package/dist/utils/createGutterUtilityContentNode.js +1 -0
- package/dist/utils/createGutterUtilityContentNode.js.map +1 -1
- package/dist/utils/cssWrappers.d.ts +3 -2
- package/dist/utils/cssWrappers.d.ts.map +1 -1
- package/dist/utils/cssWrappers.js +13 -2
- package/dist/utils/cssWrappers.js.map +1 -1
- package/dist/utils/getFiletypeFromFileName.js +16 -0
- package/dist/utils/getFiletypeFromFileName.js.map +1 -1
- package/dist/utils/parsePatchFiles.js +15 -0
- package/dist/utils/parsePatchFiles.js.map +1 -1
- package/dist/utils/scrollbarGutter.d.ts +6 -0
- package/dist/utils/scrollbarGutter.d.ts.map +1 -0
- package/dist/utils/scrollbarGutter.js +28 -0
- package/dist/utils/scrollbarGutter.js.map +1 -0
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/worker-portable.js +26 -1
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +23 -0
- package/dist/worker/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsePatchFiles.js","names":["patchMetadata: string | undefined","files: FileDiffMetadata[]","currentFile: FileDiffMetadata | undefined","currentContent: ContextContent | ChangeContent | undefined","lastLineType: 'context' | 'addition' | 'deletion' | undefined","hunkData: Hunk","lastHunkEnd","patches: ParsedPatch[]"],"sources":["../../src/utils/parsePatchFiles.ts"],"sourcesContent":["import {\n ALTERNATE_FILE_NAMES_GIT,\n COMMIT_METADATA_SPLIT,\n FILE_CONTEXT_BLOB,\n FILENAME_HEADER_REGEX,\n FILENAME_HEADER_REGEX_GIT,\n GIT_DIFF_FILE_BREAK_REGEX,\n HUNK_HEADER,\n INDEX_LINE_METADATA,\n SPLIT_WITH_NEWLINES,\n UNIFIED_DIFF_FILE_BREAK_REGEX,\n} from '../constants';\nimport type {\n ChangeContent,\n ContextContent,\n FileContents,\n FileDiffMetadata,\n Hunk,\n ParsedPatch,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { parseLineType } from './parseLineType';\n\nexport function processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch {\n const isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(data);\n const rawFiles = data.split(\n isGitDiff ? GIT_DIFF_FILE_BREAK_REGEX : UNIFIED_DIFF_FILE_BREAK_REGEX\n );\n let patchMetadata: string | undefined;\n const files: FileDiffMetadata[] = [];\n for (const fileOrPatchMetadata of rawFiles) {\n if (isGitDiff && !GIT_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {\n if (patchMetadata == null) {\n patchMetadata = fileOrPatchMetadata;\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n // If we get in here, it's most likely the introductory metadata from the\n // patch, or something is fucked with the diff format\n continue;\n } else if (\n !isGitDiff &&\n !UNIFIED_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)\n ) {\n if (patchMetadata == null) {\n patchMetadata = fileOrPatchMetadata;\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n continue;\n }\n const currentFile = processFile(fileOrPatchMetadata, {\n cacheKey:\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${files.length}`\n : undefined,\n isGitDiff,\n throwOnError,\n });\n if (currentFile != null) {\n files.push(currentFile);\n }\n }\n return { patchMetadata, files };\n}\n\ninterface ProcessFileOptions {\n cacheKey?: string;\n isGitDiff?: boolean;\n oldFile?: FileContents;\n newFile?: FileContents;\n throwOnError?: boolean;\n}\n\nexport function processFile(\n fileDiffString: string,\n {\n cacheKey,\n isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(fileDiffString),\n oldFile,\n newFile,\n throwOnError = false,\n }: ProcessFileOptions = {}\n): FileDiffMetadata | undefined {\n let lastHunkEnd = 0;\n const hunks = fileDiffString.split(FILE_CONTEXT_BLOB);\n let currentFile: FileDiffMetadata | undefined;\n const isPartial = oldFile == null || newFile == null;\n let deletionLineIndex = 0;\n let additionLineIndex = 0;\n for (const hunk of hunks) {\n const lines = hunk.split(SPLIT_WITH_NEWLINES);\n const firstLine = lines.shift();\n if (firstLine == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk');\n } else {\n console.error('parsePatchContent: invalid hunk', hunk);\n }\n continue;\n }\n const fileHeaderMatch = firstLine.match(HUNK_HEADER);\n let additionLines = 0;\n let deletionLines = 0;\n // Setup currentFile, this should be the first iteration of our hunks, and\n // technically not a hunk\n if (fileHeaderMatch == null || currentFile == null) {\n if (currentFile != null) {\n if (throwOnError) {\n throw Error('parsePatchContent: Invalid hunk');\n } else {\n console.error('parsePatchContent: Invalid hunk', hunk);\n }\n continue;\n }\n currentFile = {\n name: '',\n type: 'change',\n hunks: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n isPartial,\n additionLines:\n !isPartial && oldFile != null && newFile != null\n ? newFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n deletionLines:\n !isPartial && oldFile != null && newFile != null\n ? oldFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n cacheKey,\n };\n // If either file is technically empty, then we should empty the\n // arrays respectively\n if (currentFile.additionLines.length === 1 && newFile?.contents === '') {\n currentFile.additionLines.length = 0;\n }\n if (currentFile.deletionLines.length === 1 && oldFile?.contents === '') {\n currentFile.deletionLines.length = 0;\n }\n\n // Push that first line back into the group of lines so we can properly\n // parse it out\n lines.unshift(firstLine);\n for (const line of lines) {\n const filenameMatch = line.match(\n isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX\n );\n if (line.startsWith('diff --git')) {\n const [, , prevName, , name] =\n line.trim().match(ALTERNATE_FILE_NAMES_GIT) ?? [];\n currentFile.name = name.trim();\n if (prevName !== name) {\n currentFile.prevName = prevName.trim();\n }\n } else if (filenameMatch != null) {\n const [, type, fileName] = filenameMatch;\n if (type === '---' && fileName !== '/dev/null') {\n currentFile.prevName = fileName.trim();\n currentFile.name = fileName.trim();\n } else if (type === '+++' && fileName !== '/dev/null') {\n currentFile.name = fileName.trim();\n }\n }\n // Git diffs have a bunch of additional metadata we can pull from\n else if (isGitDiff) {\n if (line.startsWith('new mode ')) {\n currentFile.mode = line.replace('new mode', '').trim();\n }\n if (line.startsWith('old mode ')) {\n currentFile.prevMode = line.replace('old mode', '').trim();\n }\n if (line.startsWith('new file mode')) {\n currentFile.type = 'new';\n currentFile.mode = line.replace('new file mode', '').trim();\n }\n if (line.startsWith('deleted file mode')) {\n currentFile.type = 'deleted';\n currentFile.mode = line.replace('deleted file mode', '').trim();\n }\n if (line.startsWith('similarity index')) {\n if (line.startsWith('similarity index 100%')) {\n currentFile.type = 'rename-pure';\n } else {\n currentFile.type = 'rename-changed';\n }\n }\n if (line.startsWith('index ')) {\n const [, prevObjectId, newObjectId, mode] =\n line.trim().match(INDEX_LINE_METADATA) ?? [];\n if (prevObjectId != null) {\n currentFile.prevObjectId = prevObjectId;\n }\n if (newObjectId != null) {\n currentFile.newObjectId = newObjectId;\n }\n if (mode != null) {\n currentFile.mode = mode;\n }\n }\n // We have to handle these for pure renames because there won't be\n // --- and +++ lines\n if (line.startsWith('rename from ')) {\n currentFile.prevName = line.replace('rename from ', '').trim();\n }\n if (line.startsWith('rename to ')) {\n currentFile.name = line.replace('rename to ', '').trim();\n }\n }\n }\n continue;\n }\n\n // Otherwise, time to start parsing out the hunk\n let currentContent: ContextContent | ChangeContent | undefined;\n let lastLineType: 'context' | 'addition' | 'deletion' | undefined;\n\n // Strip trailing bare newlines (format-patch separators between commits)\n // if needed\n while (\n lines.length > 0 &&\n (lines[lines.length - 1] === '\\n' ||\n lines[lines.length - 1] === '\\r' ||\n lines[lines.length - 1] === '\\r\\n' ||\n lines[lines.length - 1] === '')\n ) {\n lines.pop();\n }\n\n const additionStart = parseInt(fileHeaderMatch[3]);\n const deletionStart = parseInt(fileHeaderMatch[1]);\n deletionLineIndex = isPartial ? deletionLineIndex : deletionStart - 1;\n additionLineIndex = isPartial ? additionLineIndex : additionStart - 1;\n\n const hunkData: Hunk = {\n collapsedBefore: 0,\n\n splitLineCount: 0,\n splitLineStart: 0,\n\n unifiedLineCount: 0,\n unifiedLineStart: 0,\n\n additionCount: parseInt(fileHeaderMatch[4] ?? '1'),\n additionStart,\n additionLines,\n\n deletionCount: parseInt(fileHeaderMatch[2] ?? '1'),\n deletionStart,\n deletionLines,\n\n deletionLineIndex,\n additionLineIndex,\n\n hunkContent: [],\n hunkContext: fileHeaderMatch[5],\n hunkSpecs: firstLine,\n\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n // Lets validate out hunkData to ensure there's no broken data from the\n // regex\n if (\n isNaN(hunkData.additionCount) ||\n isNaN(hunkData.deletionCount) ||\n isNaN(hunkData.additionStart) ||\n isNaN(hunkData.deletionStart)\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk metadata');\n } else {\n console.error('parsePatchContent: invalid hunk metadata', hunkData);\n }\n continue;\n }\n\n // Now we process each line of the hunk\n for (const rawLine of lines) {\n const parsedLine = parseLineType(rawLine);\n // If we can't properly process the line, well, lets just try to salvage\n // things and continue... It's possible an AI generated diff might have\n // some stray blank lines or something in there\n if (parsedLine == null) {\n console.error('processFile: invalid rawLine:', rawLine);\n continue;\n }\n\n const { type, line } = parsedLine;\n if (type === 'addition') {\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n if (isPartial) {\n currentFile.additionLines.push(line);\n }\n currentContent.additions++;\n additionLines++;\n lastLineType = 'addition';\n } else if (type === 'deletion') {\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n deletionLineIndex++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n }\n currentContent.deletions++;\n deletionLines++;\n lastLineType = 'deletion';\n } else if (type === 'context') {\n if (currentContent == null || currentContent.type !== 'context') {\n currentContent = createContentGroup(\n 'context',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n deletionLineIndex++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n currentFile.additionLines.push(line);\n }\n currentContent.lines++;\n lastLineType = 'context';\n } else if (type === 'metadata' && currentContent != null) {\n if (currentContent.type === 'context') {\n hunkData.noEOFCRAdditions = true;\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'deletion') {\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'addition') {\n hunkData.noEOFCRAdditions = true;\n }\n // If we're dealing with partial content from a diff, we need to strip\n // newlines manually from the content\n if (\n isPartial &&\n (lastLineType === 'addition' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.additionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.additionLines[lastIndex] = cleanLastNewline(\n currentFile.additionLines[lastIndex]\n );\n }\n }\n if (\n isPartial &&\n (lastLineType === 'deletion' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.deletionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.deletionLines[lastIndex] = cleanLastNewline(\n currentFile.deletionLines[lastIndex]\n );\n }\n }\n }\n }\n\n hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n hunkData.additionStart - 1 - lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = hunkData.additionStart + hunkData.additionCount - 1;\n for (const content of hunkData.hunkContent) {\n if (content.type === 'context') {\n hunkData.splitLineCount += content.lines;\n hunkData.unifiedLineCount += content.lines;\n } else {\n hunkData.splitLineCount += Math.max(\n content.additions,\n content.deletions\n );\n hunkData.unifiedLineCount += content.deletions + content.additions;\n }\n }\n hunkData.splitLineStart =\n currentFile.splitLineCount + hunkData.collapsedBefore;\n hunkData.unifiedLineStart =\n currentFile.unifiedLineCount + hunkData.collapsedBefore;\n\n currentFile.splitLineCount +=\n hunkData.collapsedBefore + hunkData.splitLineCount;\n currentFile.unifiedLineCount +=\n hunkData.collapsedBefore + hunkData.unifiedLineCount;\n }\n if (currentFile == null) {\n return undefined;\n }\n\n // Account for collapsed lines after the final hunk and increment the\n // split/unified counts properly\n if (\n currentFile.hunks.length > 0 &&\n !isPartial &&\n currentFile.additionLines.length > 0 &&\n currentFile.deletionLines.length > 0\n ) {\n const lastHunk = currentFile.hunks[currentFile.hunks.length - 1];\n const lastHunkEnd = lastHunk.additionStart + lastHunk.additionCount - 1;\n const totalFileLines = currentFile.additionLines.length;\n const collapsedAfter = Math.max(totalFileLines - lastHunkEnd, 0);\n currentFile.splitLineCount += collapsedAfter;\n currentFile.unifiedLineCount += collapsedAfter;\n }\n\n // If this isn't a git diff style patch, then we'll need to sus out some\n // additional metadata manually\n if (!isGitDiff) {\n if (\n currentFile.prevName != null &&\n currentFile.name !== currentFile.prevName\n ) {\n if (currentFile.hunks.length > 0) {\n currentFile.type = 'rename-changed';\n } else {\n currentFile.type = 'rename-pure';\n }\n }\n // Sort of a hack for detecting deleted/added files...\n else if (\n (oldFile == null || oldFile.contents === '') &&\n newFile != null &&\n newFile.contents !== ''\n ) {\n currentFile.type = 'new';\n } else if (\n oldFile != null &&\n oldFile.contents !== '' &&\n (newFile == null || newFile.contents === '')\n ) {\n currentFile.type = 'deleted';\n }\n }\n if (\n currentFile.type !== 'rename-pure' &&\n currentFile.type !== 'rename-changed'\n ) {\n currentFile.prevName = undefined;\n }\n return currentFile;\n}\n\n/**\n * Parses a patch file string into an array of parsed patches.\n *\n * @param data - The raw patch file content (supports multi-commit patches)\n * @param cacheKeyPrefix - Optional prefix for generating cache keys. When provided,\n * each file in the patch will get a cache key in the format `prefix-patchIndex-fileIndex`.\n * This enables caching of rendered diff results in the worker pool.\n */\nexport function parsePatchFiles(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch[] {\n // NOTE(amadeus): This function is pretty forgiving in that it can accept a\n // patch file that includes commit metdata, multiple commits, or not\n const patches: ParsedPatch[] = [];\n for (const patch of data.split(COMMIT_METADATA_SPLIT)) {\n try {\n patches.push(\n processPatch(\n patch,\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${patches.length}`\n : undefined,\n throwOnError\n )\n );\n } catch (error) {\n if (throwOnError) {\n throw error;\n } else {\n console.error(error);\n }\n }\n }\n return patches;\n}\n\nfunction createContentGroup(\n type: 'change',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent;\nfunction createContentGroup(\n type: 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ContextContent;\nfunction createContentGroup(\n type: 'change' | 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent | ContextContent {\n if (type === 'change') {\n return {\n type: 'change',\n additions: 0,\n deletions: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n }\n return {\n type: 'context',\n lines: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parsePatchFiles.js","names":["patchMetadata: string | undefined","files: FileDiffMetadata[]","currentFile: FileDiffMetadata | undefined","currentContent: ContextContent | ChangeContent | undefined","lastLineType: 'context' | 'addition' | 'deletion' | undefined","hunkData: Hunk","lastHunkEnd","patches: ParsedPatch[]"],"sources":["../../src/utils/parsePatchFiles.ts"],"sourcesContent":["import {\n ALTERNATE_FILE_NAMES_GIT,\n COMMIT_METADATA_SPLIT,\n FILE_CONTEXT_BLOB,\n FILENAME_HEADER_REGEX,\n FILENAME_HEADER_REGEX_GIT,\n GIT_DIFF_FILE_BREAK_REGEX,\n HUNK_HEADER,\n INDEX_LINE_METADATA,\n SPLIT_WITH_NEWLINES,\n UNIFIED_DIFF_FILE_BREAK_REGEX,\n} from '../constants';\nimport type {\n ChangeContent,\n ContextContent,\n FileContents,\n FileDiffMetadata,\n Hunk,\n ParsedPatch,\n} from '../types';\nimport { cleanLastNewline } from './cleanLastNewline';\nimport { parseLineType } from './parseLineType';\n\nexport function processPatch(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch {\n const isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(data);\n const rawFiles = data.split(\n isGitDiff ? GIT_DIFF_FILE_BREAK_REGEX : UNIFIED_DIFF_FILE_BREAK_REGEX\n );\n let patchMetadata: string | undefined;\n const files: FileDiffMetadata[] = [];\n for (const fileOrPatchMetadata of rawFiles) {\n if (isGitDiff && !GIT_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)) {\n if (patchMetadata == null) {\n patchMetadata = fileOrPatchMetadata;\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n // If we get in here, it's most likely the introductory metadata from the\n // patch, or something is fucked with the diff format\n continue;\n } else if (\n !isGitDiff &&\n !UNIFIED_DIFF_FILE_BREAK_REGEX.test(fileOrPatchMetadata)\n ) {\n if (patchMetadata == null) {\n patchMetadata = fileOrPatchMetadata;\n } else {\n if (throwOnError) {\n throw Error('parsePatchContent: unknown file blob');\n } else {\n console.error(\n 'parsePatchContent: unknown file blob:',\n fileOrPatchMetadata\n );\n }\n }\n continue;\n }\n const currentFile = processFile(fileOrPatchMetadata, {\n cacheKey:\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${files.length}`\n : undefined,\n isGitDiff,\n throwOnError,\n });\n if (currentFile != null) {\n files.push(currentFile);\n }\n }\n return { patchMetadata, files };\n}\n\ninterface ProcessFileOptions {\n cacheKey?: string;\n isGitDiff?: boolean;\n oldFile?: FileContents;\n newFile?: FileContents;\n throwOnError?: boolean;\n}\n\nexport function processFile(\n fileDiffString: string,\n {\n cacheKey,\n isGitDiff = GIT_DIFF_FILE_BREAK_REGEX.test(fileDiffString),\n oldFile,\n newFile,\n throwOnError = false,\n }: ProcessFileOptions = {}\n): FileDiffMetadata | undefined {\n let lastHunkEnd = 0;\n const hunks = fileDiffString.split(FILE_CONTEXT_BLOB);\n let currentFile: FileDiffMetadata | undefined;\n const isPartial = oldFile == null || newFile == null;\n let deletionLineIndex = 0;\n let additionLineIndex = 0;\n for (const hunk of hunks) {\n const lines = hunk.split(SPLIT_WITH_NEWLINES);\n const firstLine = lines.shift();\n if (firstLine == null) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk');\n } else {\n console.error('parsePatchContent: invalid hunk', hunk);\n }\n continue;\n }\n const fileHeaderMatch = firstLine.match(HUNK_HEADER);\n let additionLines = 0;\n let deletionLines = 0;\n // Setup currentFile, this should be the first iteration of our hunks, and\n // technically not a hunk\n if (fileHeaderMatch == null || currentFile == null) {\n if (currentFile != null) {\n if (throwOnError) {\n throw Error('parsePatchContent: Invalid hunk');\n } else {\n console.error('parsePatchContent: Invalid hunk', hunk);\n }\n continue;\n }\n currentFile = {\n name: '',\n type: 'change',\n hunks: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n isPartial,\n additionLines:\n !isPartial && oldFile != null && newFile != null\n ? newFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n deletionLines:\n !isPartial && oldFile != null && newFile != null\n ? oldFile.contents.split(SPLIT_WITH_NEWLINES)\n : [],\n cacheKey,\n };\n // If either file is technically empty, then we should empty the\n // arrays respectively\n if (currentFile.additionLines.length === 1 && newFile?.contents === '') {\n currentFile.additionLines.length = 0;\n }\n if (currentFile.deletionLines.length === 1 && oldFile?.contents === '') {\n currentFile.deletionLines.length = 0;\n }\n\n // Push that first line back into the group of lines so we can properly\n // parse it out\n lines.unshift(firstLine);\n for (const line of lines) {\n const filenameMatch = line.match(\n isGitDiff ? FILENAME_HEADER_REGEX_GIT : FILENAME_HEADER_REGEX\n );\n if (line.startsWith('diff --git')) {\n const [, , prevName, , name] =\n line.trim().match(ALTERNATE_FILE_NAMES_GIT) ?? [];\n currentFile.name = name.trim();\n if (prevName !== name) {\n currentFile.prevName = prevName.trim();\n }\n } else if (filenameMatch != null) {\n const [, type, fileName] = filenameMatch;\n if (type === '---' && fileName !== '/dev/null') {\n currentFile.prevName = fileName.trim();\n currentFile.name = fileName.trim();\n } else if (type === '+++' && fileName !== '/dev/null') {\n currentFile.name = fileName.trim();\n }\n }\n // Git diffs have a bunch of additional metadata we can pull from\n else if (isGitDiff) {\n if (line.startsWith('new mode ')) {\n currentFile.mode = line.replace('new mode', '').trim();\n }\n if (line.startsWith('old mode ')) {\n currentFile.prevMode = line.replace('old mode', '').trim();\n }\n if (line.startsWith('new file mode')) {\n currentFile.type = 'new';\n currentFile.mode = line.replace('new file mode', '').trim();\n }\n if (line.startsWith('deleted file mode')) {\n currentFile.type = 'deleted';\n currentFile.mode = line.replace('deleted file mode', '').trim();\n }\n if (line.startsWith('similarity index')) {\n if (line.startsWith('similarity index 100%')) {\n currentFile.type = 'rename-pure';\n } else {\n currentFile.type = 'rename-changed';\n }\n }\n if (line.startsWith('index ')) {\n const [, prevObjectId, newObjectId, mode] =\n line.trim().match(INDEX_LINE_METADATA) ?? [];\n if (prevObjectId != null) {\n currentFile.prevObjectId = prevObjectId;\n }\n if (newObjectId != null) {\n currentFile.newObjectId = newObjectId;\n }\n if (mode != null) {\n currentFile.mode = mode;\n }\n }\n // We have to handle these for pure renames because there won't be\n // --- and +++ lines\n if (line.startsWith('rename from ')) {\n currentFile.prevName = line.replace('rename from ', '').trim();\n }\n if (line.startsWith('rename to ')) {\n currentFile.name = line.replace('rename to ', '').trim();\n }\n }\n }\n continue;\n }\n\n // Otherwise, time to start parsing out the hunk\n let currentContent: ContextContent | ChangeContent | undefined;\n let lastLineType: 'context' | 'addition' | 'deletion' | undefined;\n\n // Strip trailing bare newlines (format-patch separators between commits)\n // if needed\n while (\n lines.length > 0 &&\n (lines[lines.length - 1] === '\\n' ||\n lines[lines.length - 1] === '\\r' ||\n lines[lines.length - 1] === '\\r\\n' ||\n lines[lines.length - 1] === '')\n ) {\n lines.pop();\n }\n\n const additionStart = parseInt(fileHeaderMatch[3]);\n const deletionStart = parseInt(fileHeaderMatch[1]);\n deletionLineIndex = isPartial ? deletionLineIndex : deletionStart - 1;\n additionLineIndex = isPartial ? additionLineIndex : additionStart - 1;\n\n const hunkData: Hunk = {\n collapsedBefore: 0,\n\n splitLineCount: 0,\n splitLineStart: 0,\n\n unifiedLineCount: 0,\n unifiedLineStart: 0,\n\n additionCount: parseInt(fileHeaderMatch[4] ?? '1'),\n additionStart,\n additionLines,\n\n deletionCount: parseInt(fileHeaderMatch[2] ?? '1'),\n deletionStart,\n deletionLines,\n\n deletionLineIndex,\n additionLineIndex,\n\n hunkContent: [],\n hunkContext: fileHeaderMatch[5],\n hunkSpecs: firstLine,\n\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n // Lets validate out hunkData to ensure there's no broken data from the\n // regex\n if (\n isNaN(hunkData.additionCount) ||\n isNaN(hunkData.deletionCount) ||\n isNaN(hunkData.additionStart) ||\n isNaN(hunkData.deletionStart)\n ) {\n if (throwOnError) {\n throw Error('parsePatchContent: invalid hunk metadata');\n } else {\n console.error('parsePatchContent: invalid hunk metadata', hunkData);\n }\n continue;\n }\n\n // Now we process each line of the hunk\n let parsedAdditionLines = 0;\n let parsedDeletionLines = 0;\n for (const rawLine of lines) {\n if (\n isHunkBodyComplete({\n additionCount: hunkData.additionCount,\n parsedAdditionLines,\n deletionCount: hunkData.deletionCount,\n parsedDeletionLines,\n }) &&\n !rawLine.startsWith('\\\\')\n ) {\n break;\n }\n\n const parsedLine = parseLineType(rawLine);\n // If we can't properly process the line, well, lets just try to salvage\n // things and continue... It's possible an AI generated diff might have\n // some stray blank lines or something in there\n if (parsedLine == null) {\n console.error('processFile: invalid rawLine:', rawLine);\n continue;\n }\n\n const { type, line } = parsedLine;\n if (type === 'addition') {\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n parsedAdditionLines++;\n if (isPartial) {\n currentFile.additionLines.push(line);\n }\n currentContent.additions++;\n additionLines++;\n lastLineType = 'addition';\n } else if (type === 'deletion') {\n if (currentContent == null || currentContent.type !== 'change') {\n currentContent = createContentGroup(\n 'change',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n deletionLineIndex++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n }\n currentContent.deletions++;\n deletionLines++;\n lastLineType = 'deletion';\n } else if (type === 'context') {\n if (currentContent == null || currentContent.type !== 'context') {\n currentContent = createContentGroup(\n 'context',\n deletionLineIndex,\n additionLineIndex\n );\n hunkData.hunkContent.push(currentContent);\n }\n additionLineIndex++;\n deletionLineIndex++;\n parsedAdditionLines++;\n parsedDeletionLines++;\n if (isPartial) {\n currentFile.deletionLines.push(line);\n currentFile.additionLines.push(line);\n }\n currentContent.lines++;\n lastLineType = 'context';\n } else if (type === 'metadata' && currentContent != null) {\n if (currentContent.type === 'context') {\n hunkData.noEOFCRAdditions = true;\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'deletion') {\n hunkData.noEOFCRDeletions = true;\n } else if (lastLineType === 'addition') {\n hunkData.noEOFCRAdditions = true;\n }\n // If we're dealing with partial content from a diff, we need to strip\n // newlines manually from the content\n if (\n isPartial &&\n (lastLineType === 'addition' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.additionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.additionLines[lastIndex] = cleanLastNewline(\n currentFile.additionLines[lastIndex]\n );\n }\n }\n if (\n isPartial &&\n (lastLineType === 'deletion' || lastLineType === 'context')\n ) {\n const lastIndex = currentFile.deletionLines.length - 1;\n if (lastIndex >= 0) {\n currentFile.deletionLines[lastIndex] = cleanLastNewline(\n currentFile.deletionLines[lastIndex]\n );\n }\n }\n }\n }\n\n hunkData.additionLines = additionLines;\n hunkData.deletionLines = deletionLines;\n\n hunkData.collapsedBefore = Math.max(\n hunkData.additionStart - 1 - lastHunkEnd,\n 0\n );\n currentFile.hunks.push(hunkData);\n lastHunkEnd = hunkData.additionStart + hunkData.additionCount - 1;\n for (const content of hunkData.hunkContent) {\n if (content.type === 'context') {\n hunkData.splitLineCount += content.lines;\n hunkData.unifiedLineCount += content.lines;\n } else {\n hunkData.splitLineCount += Math.max(\n content.additions,\n content.deletions\n );\n hunkData.unifiedLineCount += content.deletions + content.additions;\n }\n }\n hunkData.splitLineStart =\n currentFile.splitLineCount + hunkData.collapsedBefore;\n hunkData.unifiedLineStart =\n currentFile.unifiedLineCount + hunkData.collapsedBefore;\n\n currentFile.splitLineCount +=\n hunkData.collapsedBefore + hunkData.splitLineCount;\n currentFile.unifiedLineCount +=\n hunkData.collapsedBefore + hunkData.unifiedLineCount;\n }\n if (currentFile == null) {\n return undefined;\n }\n\n // Account for collapsed lines after the final hunk and increment the\n // split/unified counts properly\n if (\n currentFile.hunks.length > 0 &&\n !isPartial &&\n currentFile.additionLines.length > 0 &&\n currentFile.deletionLines.length > 0\n ) {\n const lastHunk = currentFile.hunks[currentFile.hunks.length - 1];\n const lastHunkEnd = lastHunk.additionStart + lastHunk.additionCount - 1;\n const totalFileLines = currentFile.additionLines.length;\n const collapsedAfter = Math.max(totalFileLines - lastHunkEnd, 0);\n currentFile.splitLineCount += collapsedAfter;\n currentFile.unifiedLineCount += collapsedAfter;\n }\n\n // If this isn't a git diff style patch, then we'll need to sus out some\n // additional metadata manually\n if (!isGitDiff) {\n if (\n currentFile.prevName != null &&\n currentFile.name !== currentFile.prevName\n ) {\n if (currentFile.hunks.length > 0) {\n currentFile.type = 'rename-changed';\n } else {\n currentFile.type = 'rename-pure';\n }\n }\n // Sort of a hack for detecting deleted/added files...\n else if (\n (oldFile == null || oldFile.contents === '') &&\n newFile != null &&\n newFile.contents !== ''\n ) {\n currentFile.type = 'new';\n } else if (\n oldFile != null &&\n oldFile.contents !== '' &&\n (newFile == null || newFile.contents === '')\n ) {\n currentFile.type = 'deleted';\n }\n }\n if (\n currentFile.type !== 'rename-pure' &&\n currentFile.type !== 'rename-changed'\n ) {\n currentFile.prevName = undefined;\n }\n return currentFile;\n}\n\ninterface isHunkBodyCompleteProps {\n additionCount: number;\n parsedAdditionLines: number;\n deletionCount: number;\n parsedDeletionLines: number;\n}\n\n// Git format-patch trailers follow the final hunk without a new diff marker.\n// Once both header-declared line counts are satisfied, the next non-metadata\n// line belongs to the surrounding patch file rather than the hunk body.\nfunction isHunkBodyComplete({\n additionCount,\n parsedAdditionLines,\n deletionCount,\n parsedDeletionLines,\n}: isHunkBodyCompleteProps): boolean {\n return (\n parsedAdditionLines >= additionCount && parsedDeletionLines >= deletionCount\n );\n}\n\n/**\n * Parses a patch file string into an array of parsed patches.\n *\n * @param data - The raw patch file content (supports multi-commit patches)\n * @param cacheKeyPrefix - Optional prefix for generating cache keys. When provided,\n * each file in the patch will get a cache key in the format `prefix-patchIndex-fileIndex`.\n * This enables caching of rendered diff results in the worker pool.\n */\nexport function parsePatchFiles(\n data: string,\n cacheKeyPrefix?: string,\n throwOnError = false\n): ParsedPatch[] {\n // NOTE(amadeus): This function is pretty forgiving in that it can accept a\n // patch file that includes commit metdata, multiple commits, or not\n const patches: ParsedPatch[] = [];\n for (const patch of data.split(COMMIT_METADATA_SPLIT)) {\n try {\n patches.push(\n processPatch(\n patch,\n cacheKeyPrefix != null\n ? `${cacheKeyPrefix}-${patches.length}`\n : undefined,\n throwOnError\n )\n );\n } catch (error) {\n if (throwOnError) {\n throw error;\n } else {\n console.error(error);\n }\n }\n }\n return patches;\n}\n\nfunction createContentGroup(\n type: 'change',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent;\nfunction createContentGroup(\n type: 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ContextContent;\nfunction createContentGroup(\n type: 'change' | 'context',\n deletionLineIndex: number,\n additionLineIndex: number\n): ChangeContent | ContextContent {\n if (type === 'change') {\n return {\n type: 'change',\n additions: 0,\n deletions: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n }\n return {\n type: 'context',\n lines: 0,\n additionLineIndex,\n deletionLineIndex,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/utils/scrollbarGutter.d.ts
|
|
2
|
+
declare function getMeasuredScrollbarGutter(shadowRoot: ShadowRoot): number | undefined;
|
|
3
|
+
declare function createMeasuredScrollbarGutterDeclaration(scrollbarGutter: number | undefined): string;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { createMeasuredScrollbarGutterDeclaration, getMeasuredScrollbarGutter };
|
|
6
|
+
//# sourceMappingURL=scrollbarGutter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollbarGutter.d.ts","names":["getMeasuredScrollbarGutter","ShadowRoot","createMeasuredScrollbarGutterDeclaration"],"sources":["../../src/utils/scrollbarGutter.d.ts"],"sourcesContent":["export declare function getMeasuredScrollbarGutter(shadowRoot: ShadowRoot): number | undefined;\nexport declare function createMeasuredScrollbarGutterDeclaration(scrollbarGutter: number | undefined): string;\n//# sourceMappingURL=scrollbarGutter.d.ts.map"],"mappings":";iBAAwBA,0BAAAA,aAAuCC;AAAvCD,iBACAE,wCAAAA,CADiD,eAAA,EAAA,MAAA,GAAA,SAAA,CAAA,EAAA,MAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY, DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE } from "../constants.js";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/scrollbarGutter.ts
|
|
4
|
+
let measuredScrollbarGutter;
|
|
5
|
+
function getMeasuredScrollbarGutter(shadowRoot) {
|
|
6
|
+
if (measuredScrollbarGutter != null) return measuredScrollbarGutter;
|
|
7
|
+
const host = shadowRoot.host;
|
|
8
|
+
if (typeof HTMLElement !== "undefined" && host instanceof HTMLElement && !host.isConnected) return;
|
|
9
|
+
const wrapper = document.createElement("div");
|
|
10
|
+
wrapper.setAttribute("data-code", "");
|
|
11
|
+
wrapper.setAttribute(DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE, "true");
|
|
12
|
+
const child = document.createElement("div");
|
|
13
|
+
child.style.position = "relative";
|
|
14
|
+
child.style.width = "200%";
|
|
15
|
+
child.style.height = "200%";
|
|
16
|
+
wrapper.appendChild(child);
|
|
17
|
+
shadowRoot.appendChild(wrapper);
|
|
18
|
+
measuredScrollbarGutter = Math.max(wrapper.offsetHeight - wrapper.clientHeight, 0);
|
|
19
|
+
wrapper.remove();
|
|
20
|
+
return measuredScrollbarGutter;
|
|
21
|
+
}
|
|
22
|
+
function createMeasuredScrollbarGutterDeclaration(scrollbarGutter) {
|
|
23
|
+
return `${DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY}: ${scrollbarGutter == null ? "var(--diffs-scrollbar-gutter-fallback)" : `${scrollbarGutter}px`};`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { createMeasuredScrollbarGutterDeclaration, getMeasuredScrollbarGutter };
|
|
28
|
+
//# sourceMappingURL=scrollbarGutter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollbarGutter.js","names":["measuredScrollbarGutter: number | undefined"],"sources":["../../src/utils/scrollbarGutter.ts"],"sourcesContent":["import {\n DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY,\n DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE,\n} from '../constants';\n\nlet measuredScrollbarGutter: number | undefined;\n\n// Measures the horizontal scrollbar used by `[data-code]` once per page. The\n// hidden probe opts into the same selector as real code panes so custom\n// scrollbar CSS is reflected in the measured value.\nexport function getMeasuredScrollbarGutter(\n shadowRoot: ShadowRoot\n): number | undefined {\n if (measuredScrollbarGutter != null) {\n return measuredScrollbarGutter;\n }\n\n const host = shadowRoot.host;\n if (\n typeof HTMLElement !== 'undefined' &&\n host instanceof HTMLElement &&\n !host.isConnected\n ) {\n return undefined;\n }\n\n const wrapper = document.createElement('div');\n wrapper.setAttribute('data-code', '');\n wrapper.setAttribute(DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE, 'true');\n\n const child = document.createElement('div');\n child.style.position = 'relative';\n child.style.width = '200%';\n child.style.height = '200%';\n wrapper.appendChild(child);\n\n shadowRoot.appendChild(wrapper);\n measuredScrollbarGutter = Math.max(\n wrapper.offsetHeight - wrapper.clientHeight,\n 0\n );\n wrapper.remove();\n return measuredScrollbarGutter;\n}\n\nexport function createMeasuredScrollbarGutterDeclaration(\n scrollbarGutter: number | undefined\n): string {\n const value =\n scrollbarGutter == null\n ? 'var(--diffs-scrollbar-gutter-fallback)'\n : `${scrollbarGutter}px`;\n return `${DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY}: ${value};`;\n}\n"],"mappings":";;;AAKA,IAAIA;AAKJ,SAAgB,2BACd,YACoB;AACpB,KAAI,2BAA2B,KAC7B,QAAO;CAGT,MAAM,OAAO,WAAW;AACxB,KACE,OAAO,gBAAgB,eACvB,gBAAgB,eAChB,CAAC,KAAK,YAEN;CAGF,MAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,SAAQ,aAAa,aAAa,GAAG;AACrC,SAAQ,aAAa,mCAAmC,OAAO;CAE/D,MAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,OAAM,MAAM,WAAW;AACvB,OAAM,MAAM,QAAQ;AACpB,OAAM,MAAM,SAAS;AACrB,SAAQ,YAAY,MAAM;AAE1B,YAAW,YAAY,QAAQ;AAC/B,2BAA0B,KAAK,IAC7B,QAAQ,eAAe,QAAQ,cAC/B,EACD;AACD,SAAQ,QAAQ;AAChB,QAAO;;AAGT,SAAgB,yCACd,iBACQ;AAKR,QAAO,GAAG,yCAAyC,IAHjD,mBAAmB,OACf,2CACA,GAAG,gBAAgB,IACoC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerPoolManager.js","names":["options: WorkerPoolOptions","newRenderOptions: WorkerRenderingOptions","resolvedThemes: ThemeRegistrationResolved[]","taskPromises: Promise<void>[]","task: SetRenderOptionsWorkerTask","resolvedLanguages: ResolvedLanguage[]","initPromises: Promise<unknown>[]","managedWorker: ManagedWorker","task: InitializeWorkerTask","task: RenderFileTask | RenderDiffTask","worker: ManagedWorker | undefined"],"sources":["../../src/worker/WorkerPoolManager.ts"],"sourcesContent":["import LRUMapPkg from 'lru_map';\n\nimport { DEFAULT_THEMES } from '../constants';\nimport { getResolvedLanguages } from '../highlighter/languages/getResolvedLanguages';\nimport { hasResolvedLanguages } from '../highlighter/languages/hasResolvedLanguages';\nimport { resolveLanguages } from '../highlighter/languages/resolveLanguages';\nimport { getSharedHighlighter } from '../highlighter/shared_highlighter';\nimport { attachResolvedThemes } from '../highlighter/themes/attachResolvedThemes';\nimport { getResolvedThemes } from '../highlighter/themes/getResolvedThemes';\nimport { hasResolvedThemes } from '../highlighter/themes/hasResolvedThemes';\nimport { resolveThemes } from '../highlighter/themes/resolveThemes';\nimport type {\n DiffsHighlighter,\n FileContents,\n FileDiffMetadata,\n HighlighterTypes,\n HunkExpansionRegion,\n RenderDiffOptions,\n RenderDiffResult,\n RenderFileOptions,\n RenderFileResult,\n SupportedLanguages,\n ThemedDiffResult,\n ThemedFileResult,\n ThemeRegistrationResolved,\n} from '../types';\nimport { areDiffRenderOptionsEqual } from '../utils/areDiffRenderOptionsEqual';\nimport { areFilesEqual } from '../utils/areFilesEqual';\nimport { areThemesEqual } from '../utils/areThemesEqual';\nimport {\n getCustomExtensionsMap,\n getCustomExtensionsVersion,\n getFiletypeFromFileName,\n} from '../utils/getFiletypeFromFileName';\nimport { getThemes } from '../utils/getThemes';\nimport { isDiffPlainText } from '../utils/isDiffPlainText';\nimport { isFilePlainText } from '../utils/isFilePlainText';\nimport { renderDiffWithHighlighter } from '../utils/renderDiffWithHighlighter';\nimport { renderFileWithHighlighter } from '../utils/renderFileWithHighlighter';\nimport type {\n AllWorkerTasks,\n DiffRendererInstance,\n FileRendererInstance,\n InitializeWorkerRequest,\n InitializeWorkerTask,\n RenderDiffRequest,\n RenderDiffTask,\n RenderFileRequest,\n RenderFileTask,\n ResolvedLanguage,\n SetRenderOptionsWorkerTask,\n SubmitRequest,\n WorkerInitializationRenderOptions,\n WorkerPoolOptions,\n WorkerRenderingOptions,\n WorkerRequestId,\n WorkerResponse,\n WorkerStats,\n} from './types';\n\nconst IGNORE_RESPONSE = Symbol('IGNORE_RESPONSE');\n\nclass WorkerPoolTerminatedError extends Error {\n constructor() {\n super('WorkerPoolManager: operation canceled because the pool terminated');\n }\n}\n\ninterface GetCachesResult {\n fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n}\n\ninterface ManagedWorker {\n worker: Worker;\n request_id: string | undefined;\n initialized: boolean;\n langs: Set<SupportedLanguages>;\n customExtensionsVersion: number;\n}\n\ninterface ThemeSubscriber {\n rerender(): void;\n}\n\nexport class WorkerPoolManager {\n private highlighter: DiffsHighlighter | undefined;\n private readonly preferredHighlighter: HighlighterTypes;\n private renderOptions: WorkerRenderingOptions;\n private initialized: Promise<void> | boolean = false;\n private workers: ManagedWorker[] = [];\n private taskQueue = new Map<\n FileRendererInstance | DiffRendererInstance,\n RenderDiffTask | RenderFileTask\n >();\n private pendingTasks = new Map<WorkerRequestId, AllWorkerTasks>();\n private nextRequestId = 0;\n private themeSubscribers = new Set<ThemeSubscriber>();\n private workersFailed = false;\n private instanceRequestMap = new Map<\n FileRendererInstance | DiffRendererInstance,\n string\n >();\n private statSubscribers = new Set<(stats: WorkerStats) => unknown>();\n private fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n private diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n private _queuedBroadcast: number | undefined;\n // Incremented on terminate so async lifecycle work can identify stale results.\n private lifecycleGeneration = 0;\n\n constructor(\n private options: WorkerPoolOptions,\n {\n langs,\n theme = DEFAULT_THEMES,\n useTokenTransformer = false,\n lineDiffType = 'word-alt',\n maxLineDiffLength = 1000,\n tokenizeMaxLineLength = 1000,\n preferredHighlighter = 'shiki-js',\n }: WorkerInitializationRenderOptions\n ) {\n this.preferredHighlighter = preferredHighlighter;\n this.renderOptions = {\n theme,\n useTokenTransformer,\n lineDiffType,\n maxLineDiffLength,\n tokenizeMaxLineLength,\n };\n this.fileCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n this.diffCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n this.queueInitialization(langs);\n }\n\n isWorkingPool(): boolean {\n return !this.workersFailed;\n }\n\n getFileResultCache(file: FileContents): RenderFileResult | undefined {\n return file.cacheKey != null\n ? this.fileCache.get(file.cacheKey)\n : undefined;\n }\n\n getDiffResultCache(diff: FileDiffMetadata): RenderDiffResult | undefined {\n return diff.cacheKey != null\n ? this.diffCache.get(diff.cacheKey)\n : undefined;\n }\n\n inspectCaches(): GetCachesResult {\n const { fileCache, diffCache } = this;\n return { fileCache, diffCache };\n }\n\n evictFileFromCache(cacheKey: string): boolean {\n try {\n return this.fileCache.delete(cacheKey) !== undefined;\n } finally {\n this.queueBroadcastStateChanges();\n }\n }\n\n evictDiffFromCache(cacheKey: string): boolean {\n try {\n return this.diffCache.delete(cacheKey) !== undefined;\n } finally {\n this.queueBroadcastStateChanges();\n }\n }\n\n async setRenderOptions({\n theme = DEFAULT_THEMES,\n useTokenTransformer = false,\n lineDiffType = 'word-alt',\n maxLineDiffLength = 1000,\n tokenizeMaxLineLength = 1000,\n }: Partial<WorkerRenderingOptions>): Promise<void> {\n const { lifecycleGeneration } = this;\n try {\n const newRenderOptions: WorkerRenderingOptions = {\n theme,\n useTokenTransformer,\n lineDiffType,\n maxLineDiffLength,\n tokenizeMaxLineLength,\n };\n if (!this.isInitialized()) {\n await this.initialize();\n }\n if (\n !this.isCurrentLifecycle(lifecycleGeneration) ||\n areDiffRenderOptionsEqual(newRenderOptions, this.renderOptions)\n ) {\n return;\n }\n\n const themeNames = getThemes(theme);\n let resolvedThemes: ThemeRegistrationResolved[] = [];\n if (!areThemesEqual(newRenderOptions.theme, this.renderOptions.theme)) {\n if (hasResolvedThemes(themeNames)) {\n resolvedThemes = getResolvedThemes(themeNames);\n } else {\n resolvedThemes = await resolveThemes(themeNames);\n }\n }\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n\n if (this.highlighter != null) {\n attachResolvedThemes(resolvedThemes, this.highlighter);\n await this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes);\n } else {\n const [highlighter] = await Promise.all([\n getSharedHighlighter({\n themes: themeNames,\n langs: ['text'],\n preferredHighlighter: this.preferredHighlighter,\n }),\n this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes),\n ]);\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n this.highlighter = highlighter;\n }\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n\n this.renderOptions = newRenderOptions;\n this.diffCache.clear();\n this.fileCache.clear();\n\n for (const instance of this.themeSubscribers) {\n instance.rerender();\n }\n } catch (error) {\n if (\n error instanceof WorkerPoolTerminatedError ||\n !this.isCurrentLifecycle(lifecycleGeneration)\n ) {\n return;\n }\n throw error;\n }\n }\n\n getFileRenderOptions(): RenderFileOptions {\n const { tokenizeMaxLineLength, theme, useTokenTransformer } =\n this.renderOptions;\n return { theme, useTokenTransformer, tokenizeMaxLineLength };\n }\n\n getDiffRenderOptions(): RenderDiffOptions {\n return { ...this.renderOptions };\n }\n\n private async setRenderOptionsOnWorkers(\n renderOptions: WorkerRenderingOptions,\n resolvedThemes: ThemeRegistrationResolved[]\n ): Promise<void> {\n if (this.workersFailed) {\n return;\n }\n if (!this.isInitialized()) {\n await this.initialize();\n }\n const taskPromises: Promise<void>[] = [];\n for (const managedWorker of this.workers) {\n if (!managedWorker.initialized) {\n console.log({ managedWorker });\n throw new Error(\n 'setRenderOptionsOnWorkers: Somehow we have an uninitialized worker'\n );\n }\n taskPromises.push(\n new Promise<void>((resolve, reject) => {\n const id = this.generateRequestId();\n const task: SetRenderOptionsWorkerTask = {\n type: 'set-render-options',\n id,\n request: {\n type: 'set-render-options',\n id,\n renderOptions,\n resolvedThemes,\n },\n resolve,\n reject,\n requestStart: Date.now(),\n };\n // NOTE(amadeus): We intentionally ignore the normal pending requests\n // infra because these tasks should technically interrupt the normal\n // flow and should be processed by the worker when ready immediately\n this.pendingTasks.set(id, task);\n managedWorker.worker.postMessage(task.request);\n })\n );\n }\n await Promise.all(taskPromises);\n }\n\n subscribeToThemeChanges(instance: ThemeSubscriber): () => void {\n this.themeSubscribers.add(instance);\n this.queueBroadcastStateChanges();\n return () => {\n this.unsubscribeToThemeChanges(instance);\n this.queueBroadcastStateChanges();\n };\n }\n\n unsubscribeToThemeChanges(instance: ThemeSubscriber): void {\n this.themeSubscribers.delete(instance);\n this.queueBroadcastStateChanges();\n }\n\n subscribeToStatChanges(\n callback: (stats: WorkerStats) => unknown\n ): () => void {\n this.statSubscribers.add(callback);\n callback(this.getStats());\n return () => {\n this.statSubscribers.delete(callback);\n };\n }\n\n private queueBroadcastStateChanges() {\n if (this._queuedBroadcast != null) return;\n this._queuedBroadcast = requestAnimationFrame(this._broadcastStateChanges);\n }\n\n private _broadcastStateChanges = () => {\n if (this._queuedBroadcast != null) {\n cancelAnimationFrame(this._queuedBroadcast);\n this._queuedBroadcast = undefined;\n }\n const stats = this.getStats();\n for (const callback of this.statSubscribers) {\n callback(stats);\n }\n };\n\n cleanUpPendingTasks(\n instance: FileRendererInstance | DiffRendererInstance\n ): void {\n this.taskQueue.delete(instance);\n const requestId = this.instanceRequestMap.get(instance);\n if (requestId != null) {\n this.pendingTasks.delete(requestId);\n this.instanceRequestMap.delete(instance);\n }\n this.queueBroadcastStateChanges();\n }\n\n isInitialized(): boolean {\n return this.initialized === true;\n }\n\n async initialize(languages: SupportedLanguages[] = []): Promise<void> {\n if (this.initialized === true) {\n return;\n } else if (this.initialized === false) {\n const { lifecycleGeneration } = this;\n this.initialized = new Promise((resolve, reject) => {\n void (async () => {\n try {\n const themes = getThemes(this.renderOptions.theme);\n let resolvedThemes: ThemeRegistrationResolved[] = [];\n if (hasResolvedThemes(themes)) {\n resolvedThemes = getResolvedThemes(themes);\n } else {\n resolvedThemes = await resolveThemes(themes);\n }\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n resolve();\n return;\n }\n\n let resolvedLanguages: ResolvedLanguage[] = [];\n if (hasResolvedLanguages(languages)) {\n resolvedLanguages = getResolvedLanguages(languages);\n } else {\n resolvedLanguages = await resolveLanguages(languages);\n }\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n resolve();\n return;\n }\n\n const [highlighter] = await Promise.all([\n getSharedHighlighter({\n themes,\n langs: ['text', ...languages],\n preferredHighlighter: this.preferredHighlighter,\n }),\n this.initializeWorkers(resolvedThemes, resolvedLanguages),\n ]);\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n this.terminateWorkers();\n resolve();\n return;\n }\n this.highlighter = highlighter;\n this.initialized = true;\n this.diffCache.clear();\n this.fileCache.clear();\n this.drainQueue();\n this.queueBroadcastStateChanges();\n resolve();\n } catch (e) {\n if (\n e instanceof WorkerPoolTerminatedError ||\n !this.isCurrentLifecycle(lifecycleGeneration)\n ) {\n resolve();\n return;\n }\n this.initialized = false;\n this.workersFailed = true;\n this.queueBroadcastStateChanges();\n reject(e);\n }\n })();\n });\n this.queueBroadcastStateChanges();\n } else {\n return this.initialized;\n }\n }\n\n private async initializeWorkers(\n resolvedThemes: ThemeRegistrationResolved[],\n resolvedLanguages: ResolvedLanguage[]\n ): Promise<void> {\n this.workersFailed = false;\n const initPromises: Promise<unknown>[] = [];\n const customExtensionVersion = getCustomExtensionsVersion();\n const customExtensionMap =\n customExtensionVersion > 0 ? getCustomExtensionsMap() : undefined;\n if (this.workers.length > 0) {\n this.terminateWorkers();\n }\n for (let i = 0; i < (this.options.poolSize ?? 8); i++) {\n const worker = this.options.workerFactory();\n const managedWorker: ManagedWorker = {\n worker,\n request_id: undefined,\n initialized: false,\n langs: new Set(['text', ...resolvedLanguages.map(({ name }) => name)]),\n customExtensionsVersion: 0,\n };\n worker.addEventListener(\n 'message',\n (event: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(managedWorker, event.data);\n }\n );\n worker.addEventListener('error', (error) =>\n console.error('Worker error:', error, managedWorker)\n );\n this.workers.push(managedWorker);\n initPromises.push(\n new Promise<void>((resolve, reject) => {\n const id = this.generateRequestId();\n const task: InitializeWorkerTask = {\n type: 'initialize',\n id,\n request: {\n type: 'initialize',\n id,\n renderOptions: this.renderOptions,\n preferredHighlighter: this.preferredHighlighter,\n resolvedThemes,\n resolvedLanguages,\n customExtensionsVersion:\n customExtensionMap != null ? customExtensionVersion : undefined,\n customExtensionMap,\n },\n resolve() {\n managedWorker.initialized = true;\n resolve();\n },\n reject,\n requestStart: Date.now(),\n };\n this.pendingTasks.set(id, task);\n this.executeTask(managedWorker, task);\n })\n );\n }\n await Promise.all(initPromises);\n }\n\n private drainQueue = () => {\n this._queuedDrain = undefined;\n // If we are initializing or things got cancelled while initializing, we\n // should not attempt to drain the queue\n if (this.initialized !== true || this.taskQueue.size === 0) {\n return;\n }\n for (const [instance, task] of this.taskQueue) {\n // If we have a request in progress for the same instance, we should wait\n // for it to finish\n if (this.instanceRequestMap.has(instance)) {\n continue;\n }\n const langs = getLangsFromTask(task);\n const availableWorker = this.getAvailableWorker(langs);\n if (availableWorker == null) {\n break;\n }\n this.assignWorkerToTask(task, availableWorker);\n void this.resolveLanguagesAndExecuteTask(availableWorker, task, langs);\n }\n this.queueBroadcastStateChanges();\n };\n\n highlightFileAST(instance: FileRendererInstance, file: FileContents): void {\n if (isFilePlainText(file)) {\n return;\n }\n // If we already have a task in progress for this same file content, we\n // should drop it\n for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n for (const task of tasks) {\n if (\n 'instance' in task &&\n task.instance === instance &&\n task.request.type === 'file' &&\n areFilesEqual(file, task.request.file)\n ) {\n return;\n }\n }\n }\n this.submitTask(instance, { type: 'file', file });\n }\n\n getPlainFileAST(\n file: FileContents,\n startingLine: number,\n totalLines: number,\n lines?: string[]\n ): ThemedFileResult | undefined {\n if (this.highlighter == null) {\n this.queueInitialization();\n return undefined;\n }\n return renderFileWithHighlighter(\n file,\n this.highlighter,\n this.renderOptions,\n { forcePlainText: true, startingLine, totalLines, lines }\n );\n }\n\n highlightDiffAST(\n instance: DiffRendererInstance,\n diff: FileDiffMetadata\n ): void {\n if (isDiffPlainText(diff)) {\n return;\n }\n // If we already have a task in progress for this same diff content, we\n // should ignore executing it again\n for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n for (const task of tasks) {\n if (\n 'instance' in task &&\n task.instance === instance &&\n task.request.type === 'diff' &&\n task.request.diff === diff\n ) {\n return;\n }\n }\n }\n this.submitTask(instance, { type: 'diff', diff });\n }\n\n getPlainDiffAST(\n diff: FileDiffMetadata,\n startingLine: number,\n totalLines: number,\n expandedHunks?: Map<number, HunkExpansionRegion> | true,\n collapsedContextThreshold?: number\n ): ThemedDiffResult | undefined {\n return this.highlighter != null\n ? renderDiffWithHighlighter(diff, this.highlighter, this.renderOptions, {\n forcePlainText: true,\n startingLine,\n totalLines,\n expandedHunks,\n collapsedContextThreshold,\n })\n : undefined;\n }\n\n terminate(): void {\n this.lifecycleGeneration++;\n this.cancelPendingAsyncWorkerTasks();\n this.terminateWorkers();\n this.fileCache.clear();\n this.diffCache.clear();\n this.instanceRequestMap.clear();\n this.taskQueue.clear();\n this.pendingTasks.clear();\n this.highlighter = undefined;\n this.initialized = false;\n this.workersFailed = false;\n this.queueBroadcastStateChanges();\n }\n\n private isCurrentLifecycle(lifecycleGeneration: number): boolean {\n return this.lifecycleGeneration === lifecycleGeneration;\n }\n\n private queueInitialization(languages?: SupportedLanguages[]): void {\n void this.initialize(languages).catch((error) => {\n console.error(error);\n });\n }\n\n private cancelPendingAsyncWorkerTasks(): void {\n const error = new WorkerPoolTerminatedError();\n for (const task of this.pendingTasks.values()) {\n if ('reject' in task) {\n task.reject(error);\n }\n }\n }\n\n private terminateWorkers() {\n for (const managedWorker of this.workers) {\n managedWorker.worker.terminate();\n }\n this.workers.length = 0;\n }\n\n getStats(): WorkerStats {\n return {\n managerState: (() => {\n if (this.initialized === false) {\n return 'waiting';\n }\n if (this.initialized !== true) {\n return 'initializing';\n }\n return 'initialized';\n })(),\n totalWorkers: this.workers.length,\n workersFailed: this.workersFailed,\n busyWorkers: this.workers.filter((w) => w.request_id != null).length,\n queuedTasks: this.taskQueue.size,\n pendingTasks: this.pendingTasks.size,\n themeSubscribers: this.themeSubscribers.size,\n fileCacheSize: this.fileCache.size,\n diffCacheSize: this.diffCache.size,\n };\n }\n\n private submitTask(\n instance: FileRendererInstance,\n request: Omit<RenderFileRequest, 'id'>\n ): void;\n private submitTask(\n instance: DiffRendererInstance,\n request: Omit<RenderDiffRequest, 'id'>\n ): void;\n private submitTask(\n instance: FileRendererInstance | DiffRendererInstance,\n request: SubmitRequest\n ): void {\n if (this.initialized === false) {\n this.queueInitialization();\n }\n\n const id = this.generateRequestId();\n const requestStart = Date.now();\n const task: RenderFileTask | RenderDiffTask = (() => {\n switch (request.type) {\n case 'file':\n return {\n type: 'file',\n id,\n request: { ...request, id },\n instance: instance as FileRendererInstance,\n requestStart,\n };\n case 'diff':\n return {\n type: 'diff',\n id,\n request: { ...request, id },\n instance: instance as DiffRendererInstance,\n requestStart,\n };\n }\n })();\n\n this.taskQueue.set(instance, task);\n this.queueDrain();\n }\n\n private async resolveLanguagesAndExecuteTask(\n availableWorker: ManagedWorker,\n task: RenderFileTask | RenderDiffTask,\n langs: SupportedLanguages[]\n ): Promise<void> {\n try {\n // Add resolved languages if required\n const workerMissingLangs = langs.filter(\n (lang) => !availableWorker.langs.has(lang)\n );\n\n if (workerMissingLangs.length > 0) {\n if (hasResolvedLanguages(workerMissingLangs)) {\n task.request.resolvedLanguages =\n getResolvedLanguages(workerMissingLangs);\n } else {\n task.request.resolvedLanguages =\n await resolveLanguages(workerMissingLangs);\n }\n }\n this.executeTask(availableWorker, task);\n } catch {\n this.cleanWorkerAndTask(availableWorker, task);\n }\n }\n\n private handleWorkerMessage(\n managedWorker: ManagedWorker,\n response: WorkerResponse\n ): void {\n const task = this.pendingTasks.get(response.id);\n try {\n if (task == null) {\n // If we can't find a task for this response, it probably means the\n // component has been unmounted, so we should silently ignore it\n throw IGNORE_RESPONSE;\n } else if (response.type === 'error') {\n const error = new Error(response.error);\n if (response.stack) {\n error.stack = response.stack;\n }\n if ('reject' in task) {\n task.reject(error);\n } else {\n task.instance.onHighlightError(error);\n }\n throw error;\n } else {\n // If we've gotten a newer request from the same instance, we should\n // ignore this response either because it's out of order or because we\n // have a newer more important request\n if (\n 'instance' in task &&\n this.instanceRequestMap.get(task.instance) !== response.id\n ) {\n throw IGNORE_RESPONSE;\n }\n switch (response.requestType) {\n case 'initialize':\n if (task.type !== 'initialize') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n this.syncCustomExtensionVersion(managedWorker, task.request);\n task.resolve();\n break;\n case 'set-render-options':\n if (task.type !== 'set-render-options') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n task.resolve();\n break;\n case 'file': {\n if (task.type !== 'file') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n const { result, options } = response;\n const { instance, request } = task;\n this.syncCustomExtensionVersion(managedWorker, request);\n if (request.file.cacheKey != null) {\n this.fileCache.set(request.file.cacheKey, { result, options });\n }\n instance.onHighlightSuccess(request.file, result, options);\n break;\n }\n case 'diff': {\n if (task.type !== 'diff') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n const { result, options } = response;\n const { instance, request } = task;\n this.syncCustomExtensionVersion(managedWorker, request);\n if (request.diff.cacheKey != null) {\n this.diffCache.set(request.diff.cacheKey, { result, options });\n }\n instance.onHighlightSuccess(request.diff, result, options);\n break;\n }\n }\n }\n } catch (error) {\n if (error !== IGNORE_RESPONSE) {\n console.error(error, task, response);\n }\n }\n\n this.cleanWorkerAndTask(managedWorker, task);\n this.queueBroadcastStateChanges();\n if (this.taskQueue.size > 0) {\n // We queue drain so that potentially multiple workers can free up\n // allowing for better language matches if possible\n this.queueDrain();\n }\n }\n\n private _queuedDrain: Promise<void> | undefined;\n private queueDrain() {\n if (this._queuedDrain != null) return;\n this._queuedDrain = Promise.resolve().then(this.drainQueue);\n this.queueBroadcastStateChanges();\n }\n\n private assignWorkerToTask(\n task: AllWorkerTasks,\n managedWorker: ManagedWorker\n ) {\n managedWorker.request_id = task.id;\n if ('instance' in task) {\n this.taskQueue.delete(task.instance);\n this.instanceRequestMap.set(task.instance, task.id);\n }\n this.pendingTasks.set(task.id, task);\n }\n\n private cleanWorkerAndTask(\n managedWorker: ManagedWorker,\n task?: AllWorkerTasks\n ) {\n managedWorker.request_id = undefined;\n if (task != null) {\n if ('instance' in task) {\n this.instanceRequestMap.delete(task.instance);\n }\n this.pendingTasks.delete(task.id);\n }\n }\n\n private executeTask(\n managedWorker: ManagedWorker,\n task: AllWorkerTasks\n ): void {\n if (shouldSyncCustomExtensions(task.request)) {\n this.maybeAttachCustomExtensions(managedWorker, task.request);\n }\n this.assignWorkerToTask(task, managedWorker);\n for (const lang of getLangsFromTask(task)) {\n managedWorker.langs.add(lang);\n }\n try {\n managedWorker.worker.postMessage(task.request);\n } catch (error) {\n this.cleanWorkerAndTask(managedWorker, task);\n console.error('Failed to post message to worker:', error);\n if ('instance' in task) {\n task.instance.onHighlightError(error);\n } else if ('reject' in task) {\n task.reject(error as Error);\n }\n }\n this.queueBroadcastStateChanges();\n }\n\n private maybeAttachCustomExtensions(\n managedWorker: ManagedWorker,\n request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n ): void {\n if (request.customExtensionsVersion != null) {\n return;\n }\n const version = getCustomExtensionsVersion();\n if (managedWorker.customExtensionsVersion >= version) {\n return;\n }\n request.customExtensionsVersion = version;\n request.customExtensionMap = getCustomExtensionsMap();\n }\n\n private syncCustomExtensionVersion(\n managedWorker: ManagedWorker,\n request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n ): void {\n if (request.customExtensionsVersion == null) {\n return;\n }\n managedWorker.customExtensionsVersion = request.customExtensionsVersion;\n }\n\n private getAvailableWorker(\n langs: SupportedLanguages[]\n ): ManagedWorker | undefined {\n let worker: ManagedWorker | undefined;\n for (const managedWorker of this.workers) {\n if (managedWorker.request_id != null || !managedWorker.initialized) {\n continue;\n }\n worker = managedWorker;\n if (langs.length === 0) {\n break;\n }\n let hasEveryLang = true;\n for (const lang of langs) {\n if (!managedWorker.langs.has(lang)) {\n hasEveryLang = false;\n break;\n }\n }\n if (hasEveryLang) {\n break;\n }\n }\n return worker;\n }\n\n private generateRequestId(): WorkerRequestId {\n return `req_${++this.nextRequestId}`;\n }\n}\n\nfunction shouldSyncCustomExtensions(\n request: AllWorkerTasks['request']\n): request is InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest {\n return (\n request.type === 'initialize' ||\n request.type === 'file' ||\n request.type === 'diff'\n );\n}\n\nfunction getLangsFromTask(task: AllWorkerTasks): SupportedLanguages[] {\n const langs = new Set<SupportedLanguages>();\n if (task.type === 'initialize' || task.type === 'set-render-options') {\n return [];\n }\n switch (task.type) {\n case 'file': {\n langs.add(\n task.request.file.lang ??\n getFiletypeFromFileName(task.request.file.name)\n );\n break;\n }\n case 'diff': {\n langs.add(\n task.request.diff.lang ??\n getFiletypeFromFileName(task.request.diff.name)\n );\n langs.add(\n task.request.diff.lang ??\n getFiletypeFromFileName(task.request.diff.prevName ?? '-')\n );\n break;\n }\n }\n langs.delete('text');\n return Array.from(langs);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkerPoolManager.js","names":["options: WorkerPoolOptions","newRenderOptions: WorkerRenderingOptions","resolvedThemes: ThemeRegistrationResolved[]","taskPromises: Promise<void>[]","task: SetRenderOptionsWorkerTask","resolvedLanguages: ResolvedLanguage[]","initPromises: Promise<unknown>[]","managedWorker: ManagedWorker","task: InitializeWorkerTask","task: RenderFileTask | RenderDiffTask","worker: ManagedWorker | undefined"],"sources":["../../src/worker/WorkerPoolManager.ts"],"sourcesContent":["import LRUMapPkg from 'lru_map';\n\nimport { DEFAULT_THEMES } from '../constants';\nimport { getResolvedLanguages } from '../highlighter/languages/getResolvedLanguages';\nimport { hasResolvedLanguages } from '../highlighter/languages/hasResolvedLanguages';\nimport { resolveLanguages } from '../highlighter/languages/resolveLanguages';\nimport { getSharedHighlighter } from '../highlighter/shared_highlighter';\nimport { attachResolvedThemes } from '../highlighter/themes/attachResolvedThemes';\nimport { getResolvedThemes } from '../highlighter/themes/getResolvedThemes';\nimport { hasResolvedThemes } from '../highlighter/themes/hasResolvedThemes';\nimport { resolveThemes } from '../highlighter/themes/resolveThemes';\nimport type {\n DiffsHighlighter,\n FileContents,\n FileDiffMetadata,\n HighlighterTypes,\n HunkExpansionRegion,\n RenderDiffOptions,\n RenderDiffResult,\n RenderFileOptions,\n RenderFileResult,\n SupportedLanguages,\n ThemedDiffResult,\n ThemedFileResult,\n ThemeRegistrationResolved,\n} from '../types';\nimport { areDiffRenderOptionsEqual } from '../utils/areDiffRenderOptionsEqual';\nimport { areFilesEqual } from '../utils/areFilesEqual';\nimport { areThemesEqual } from '../utils/areThemesEqual';\nimport {\n getCustomExtensionsMap,\n getCustomExtensionsVersion,\n getFiletypeFromFileName,\n} from '../utils/getFiletypeFromFileName';\nimport { getThemes } from '../utils/getThemes';\nimport { isDiffPlainText } from '../utils/isDiffPlainText';\nimport { isFilePlainText } from '../utils/isFilePlainText';\nimport { renderDiffWithHighlighter } from '../utils/renderDiffWithHighlighter';\nimport { renderFileWithHighlighter } from '../utils/renderFileWithHighlighter';\nimport type {\n AllWorkerTasks,\n DiffRendererInstance,\n FileRendererInstance,\n InitializeWorkerRequest,\n InitializeWorkerTask,\n RenderDiffRequest,\n RenderDiffTask,\n RenderFileRequest,\n RenderFileTask,\n ResolvedLanguage,\n SetRenderOptionsWorkerTask,\n SubmitRequest,\n WorkerInitializationRenderOptions,\n WorkerPoolOptions,\n WorkerRenderingOptions,\n WorkerRequestId,\n WorkerResponse,\n WorkerStats,\n} from './types';\n\nconst IGNORE_RESPONSE = Symbol('IGNORE_RESPONSE');\n\nclass WorkerPoolTerminatedError extends Error {\n constructor() {\n super('WorkerPoolManager: operation canceled because the pool terminated');\n }\n}\n\ninterface GetCachesResult {\n fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n}\n\ninterface ManagedWorker {\n worker: Worker;\n request_id: string | undefined;\n initialized: boolean;\n langs: Set<SupportedLanguages>;\n customExtensionsVersion: number;\n}\n\ninterface ThemeSubscriber {\n rerender(): void;\n}\n\nexport class WorkerPoolManager {\n private highlighter: DiffsHighlighter | undefined;\n private readonly preferredHighlighter: HighlighterTypes;\n private renderOptions: WorkerRenderingOptions;\n private initialized: Promise<void> | boolean = false;\n private workers: ManagedWorker[] = [];\n private taskQueue = new Map<\n FileRendererInstance | DiffRendererInstance,\n RenderDiffTask | RenderFileTask\n >();\n private pendingTasks = new Map<WorkerRequestId, AllWorkerTasks>();\n private nextRequestId = 0;\n private themeSubscribers = new Set<ThemeSubscriber>();\n private workersFailed = false;\n private instanceRequestMap = new Map<\n FileRendererInstance | DiffRendererInstance,\n string\n >();\n private statSubscribers = new Set<(stats: WorkerStats) => unknown>();\n private fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n private diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n private _queuedBroadcast: number | undefined;\n // Incremented on terminate so async lifecycle work can identify stale results.\n private lifecycleGeneration = 0;\n\n constructor(\n private options: WorkerPoolOptions,\n {\n langs,\n theme = DEFAULT_THEMES,\n useTokenTransformer = false,\n lineDiffType = 'word-alt',\n maxLineDiffLength = 1000,\n tokenizeMaxLineLength = 1000,\n preferredHighlighter = 'shiki-js',\n }: WorkerInitializationRenderOptions\n ) {\n this.preferredHighlighter = preferredHighlighter;\n this.renderOptions = {\n theme,\n useTokenTransformer,\n lineDiffType,\n maxLineDiffLength,\n tokenizeMaxLineLength,\n };\n this.fileCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n this.diffCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n this.queueInitialization(langs);\n }\n\n public isWorkingPool(): boolean {\n return !this.workersFailed;\n }\n\n public getFileResultCache(file: FileContents): RenderFileResult | undefined {\n return file.cacheKey != null\n ? this.fileCache.get(file.cacheKey)\n : undefined;\n }\n\n public getDiffResultCache(\n diff: FileDiffMetadata\n ): RenderDiffResult | undefined {\n return diff.cacheKey != null\n ? this.diffCache.get(diff.cacheKey)\n : undefined;\n }\n\n public inspectCaches(): GetCachesResult {\n const { fileCache, diffCache } = this;\n return { fileCache, diffCache };\n }\n\n public evictFileFromCache(cacheKey: string): boolean {\n try {\n return this.fileCache.delete(cacheKey) !== undefined;\n } finally {\n this.queueBroadcastStateChanges();\n }\n }\n\n public evictDiffFromCache(cacheKey: string): boolean {\n try {\n return this.diffCache.delete(cacheKey) !== undefined;\n } finally {\n this.queueBroadcastStateChanges();\n }\n }\n\n async setRenderOptions({\n theme = DEFAULT_THEMES,\n useTokenTransformer = false,\n lineDiffType = 'word-alt',\n maxLineDiffLength = 1000,\n tokenizeMaxLineLength = 1000,\n }: Partial<WorkerRenderingOptions>): Promise<void> {\n const { lifecycleGeneration } = this;\n try {\n const newRenderOptions: WorkerRenderingOptions = {\n theme,\n useTokenTransformer,\n lineDiffType,\n maxLineDiffLength,\n tokenizeMaxLineLength,\n };\n if (!this.isInitialized()) {\n await this.initialize();\n }\n if (\n !this.isCurrentLifecycle(lifecycleGeneration) ||\n areDiffRenderOptionsEqual(newRenderOptions, this.renderOptions)\n ) {\n return;\n }\n\n const themeNames = getThemes(theme);\n let resolvedThemes: ThemeRegistrationResolved[] = [];\n if (!areThemesEqual(newRenderOptions.theme, this.renderOptions.theme)) {\n if (hasResolvedThemes(themeNames)) {\n resolvedThemes = getResolvedThemes(themeNames);\n } else {\n resolvedThemes = await resolveThemes(themeNames);\n }\n }\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n\n if (this.highlighter != null) {\n attachResolvedThemes(resolvedThemes, this.highlighter);\n await this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes);\n } else {\n const [highlighter] = await Promise.all([\n getSharedHighlighter({\n themes: themeNames,\n langs: ['text'],\n preferredHighlighter: this.preferredHighlighter,\n }),\n this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes),\n ]);\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n this.highlighter = highlighter;\n }\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n return;\n }\n\n this.renderOptions = newRenderOptions;\n this.diffCache.clear();\n this.fileCache.clear();\n\n for (const instance of this.themeSubscribers) {\n instance.rerender();\n }\n } catch (error) {\n if (\n error instanceof WorkerPoolTerminatedError ||\n !this.isCurrentLifecycle(lifecycleGeneration)\n ) {\n return;\n }\n throw error;\n }\n }\n\n public getFileRenderOptions(): RenderFileOptions {\n const { tokenizeMaxLineLength, theme, useTokenTransformer } =\n this.renderOptions;\n return { theme, useTokenTransformer, tokenizeMaxLineLength };\n }\n\n public getDiffRenderOptions(): RenderDiffOptions {\n return { ...this.renderOptions };\n }\n\n private async setRenderOptionsOnWorkers(\n renderOptions: WorkerRenderingOptions,\n resolvedThemes: ThemeRegistrationResolved[]\n ): Promise<void> {\n if (this.workersFailed) {\n return;\n }\n if (!this.isInitialized()) {\n await this.initialize();\n }\n const taskPromises: Promise<void>[] = [];\n for (const managedWorker of this.workers) {\n if (!managedWorker.initialized) {\n console.log({ managedWorker });\n throw new Error(\n 'setRenderOptionsOnWorkers: Somehow we have an uninitialized worker'\n );\n }\n taskPromises.push(\n new Promise<void>((resolve, reject) => {\n const id = this.generateRequestId();\n const task: SetRenderOptionsWorkerTask = {\n type: 'set-render-options',\n id,\n request: {\n type: 'set-render-options',\n id,\n renderOptions,\n resolvedThemes,\n },\n resolve,\n reject,\n requestStart: Date.now(),\n };\n // NOTE(amadeus): We intentionally ignore the normal pending requests\n // infra because these tasks should technically interrupt the normal\n // flow and should be processed by the worker when ready immediately\n this.pendingTasks.set(id, task);\n managedWorker.worker.postMessage(task.request);\n })\n );\n }\n await Promise.all(taskPromises);\n }\n\n public subscribeToThemeChanges(instance: ThemeSubscriber): () => void {\n this.themeSubscribers.add(instance);\n this.queueBroadcastStateChanges();\n return () => {\n this.unsubscribeToThemeChanges(instance);\n this.queueBroadcastStateChanges();\n };\n }\n\n public unsubscribeToThemeChanges(instance: ThemeSubscriber): void {\n this.themeSubscribers.delete(instance);\n this.queueBroadcastStateChanges();\n }\n\n public subscribeToStatChanges(\n callback: (stats: WorkerStats) => unknown\n ): () => void {\n this.statSubscribers.add(callback);\n callback(this.getStats());\n return () => {\n this.statSubscribers.delete(callback);\n };\n }\n\n private queueBroadcastStateChanges() {\n if (this._queuedBroadcast != null) return;\n this._queuedBroadcast = requestAnimationFrame(this._broadcastStateChanges);\n }\n\n private _broadcastStateChanges = () => {\n if (this._queuedBroadcast != null) {\n cancelAnimationFrame(this._queuedBroadcast);\n this._queuedBroadcast = undefined;\n }\n const stats = this.getStats();\n for (const callback of this.statSubscribers) {\n callback(stats);\n }\n };\n\n public cleanUpPendingTasks(\n instance: FileRendererInstance | DiffRendererInstance\n ): void {\n this.taskQueue.delete(instance);\n const requestId = this.instanceRequestMap.get(instance);\n if (requestId != null) {\n this.pendingTasks.delete(requestId);\n this.instanceRequestMap.delete(instance);\n }\n this.queueBroadcastStateChanges();\n }\n\n public isInitialized(): boolean {\n return this.initialized === true;\n }\n\n public async initialize(languages: SupportedLanguages[] = []): Promise<void> {\n if (this.initialized === true) {\n return;\n } else if (this.initialized === false) {\n const { lifecycleGeneration } = this;\n this.initialized = new Promise((resolve, reject) => {\n void (async () => {\n try {\n const themes = getThemes(this.renderOptions.theme);\n let resolvedThemes: ThemeRegistrationResolved[] = [];\n if (hasResolvedThemes(themes)) {\n resolvedThemes = getResolvedThemes(themes);\n } else {\n resolvedThemes = await resolveThemes(themes);\n }\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n resolve();\n return;\n }\n\n let resolvedLanguages: ResolvedLanguage[] = [];\n if (hasResolvedLanguages(languages)) {\n resolvedLanguages = getResolvedLanguages(languages);\n } else {\n resolvedLanguages = await resolveLanguages(languages);\n }\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n resolve();\n return;\n }\n\n const [highlighter] = await Promise.all([\n getSharedHighlighter({\n themes,\n langs: ['text', ...languages],\n preferredHighlighter: this.preferredHighlighter,\n }),\n this.initializeWorkers(resolvedThemes, resolvedLanguages),\n ]);\n\n if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n this.terminateWorkers();\n resolve();\n return;\n }\n this.highlighter = highlighter;\n this.initialized = true;\n this.diffCache.clear();\n this.fileCache.clear();\n this.drainQueue();\n this.queueBroadcastStateChanges();\n resolve();\n } catch (e) {\n if (\n e instanceof WorkerPoolTerminatedError ||\n !this.isCurrentLifecycle(lifecycleGeneration)\n ) {\n resolve();\n return;\n }\n this.initialized = false;\n this.workersFailed = true;\n this.queueBroadcastStateChanges();\n reject(e);\n }\n })();\n });\n this.queueBroadcastStateChanges();\n } else {\n return this.initialized;\n }\n }\n\n private async initializeWorkers(\n resolvedThemes: ThemeRegistrationResolved[],\n resolvedLanguages: ResolvedLanguage[]\n ): Promise<void> {\n this.workersFailed = false;\n const initPromises: Promise<unknown>[] = [];\n const customExtensionVersion = getCustomExtensionsVersion();\n const customExtensionMap =\n customExtensionVersion > 0 ? getCustomExtensionsMap() : undefined;\n if (this.workers.length > 0) {\n this.terminateWorkers();\n }\n for (let i = 0; i < (this.options.poolSize ?? 8); i++) {\n const worker = this.options.workerFactory();\n const managedWorker: ManagedWorker = {\n worker,\n request_id: undefined,\n initialized: false,\n langs: new Set(['text', ...resolvedLanguages.map(({ name }) => name)]),\n customExtensionsVersion: 0,\n };\n worker.addEventListener(\n 'message',\n (event: MessageEvent<WorkerResponse>) => {\n this.handleWorkerMessage(managedWorker, event.data);\n }\n );\n worker.addEventListener('error', (error) =>\n console.error('Worker error:', error, managedWorker)\n );\n this.workers.push(managedWorker);\n initPromises.push(\n new Promise<void>((resolve, reject) => {\n const id = this.generateRequestId();\n const task: InitializeWorkerTask = {\n type: 'initialize',\n id,\n request: {\n type: 'initialize',\n id,\n renderOptions: this.renderOptions,\n preferredHighlighter: this.preferredHighlighter,\n resolvedThemes,\n resolvedLanguages,\n customExtensionsVersion:\n customExtensionMap != null ? customExtensionVersion : undefined,\n customExtensionMap,\n },\n resolve() {\n managedWorker.initialized = true;\n resolve();\n },\n reject,\n requestStart: Date.now(),\n };\n this.pendingTasks.set(id, task);\n this.executeTask(managedWorker, task);\n })\n );\n }\n await Promise.all(initPromises);\n }\n\n private drainQueue = () => {\n this._queuedDrain = undefined;\n // If we are initializing or things got cancelled while initializing, we\n // should not attempt to drain the queue\n if (this.initialized !== true || this.taskQueue.size === 0) {\n return;\n }\n for (const [instance, task] of this.taskQueue) {\n // If we have a request in progress for the same instance, we should wait\n // for it to finish\n if (this.instanceRequestMap.has(instance)) {\n continue;\n }\n const langs = getLangsFromTask(task);\n const availableWorker = this.getAvailableWorker(langs);\n if (availableWorker == null) {\n break;\n }\n this.assignWorkerToTask(task, availableWorker);\n void this.resolveLanguagesAndExecuteTask(availableWorker, task, langs);\n }\n this.queueBroadcastStateChanges();\n };\n\n public highlightFileAST(\n instance: FileRendererInstance,\n file: FileContents\n ): void {\n if (isFilePlainText(file)) {\n return;\n }\n // If we already have a task in progress for this same file content, we\n // should drop it\n for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n for (const task of tasks) {\n if (\n 'instance' in task &&\n task.instance === instance &&\n task.request.type === 'file' &&\n areFilesEqual(file, task.request.file)\n ) {\n return;\n }\n }\n }\n this.submitTask(instance, { type: 'file', file });\n }\n\n public getPlainFileAST(\n file: FileContents,\n startingLine: number,\n totalLines: number,\n lines?: string[]\n ): ThemedFileResult | undefined {\n if (this.highlighter == null) {\n this.queueInitialization();\n return undefined;\n }\n return renderFileWithHighlighter(\n file,\n this.highlighter,\n this.renderOptions,\n { forcePlainText: true, startingLine, totalLines, lines }\n );\n }\n\n public highlightDiffAST(\n instance: DiffRendererInstance,\n diff: FileDiffMetadata\n ): void {\n if (isDiffPlainText(diff)) {\n return;\n }\n // If we already have a task in progress for this same diff content, we\n // should ignore executing it again\n for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n for (const task of tasks) {\n if (\n 'instance' in task &&\n task.instance === instance &&\n task.request.type === 'diff' &&\n task.request.diff === diff\n ) {\n return;\n }\n }\n }\n this.submitTask(instance, { type: 'diff', diff });\n }\n\n public getPlainDiffAST(\n diff: FileDiffMetadata,\n startingLine: number,\n totalLines: number,\n expandedHunks?: Map<number, HunkExpansionRegion> | true,\n collapsedContextThreshold?: number\n ): ThemedDiffResult | undefined {\n return this.highlighter != null\n ? renderDiffWithHighlighter(diff, this.highlighter, this.renderOptions, {\n forcePlainText: true,\n startingLine,\n totalLines,\n expandedHunks,\n collapsedContextThreshold,\n })\n : undefined;\n }\n\n public terminate(): void {\n this.lifecycleGeneration++;\n this.cancelPendingAsyncWorkerTasks();\n this.terminateWorkers();\n this.fileCache.clear();\n this.diffCache.clear();\n this.instanceRequestMap.clear();\n this.taskQueue.clear();\n this.pendingTasks.clear();\n this.highlighter = undefined;\n this.initialized = false;\n this.workersFailed = false;\n this.queueBroadcastStateChanges();\n }\n\n private isCurrentLifecycle(lifecycleGeneration: number): boolean {\n return this.lifecycleGeneration === lifecycleGeneration;\n }\n\n private queueInitialization(languages?: SupportedLanguages[]): void {\n void this.initialize(languages).catch((error) => {\n console.error(error);\n });\n }\n\n private cancelPendingAsyncWorkerTasks(): void {\n const error = new WorkerPoolTerminatedError();\n for (const task of this.pendingTasks.values()) {\n if ('reject' in task) {\n task.reject(error);\n }\n }\n }\n\n private terminateWorkers() {\n for (const managedWorker of this.workers) {\n managedWorker.worker.terminate();\n }\n this.workers.length = 0;\n }\n\n public getStats(): WorkerStats {\n return {\n managerState: (() => {\n if (this.initialized === false) {\n return 'waiting';\n }\n if (this.initialized !== true) {\n return 'initializing';\n }\n return 'initialized';\n })(),\n totalWorkers: this.workers.length,\n workersFailed: this.workersFailed,\n busyWorkers: this.workers.filter((w) => w.request_id != null).length,\n queuedTasks: this.taskQueue.size,\n pendingTasks: this.pendingTasks.size,\n themeSubscribers: this.themeSubscribers.size,\n fileCacheSize: this.fileCache.size,\n diffCacheSize: this.diffCache.size,\n };\n }\n\n private submitTask(\n instance: FileRendererInstance,\n request: Omit<RenderFileRequest, 'id'>\n ): void;\n private submitTask(\n instance: DiffRendererInstance,\n request: Omit<RenderDiffRequest, 'id'>\n ): void;\n private submitTask(\n instance: FileRendererInstance | DiffRendererInstance,\n request: SubmitRequest\n ): void {\n if (this.initialized === false) {\n this.queueInitialization();\n }\n\n const id = this.generateRequestId();\n const requestStart = Date.now();\n const task: RenderFileTask | RenderDiffTask = (() => {\n switch (request.type) {\n case 'file':\n return {\n type: 'file',\n id,\n request: { ...request, id },\n instance: instance as FileRendererInstance,\n requestStart,\n };\n case 'diff':\n return {\n type: 'diff',\n id,\n request: { ...request, id },\n instance: instance as DiffRendererInstance,\n requestStart,\n };\n }\n })();\n\n this.taskQueue.set(instance, task);\n this.queueDrain();\n }\n\n private async resolveLanguagesAndExecuteTask(\n availableWorker: ManagedWorker,\n task: RenderFileTask | RenderDiffTask,\n langs: SupportedLanguages[]\n ): Promise<void> {\n try {\n // Add resolved languages if required\n const workerMissingLangs = langs.filter(\n (lang) => !availableWorker.langs.has(lang)\n );\n\n if (workerMissingLangs.length > 0) {\n if (hasResolvedLanguages(workerMissingLangs)) {\n task.request.resolvedLanguages =\n getResolvedLanguages(workerMissingLangs);\n } else {\n task.request.resolvedLanguages =\n await resolveLanguages(workerMissingLangs);\n }\n }\n this.executeTask(availableWorker, task);\n } catch {\n this.cleanWorkerAndTask(availableWorker, task);\n }\n }\n\n private handleWorkerMessage(\n managedWorker: ManagedWorker,\n response: WorkerResponse\n ): void {\n const task = this.pendingTasks.get(response.id);\n try {\n if (task == null) {\n // If we can't find a task for this response, it probably means the\n // component has been unmounted, so we should silently ignore it\n throw IGNORE_RESPONSE;\n } else if (response.type === 'error') {\n const error = new Error(response.error);\n if (response.stack) {\n error.stack = response.stack;\n }\n if ('reject' in task) {\n task.reject(error);\n } else {\n task.instance.onHighlightError(error);\n }\n throw error;\n } else {\n // If we've gotten a newer request from the same instance, we should\n // ignore this response either because it's out of order or because we\n // have a newer more important request\n if (\n 'instance' in task &&\n this.instanceRequestMap.get(task.instance) !== response.id\n ) {\n throw IGNORE_RESPONSE;\n }\n switch (response.requestType) {\n case 'initialize':\n if (task.type !== 'initialize') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n this.syncCustomExtensionVersion(managedWorker, task.request);\n task.resolve();\n break;\n case 'set-render-options':\n if (task.type !== 'set-render-options') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n task.resolve();\n break;\n case 'file': {\n if (task.type !== 'file') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n const { result, options } = response;\n const { instance, request } = task;\n this.syncCustomExtensionVersion(managedWorker, request);\n if (request.file.cacheKey != null) {\n this.fileCache.set(request.file.cacheKey, { result, options });\n }\n instance.onHighlightSuccess(request.file, result, options);\n break;\n }\n case 'diff': {\n if (task.type !== 'diff') {\n throw new Error('handleWorkerMessage: task/response dont match');\n }\n const { result, options } = response;\n const { instance, request } = task;\n this.syncCustomExtensionVersion(managedWorker, request);\n if (request.diff.cacheKey != null) {\n this.diffCache.set(request.diff.cacheKey, { result, options });\n }\n instance.onHighlightSuccess(request.diff, result, options);\n break;\n }\n }\n }\n } catch (error) {\n if (error !== IGNORE_RESPONSE) {\n console.error(error, task, response);\n }\n }\n\n this.cleanWorkerAndTask(managedWorker, task);\n this.queueBroadcastStateChanges();\n if (this.taskQueue.size > 0) {\n // We queue drain so that potentially multiple workers can free up\n // allowing for better language matches if possible\n this.queueDrain();\n }\n }\n\n private _queuedDrain: Promise<void> | undefined;\n private queueDrain() {\n if (this._queuedDrain != null) return;\n this._queuedDrain = Promise.resolve().then(this.drainQueue);\n this.queueBroadcastStateChanges();\n }\n\n private assignWorkerToTask(\n task: AllWorkerTasks,\n managedWorker: ManagedWorker\n ) {\n managedWorker.request_id = task.id;\n if ('instance' in task) {\n this.taskQueue.delete(task.instance);\n this.instanceRequestMap.set(task.instance, task.id);\n }\n this.pendingTasks.set(task.id, task);\n }\n\n private cleanWorkerAndTask(\n managedWorker: ManagedWorker,\n task?: AllWorkerTasks\n ) {\n managedWorker.request_id = undefined;\n if (task != null) {\n if ('instance' in task) {\n this.instanceRequestMap.delete(task.instance);\n }\n this.pendingTasks.delete(task.id);\n }\n }\n\n private executeTask(\n managedWorker: ManagedWorker,\n task: AllWorkerTasks\n ): void {\n if (shouldSyncCustomExtensions(task.request)) {\n this.maybeAttachCustomExtensions(managedWorker, task.request);\n }\n this.assignWorkerToTask(task, managedWorker);\n for (const lang of getLangsFromTask(task)) {\n managedWorker.langs.add(lang);\n }\n try {\n managedWorker.worker.postMessage(task.request);\n } catch (error) {\n this.cleanWorkerAndTask(managedWorker, task);\n console.error('Failed to post message to worker:', error);\n if ('instance' in task) {\n task.instance.onHighlightError(error);\n } else if ('reject' in task) {\n task.reject(error as Error);\n }\n }\n this.queueBroadcastStateChanges();\n }\n\n private maybeAttachCustomExtensions(\n managedWorker: ManagedWorker,\n request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n ): void {\n if (request.customExtensionsVersion != null) {\n return;\n }\n const version = getCustomExtensionsVersion();\n if (managedWorker.customExtensionsVersion >= version) {\n return;\n }\n request.customExtensionsVersion = version;\n request.customExtensionMap = getCustomExtensionsMap();\n }\n\n private syncCustomExtensionVersion(\n managedWorker: ManagedWorker,\n request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n ): void {\n if (request.customExtensionsVersion == null) {\n return;\n }\n managedWorker.customExtensionsVersion = request.customExtensionsVersion;\n }\n\n private getAvailableWorker(\n langs: SupportedLanguages[]\n ): ManagedWorker | undefined {\n let worker: ManagedWorker | undefined;\n for (const managedWorker of this.workers) {\n if (managedWorker.request_id != null || !managedWorker.initialized) {\n continue;\n }\n worker = managedWorker;\n if (langs.length === 0) {\n break;\n }\n let hasEveryLang = true;\n for (const lang of langs) {\n if (!managedWorker.langs.has(lang)) {\n hasEveryLang = false;\n break;\n }\n }\n if (hasEveryLang) {\n break;\n }\n }\n return worker;\n }\n\n private generateRequestId(): WorkerRequestId {\n return `req_${++this.nextRequestId}`;\n }\n}\n\nfunction shouldSyncCustomExtensions(\n request: AllWorkerTasks['request']\n): request is InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest {\n return (\n request.type === 'initialize' ||\n request.type === 'file' ||\n request.type === 'diff'\n );\n}\n\nfunction getLangsFromTask(task: AllWorkerTasks): SupportedLanguages[] {\n const langs = new Set<SupportedLanguages>();\n if (task.type === 'initialize' || task.type === 'set-render-options') {\n return [];\n }\n switch (task.type) {\n case 'file': {\n langs.add(\n task.request.file.lang ??\n getFiletypeFromFileName(task.request.file.name)\n );\n break;\n }\n case 'diff': {\n langs.add(\n task.request.diff.lang ??\n getFiletypeFromFileName(task.request.diff.name)\n );\n langs.add(\n task.request.diff.lang ??\n getFiletypeFromFileName(task.request.diff.prevName ?? '-')\n );\n break;\n }\n }\n langs.delete('text');\n return Array.from(langs);\n}\n"],"mappings":"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"}
|
|
@@ -12137,6 +12137,13 @@ __reExport(engine_oniguruma_exports, dist_exports);
|
|
|
12137
12137
|
//#endregion
|
|
12138
12138
|
//#region src/constants.ts
|
|
12139
12139
|
const DIFFS_TAG_NAME = "diffs-container";
|
|
12140
|
+
const DIFFS_DEVELOPMENT_BUILD = (() => {
|
|
12141
|
+
try {
|
|
12142
|
+
return process.env.NODE_ENV === "development";
|
|
12143
|
+
} catch {
|
|
12144
|
+
return false;
|
|
12145
|
+
}
|
|
12146
|
+
})();
|
|
12140
12147
|
const COMMIT_METADATA_SPLIT = /(?=^From [a-f0-9]+ .+$)/m;
|
|
12141
12148
|
const GIT_DIFF_FILE_BREAK_REGEX = /(?=^diff --git)/gm;
|
|
12142
12149
|
const UNIFIED_DIFF_FILE_BREAK_REGEX = /(?=^---\s+\S)/gm;
|
|
@@ -12161,6 +12168,8 @@ const DEFAULT_THEMES = {
|
|
|
12161
12168
|
const THEME_CSS_ATTRIBUTE = "data-theme-css";
|
|
12162
12169
|
const UNSAFE_CSS_ATTRIBUTE = "data-unsafe-css";
|
|
12163
12170
|
const CORE_CSS_ATTRIBUTE = "data-core-css";
|
|
12171
|
+
const DIFFS_SCROLLBAR_MEASURE_ATTRIBUTE = "data-diffs-scrollbar-measure";
|
|
12172
|
+
const DIFFS_SCROLLBAR_GUTTER_MEASURED_PROPERTY = "--diffs-scrollbar-gutter-measured";
|
|
12164
12173
|
const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;
|
|
12165
12174
|
const DEFAULT_TOKENIZE_MAX_LENGTH = 1e5;
|
|
12166
12175
|
const DEFAULT_VIRTUAL_FILE_METRICS = {
|
|
@@ -12174,7 +12183,7 @@ const DEFAULT_CODE_VIEW_FILE_METRICS = {
|
|
|
12174
12183
|
...DEFAULT_VIRTUAL_FILE_METRICS,
|
|
12175
12184
|
hunkLineCount: 1
|
|
12176
12185
|
};
|
|
12177
|
-
const
|
|
12186
|
+
const DEFAULT_CODE_VIEW_LAYOUT = {
|
|
12178
12187
|
paddingTop: 8,
|
|
12179
12188
|
paddingBottom: 8,
|
|
12180
12189
|
gap: 8
|
|
@@ -12515,6 +12524,22 @@ const EXTENSION_TO_FILE_FORMAT = {
|
|
|
12515
12524
|
rst: "rst",
|
|
12516
12525
|
rake: "ruby",
|
|
12517
12526
|
gemspec: "ruby",
|
|
12527
|
+
jbuilder: "ruby",
|
|
12528
|
+
builder: "ruby",
|
|
12529
|
+
rabl: "ruby",
|
|
12530
|
+
arb: "ruby",
|
|
12531
|
+
ru: "ruby",
|
|
12532
|
+
podspec: "ruby",
|
|
12533
|
+
Gemfile: "ruby",
|
|
12534
|
+
Rakefile: "ruby",
|
|
12535
|
+
Guardfile: "ruby",
|
|
12536
|
+
Capfile: "ruby",
|
|
12537
|
+
Berksfile: "ruby",
|
|
12538
|
+
Brewfile: "ruby",
|
|
12539
|
+
Vagrantfile: "ruby",
|
|
12540
|
+
Thorfile: "ruby",
|
|
12541
|
+
Appraisals: "ruby",
|
|
12542
|
+
Dangerfile: "ruby",
|
|
12518
12543
|
sas: "sas",
|
|
12519
12544
|
sass: "sass",
|
|
12520
12545
|
scala: "scala",
|