@pierre/diffs 1.1.2 → 1.1.4
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/AdvancedVirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/FileDiff.d.ts +4 -4
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/UnresolvedFile.d.ts +18 -9
- package/dist/components/UnresolvedFile.d.ts.map +1 -1
- package/dist/components/UnresolvedFile.js +199 -57
- package/dist/components/UnresolvedFile.js.map +1 -1
- package/dist/components/VirtulizerDevelopment.d.ts.map +1 -1
- package/dist/constants.d.ts +5 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +5 -4
- package/dist/react/UnresolvedFile.d.ts +3 -1
- package/dist/react/UnresolvedFile.d.ts.map +1 -1
- package/dist/react/UnresolvedFile.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.d.ts +1 -1
- package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
- package/dist/react/utils/renderDiffChildren.js +6 -4
- package/dist/react/utils/renderDiffChildren.js.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.d.ts +5 -4
- package/dist/react/utils/useUnresolvedFileInstance.d.ts.map +1 -1
- package/dist/react/utils/useUnresolvedFileInstance.js +15 -12
- package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts +15 -7
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +9 -7
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +11 -16
- package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/UnresolvedFileHunksRenderer.js +68 -75
- package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/preloadDiffs.d.ts +2 -2
- package/dist/ssr/preloadDiffs.d.ts.map +1 -1
- package/dist/ssr/preloadDiffs.js +4 -3
- package/dist/ssr/preloadDiffs.js.map +1 -1
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +41 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/areMergeConflictActionsEqual.js +1 -1
- package/dist/utils/areMergeConflictActionsEqual.js.map +1 -1
- package/dist/utils/diffAcceptRejectHunk.d.ts +3 -2
- package/dist/utils/diffAcceptRejectHunk.d.ts.map +1 -1
- package/dist/utils/diffAcceptRejectHunk.js +24 -89
- package/dist/utils/diffAcceptRejectHunk.js.map +1 -1
- package/dist/utils/getMergeConflictActionSlotName.d.ts +4 -6
- package/dist/utils/getMergeConflictActionSlotName.d.ts.map +1 -1
- package/dist/utils/getMergeConflictActionSlotName.js +2 -2
- package/dist/utils/getMergeConflictActionSlotName.js.map +1 -1
- package/dist/utils/getMergeConflictLineTypes.d.ts +1 -2
- package/dist/utils/getMergeConflictLineTypes.d.ts.map +1 -1
- package/dist/utils/getMergeConflictLineTypes.js +7 -12
- package/dist/utils/getMergeConflictLineTypes.js.map +1 -1
- package/dist/utils/normalizeDiffResolution.d.ts +7 -0
- package/dist/utils/normalizeDiffResolution.d.ts.map +1 -0
- package/dist/utils/normalizeDiffResolution.js +11 -0
- package/dist/utils/normalizeDiffResolution.js.map +1 -0
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts +16 -12
- package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -1
- package/dist/utils/parseMergeConflictDiffFromFile.js +474 -117
- package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
- package/dist/utils/resolveConflict.d.ts +7 -0
- package/dist/utils/resolveConflict.d.ts.map +1 -0
- package/dist/utils/resolveConflict.js +23 -0
- package/dist/utils/resolveConflict.js.map +1 -0
- package/dist/utils/resolveRegion.d.ts +14 -0
- package/dist/utils/resolveRegion.d.ts.map +1 -0
- package/dist/utils/resolveRegion.js +215 -0
- package/dist/utils/resolveRegion.js.map +1 -0
- package/dist/utils/trimPatchContext.js +19 -20
- package/dist/utils/trimPatchContext.js.map +1 -1
- package/dist/worker/{wasm-BlUZCxHM.js → wasm-BaDzIkIn.js} +2 -2
- package/dist/worker/wasm-BaDzIkIn.js.map +1 -0
- package/dist/worker/worker-portable.js +4880 -4810
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js.map +1 -1
- package/package.json +2 -1
- package/dist/utils/resolveMergeConflict.d.ts +0 -7
- package/dist/utils/resolveMergeConflict.d.ts.map +0 -1
- package/dist/utils/resolveMergeConflict.js +0 -30
- package/dist/utils/resolveMergeConflict.js.map +0 -1
- package/dist/worker/wasm-BlUZCxHM.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseMergeConflictDiffFromFile.js","names":["currentContentChunks: string[]","incomingContentChunks: string[]","patchContentChunks: string[]","actions: MergeConflictDiffAction[]","currentLineNumberAtIndex: number | undefined","incomingLineNumberAtIndex: number | undefined"],"sources":["../../src/utils/parseMergeConflictDiffFromFile.ts"],"sourcesContent":["import type {\n FileContents,\n FileDiffMetadata,\n MergeConflictRegion,\n} from '../types';\nimport {\n getMergeConflictActionLineNumber,\n getMergeConflictParseResult,\n} from './getMergeConflictLineTypes';\nimport { processFile } from './parsePatchFiles';\nimport { splitFileContents } from './splitFileContents';\n\nexport interface ParseMergeConflictDiffFromFileResult {\n fileDiff: FileDiffMetadata;\n currentFile: FileContents;\n incomingFile: FileContents;\n actions: MergeConflictDiffAction[];\n}\n\nexport interface MergeConflictDiffAction {\n actionOriginalLineIndex: number;\n actionOriginalLineNumber: number;\n currentLineNumber: number | undefined;\n incomingLineNumber: number | undefined;\n conflict: MergeConflictRegion;\n conflictIndex: number;\n}\n\ninterface GetMergeConflictActionAnchorReturn {\n side: 'additions' | 'deletions';\n lineNumber: number;\n}\n\nexport function getMergeConflictActionAnchor(\n action: MergeConflictDiffAction\n): GetMergeConflictActionAnchorReturn | undefined {\n if (action.incomingLineNumber != null) {\n return {\n side: 'additions',\n lineNumber: action.incomingLineNumber,\n };\n }\n if (action.currentLineNumber != null) {\n return {\n side: 'deletions',\n lineNumber: action.currentLineNumber,\n };\n }\n return undefined;\n}\n\nexport function parseMergeConflictDiffFromFile(\n file: FileContents\n): ParseMergeConflictDiffFromFileResult {\n const lines = splitFileContents(file.contents);\n const { lineTypes, regions } = getMergeConflictParseResult(lines);\n const currentContentChunks: string[] = [];\n const incomingContentChunks: string[] = [];\n const patchContentChunks: string[] = [];\n const actions: MergeConflictDiffAction[] = new Array(regions.length);\n const actionOriginalLineNumbersByRegion = new Array<number>(regions.length);\n const actionOriginalLineIndexesByRegion = new Array<number>(regions.length);\n const actionLineIndexSet = new Set<number>();\n for (let regionIndex = 0; regionIndex < regions.length; regionIndex++) {\n const actionOriginalLineNumber = getMergeConflictActionLineNumber(\n regions[regionIndex]\n );\n const actionOriginalLineIndex = actionOriginalLineNumber - 1;\n actionOriginalLineNumbersByRegion[regionIndex] = actionOriginalLineNumber;\n actionOriginalLineIndexesByRegion[regionIndex] = actionOriginalLineIndex;\n actionLineIndexSet.add(actionOriginalLineIndex);\n }\n const actionLineNumbersByOriginalIndex = new Map<\n number,\n {\n currentLineNumber: number | undefined;\n incomingLineNumber: number | undefined;\n }\n >();\n let currentLineNumber = 0;\n let incomingLineNumber = 0;\n let actionIndex = 0;\n let nextConflict = regions[actionIndex];\n let nextActionOriginalLineNumber =\n nextConflict != null ? actionOriginalLineNumbersByRegion[actionIndex] : -1;\n let nextActionOriginalLineIndex =\n nextConflict != null ? actionOriginalLineIndexesByRegion[actionIndex] : -1;\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index];\n const lineType = lineTypes[index];\n let currentLineNumberAtIndex: number | undefined;\n let incomingLineNumberAtIndex: number | undefined;\n switch (lineType) {\n case 'none': {\n currentContentChunks.push(line);\n incomingContentChunks.push(line);\n patchContentChunks.push(` ${line}`);\n currentLineNumber++;\n incomingLineNumber++;\n currentLineNumberAtIndex = currentLineNumber;\n incomingLineNumberAtIndex = incomingLineNumber;\n break;\n }\n case 'current': {\n currentContentChunks.push(line);\n patchContentChunks.push(`-${line}`);\n currentLineNumber++;\n currentLineNumberAtIndex = currentLineNumber;\n break;\n }\n case 'incoming': {\n incomingContentChunks.push(line);\n patchContentChunks.push(`+${line}`);\n incomingLineNumber++;\n incomingLineNumberAtIndex = incomingLineNumber;\n break;\n }\n case 'base':\n case 'marker-start':\n case 'marker-base':\n case 'marker-separator':\n case 'marker-end': {\n currentContentChunks.push(line);\n incomingContentChunks.push(line);\n patchContentChunks.push(` ${line}`);\n currentLineNumber++;\n incomingLineNumber++;\n currentLineNumberAtIndex = currentLineNumber;\n incomingLineNumberAtIndex = incomingLineNumber;\n break;\n }\n default: {\n assertNever(lineType);\n }\n }\n\n if (actionLineIndexSet.has(index)) {\n actionLineNumbersByOriginalIndex.set(index, {\n currentLineNumber: currentLineNumberAtIndex,\n incomingLineNumber: incomingLineNumberAtIndex,\n });\n }\n\n // Regions are emitted in a stable order; resolve actions as soon as their\n // anchor original line has been processed.\n while (nextConflict != null && nextActionOriginalLineIndex <= index) {\n const actionLineNumbers = actionLineNumbersByOriginalIndex.get(\n nextActionOriginalLineIndex\n );\n actions[actionIndex] = {\n actionOriginalLineIndex: nextActionOriginalLineIndex,\n actionOriginalLineNumber: nextActionOriginalLineNumber,\n currentLineNumber: actionLineNumbers?.currentLineNumber,\n incomingLineNumber: actionLineNumbers?.incomingLineNumber,\n conflict: nextConflict,\n conflictIndex: nextConflict.conflictIndex,\n };\n actionIndex++;\n nextConflict = regions[actionIndex];\n if (nextConflict == null) {\n break;\n }\n nextActionOriginalLineNumber =\n actionOriginalLineNumbersByRegion[actionIndex];\n nextActionOriginalLineIndex =\n actionOriginalLineIndexesByRegion[actionIndex];\n }\n }\n\n const currentContents = currentContentChunks.join('');\n const incomingContents = incomingContentChunks.join('');\n const patchContents = patchContentChunks.join('');\n\n const currentFile = createResolvedConflictFile(\n file,\n 'current',\n currentContents\n );\n const incomingFile = createResolvedConflictFile(\n file,\n 'incoming',\n incomingContents\n );\n const patch = createMergeConflictPatch({\n name: file.name,\n patchContents,\n currentLineCount: currentLineNumber,\n incomingLineCount: incomingLineNumber,\n });\n\n const fileDiff = processFile(patch, {\n oldFile: currentFile,\n newFile: incomingFile,\n cacheKey:\n file.cacheKey != null\n ? `${file.cacheKey}:merge-conflict-diff`\n : undefined,\n throwOnError: true,\n });\n\n if (fileDiff == null) {\n throw new Error(\n 'parseMergeConflictDiffFromFile: failed to build merge conflict diff metadata'\n );\n }\n\n return {\n fileDiff,\n currentFile,\n incomingFile,\n actions,\n };\n}\n\ninterface CreateMergeConflictPatchProps {\n name: string;\n patchContents: string;\n currentLineCount: number;\n incomingLineCount: number;\n}\n\nfunction createMergeConflictPatch({\n name,\n patchContents,\n currentLineCount,\n incomingLineCount,\n}: CreateMergeConflictPatchProps): string {\n const currentStart = currentLineCount > 0 ? 1 : 0;\n const incomingStart = incomingLineCount > 0 ? 1 : 0;\n return (\n `--- ${name}\\n` +\n `+++ ${name}\\n` +\n `@@ -${currentStart},${currentLineCount} +${incomingStart},${incomingLineCount} @@\\n` +\n patchContents\n );\n}\n\nfunction createResolvedConflictFile(\n file: FileContents,\n side: 'current' | 'incoming',\n contents: string\n): FileContents {\n return {\n ...file,\n contents,\n cacheKey:\n file.cacheKey != null\n ? `${file.cacheKey}:merge-conflict-${side}`\n : undefined,\n };\n}\n\nfunction assertNever(value: never): never {\n throw new Error(\n `parseMergeConflictDiffFromFile: unknown merge conflict line type ${String(value)}`\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parseMergeConflictDiffFromFile.js","names":["s: ParseState","type: FileDiffMetadata['type']","fileDiff: FileDiffMetadata","contentIndex: number","finalizedHunk: Hunk","nextBaseConflicts: Map<number, number> | undefined","markerRows: MergeConflictMarkerRow[]","hunkLineStartCache: (number[] | undefined)[]","currentContentIndex"],"sources":["../../src/utils/parseMergeConflictDiffFromFile.ts"],"sourcesContent":["// Parses a file containing git merge conflict markers (<<<<<<< / ======= / >>>>>>>)\n// into a synthetic unified diff. The core idea: treat the conflict file as though\n// \"current\" lines are deletions and \"incoming\" lines are additions. Lines outside\n// conflicts (and optional \"base\" sections from diff3) become shared context.\n//\n// The result is a standard FileDiffMetadata with hunks — identical in shape to what\n// you'd get from parsing a real unified diff — plus a parallel array of\n// MergeConflictDiffActions that anchor each conflict region to positions within the\n// hunk structure. Downstream consumers (e.g. the merge conflict UI) use these\n// anchors to overlay conflict markers onto the diff view.\n//\n// Architecture note: all helper functions are module-level (not closures inside the\n// main function) and receive a shared ParseState object by reference. This avoids\n// per-call scope-chain traversal on the hot path (~20K lines), where every line\n// triggers 2-3 helper calls.\n//\n// ---\n// NOTE: This file was nearly entirely written and optimized by AI. It has a\n// verification harness that any future changes (human or AI) should be validated\n// against:\n//\n// Snapshot tests (from packages/diffs/):\n// bun test parseMergeConflictDiffFromFile\n//\n// Performance benchmark (checksum must match 33121550):\n// bun ws diffs benchmark:parse-merge-conflict\n//\n// If you encounter a bug:\n// 1. Add a new test case in test/parseMergeConflictDiffFromFile.test.ts with\n// input that reproduces the failure. Use toMatchSnapshot() so the expected\n// output is captured automatically once fixed.\n// 2. Run `bun test parseMergeConflictDiffFromFile` from packages/diffs/ to\n// confirm the new test fails.\n// 3. Use an AI agent with extended/high thinking to fix the logic — the\n// snapshot tests and benchmark provide a tight feedback loop. The agent\n// should iterate until all snapshots pass AND the benchmark checksum\n// matches. Update snapshots with `bun test test/parseMergeConflictDiffFromFile.test.ts -u`\n// only after verifying the new output is correct.\n// ---\n\nimport type {\n FileContents,\n FileDiffMetadata,\n Hunk,\n MergeConflictMarkerRow,\n MergeConflictMarkerRowType,\n MergeConflictRegion,\n ProcessFileConflictData,\n} from '../types';\n\nexport interface ParseMergeConflictDiffFromFileResult {\n fileDiff: FileDiffMetadata;\n currentFile: FileContents;\n incomingFile: FileContents;\n actions: (MergeConflictDiffAction | undefined)[];\n markerRows: MergeConflictMarkerRow[];\n}\n\nexport interface MergeConflictDiffAction extends ProcessFileConflictData {\n // Kept for callback consumers that still need the original unresolved-region\n // source-line coordinates alongside structural hunk-content anchors.\n conflict: MergeConflictRegion;\n conflictIndex: number;\n markerLines: {\n start: string;\n base?: string;\n separator: string;\n end: string;\n };\n}\n\ninterface GetMergeConflictActionAnchorReturn {\n hunkIndex: number;\n lineIndex: number;\n}\n\n// Which section of a conflict we're currently inside while scanning lines.\n// Progresses: current → (optional) base → incoming.\ntype MergeConflictStage = 'current' | 'base' | 'incoming';\ntype MergeConflictSide = MergeConflictStage;\ntype MergeConflictMarkerType = 'start' | 'base' | 'separator' | 'end';\n\n// Controls how buffered context lines are trimmed when flushed to hunkContent:\n// 'leading' — first flush of a hunk; trim excess from the start\n// 'before-change' — flush between changes; emit all buffered lines\n// 'trailing' — last flush of a hunk; trim excess from the end\ntype ContextFlushMode = 'before-change' | 'leading' | 'trailing';\n\n// Mutable accumulator for building a single Hunk. Tracks line counts, the\n// hunkContent array (sequence of context/change groups), and a \"context buffer\"\n// that defers writing context lines until we know whether they're leading,\n// trailing, or mid-hunk context.\n//\n// The context buffer avoids eagerly committing context lines to hunkContent.\n// When a change line arrives, we flush the buffer — trimming to maxContextLines\n// if it's the leading or trailing edge of a hunk, or splitting into two hunks\n// if the gap between changes exceeds maxContextLines * 2.\ninterface HunkBuilder {\n additionStart: number;\n deletionStart: number;\n additionCount: number;\n deletionCount: number;\n additionLines: number;\n deletionLines: number;\n additionLineIndex: number;\n deletionLineIndex: number;\n hunkContent: Hunk['hunkContent'];\n // Context buffer: instead of storing per-line index arrays, we track the\n // starting indices and a count. Since context lines always push to both\n // additionLines and deletionLines consecutively, indices can be derived.\n contextBufferAdditionStart: number;\n contextBufferDeletionStart: number;\n contextBufferCount: number;\n // Sparse map of buffer-offset → conflictIndex for base-section context lines.\n // Empty for most buffers since base lines are rare.\n contextBufferBaseConflicts: Map<number, number> | undefined;\n}\n\n// Tracks an in-progress conflict as we scan through its lines. Pushed onto\n// conflictStack when we hit a <<<<<<< marker, and popped + finalized when we\n// hit the matching >>>>>>> marker. The `stage` field tells processLine which\n// section we're in so it knows whether to emit deletions, context, or additions.\ninterface ConflictFrame {\n conflictIndex: number;\n stage: MergeConflictStage;\n startLineIndex: number;\n baseMarkerLineIndex?: number;\n separatorLineIndex?: number;\n markerLines: {\n start: string;\n base?: string;\n separator?: string;\n };\n}\n\ninterface ConflictActionBuilder {\n action: MergeConflictDiffAction;\n completed: boolean;\n}\n\n// Bundles all mutable state shared across parse helper functions, replacing\n// closure-captured variables with a single object passed by reference.\n//\n// The two key arrays — deletionLines and additionLines — are the synthetic\n// \"before\" and \"after\" file contents. Context lines are pushed to both arrays\n// (identical on both sides). Current-side conflict lines go only into\n// deletionLines; incoming-side lines go only into additionLines. After parsing,\n// joining each array produces the resolved file for that side.\ninterface ParseState {\n // \"Before\" file lines (context + current-side conflict content).\n deletionLines: string[];\n // \"After\" file lines (context + incoming-side conflict content).\n additionLines: string[];\n // Stack of open conflict regions (supports nested conflicts, though rare).\n conflictStack: ConflictFrame[];\n // Parallel to actions[]; accumulates content indices during parsing.\n conflictBuilders: ConflictActionBuilder[];\n // Final output: one action per conflict, indexed by conflictIndex.\n actions: (MergeConflictDiffAction | undefined)[];\n // Finalized hunks, appended as context gaps cause hunk splits.\n hunks: Hunk[];\n nextConflictIndex: number;\n // Running line totals used to compute hunk splitLineStart/unifiedLineStart.\n splitLineCount: number;\n unifiedLineCount: number;\n // 1-based line number where the previous hunk ended (for collapsedBefore).\n lastHunkEnd: number;\n // The hunk currently being built; undefined between hunks.\n activeHunk: HunkBuilder | undefined;\n maxContextLines: number;\n // Cached maxContextLines * 2 (the threshold for splitting a hunk).\n maxContextLines2: number;\n}\n\nexport function getMergeConflictActionAnchor(\n action: MergeConflictDiffAction,\n fileDiff: FileDiffMetadata\n): GetMergeConflictActionAnchorReturn | undefined {\n const hunk = fileDiff.hunks[action.hunkIndex];\n if (hunk == null) {\n return undefined;\n }\n return {\n hunkIndex: action.hunkIndex,\n lineIndex: getUnifiedLineStartForContent(hunk, action.startContentIndex),\n };\n}\n\n// Main entry point. Walks every line of the conflict file exactly once,\n// dispatching each line through processLine which routes it to the appropriate\n// emitter (context or change). After the loop, finalizes the last hunk,\n// validates all conflicts were closed, and assembles the result.\n//\n// The three phases are:\n// 1. Line-by-line scan — builds hunks and conflict actions incrementally\n// 2. Post-loop cleanup — flushes trailing context, finalizes last hunk\n// 3. Result assembly — joins line arrays, builds marker rows for the UI\nexport function parseMergeConflictDiffFromFile(\n file: FileContents,\n maxContextLines: number = 6\n): ParseMergeConflictDiffFromFileResult {\n // Never allow maxContextLines to drop below 1 or else things break.\n maxContextLines = Math.max(maxContextLines, 1);\n\n const s: ParseState = {\n deletionLines: [],\n additionLines: [],\n conflictStack: [],\n conflictBuilders: [],\n actions: [],\n hunks: [],\n nextConflictIndex: 0,\n splitLineCount: 0,\n unifiedLineCount: 0,\n lastHunkEnd: 0,\n activeHunk: undefined,\n maxContextLines,\n maxContextLines2: maxContextLines * 2,\n };\n\n // Phase 1: Line-by-line scan. We inline the indexOf loop here (rather than\n // calling a helper with a callback) to avoid creating a closure on the hot\n // path. Each line is sliced and dispatched to processLine.\n const contents = file.contents;\n const contentLength = contents.length;\n if (contentLength > 0) {\n let lineStart = 0;\n let lineIndex = 0;\n let newlinePos = contents.indexOf('\\n', lineStart);\n while (newlinePos !== -1) {\n processLine(s, contents.slice(lineStart, newlinePos + 1), lineIndex);\n lineStart = newlinePos + 1;\n lineIndex++;\n newlinePos = contents.indexOf('\\n', lineStart);\n }\n if (lineStart < contentLength) {\n processLine(s, contents.slice(lineStart), lineIndex);\n }\n }\n\n // Phase 2: Post-loop cleanup. Any unclosed conflict is an error. If the\n // last hunk has buffered context lines, flush them as trailing context and\n // finalize the hunk.\n if (s.conflictStack.length > 0) {\n throw new Error(\n 'parseMergeConflictDiffFromFile: unfinished merge conflict marker stack'\n );\n }\n\n if (s.activeHunk != null && s.activeHunk.hunkContent.length > 0) {\n flushBufferedContext(s, s.activeHunk, 'trailing');\n finalizeActiveHunk(s);\n }\n\n for (\n let conflictIndex = 0;\n conflictIndex < s.conflictBuilders.length;\n conflictIndex++\n ) {\n const builder = s.conflictBuilders[conflictIndex];\n if (builder == null || !builder.completed) {\n throw new Error(\n `parseMergeConflictDiffFromFile: failed to build merge conflict action ${conflictIndex}`\n );\n }\n }\n\n // Phase 3: Result assembly. Account for any collapsed lines after the last\n // hunk, then join the line arrays to produce resolved file contents.\n if (\n s.hunks.length > 0 &&\n s.additionLines.length > 0 &&\n s.deletionLines.length > 0\n ) {\n const lastHunk = s.hunks[s.hunks.length - 1];\n const collapsedAfter = Math.max(\n s.additionLines.length -\n (lastHunk.additionStart + lastHunk.additionCount - 1),\n 0\n );\n s.splitLineCount += collapsedAfter;\n s.unifiedLineCount += collapsedAfter;\n }\n\n const currentContents = s.deletionLines.join('');\n const incomingContents = s.additionLines.join('');\n const currentFile = createResolvedConflictFile(\n file,\n 'current',\n currentContents\n );\n const incomingFile = createResolvedConflictFile(\n file,\n 'incoming',\n incomingContents\n );\n\n let type: FileDiffMetadata['type'] = 'change';\n if (incomingContents === '') {\n type = 'deleted';\n } else if (currentContents === '') {\n type = 'new';\n }\n\n const fileDiff: FileDiffMetadata = {\n name: file.name,\n prevName: undefined,\n type,\n hunks: s.hunks,\n splitLineCount: s.splitLineCount,\n unifiedLineCount: s.unifiedLineCount,\n isPartial: false,\n deletionLines: s.deletionLines,\n additionLines: s.additionLines,\n cacheKey:\n file.cacheKey != null\n ? `${file.cacheKey}:merge-conflict-diff`\n : undefined,\n };\n\n return {\n fileDiff,\n currentFile,\n incomingFile,\n actions: s.actions,\n markerRows: buildMergeConflictMarkerRows(fileDiff, s.actions),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Module-level parse helpers. Each receives ParseState by reference rather\n// than capturing variables via closure. The call graph from the hot path is:\n//\n// processLine\n// ├─ emitContextLine → ensureActiveHunk\n// ├─ emitChangeLine → ensureActiveHunk, splitHunkWithBufferedContext,\n// │ flushBufferedContext, appendChangeLine,\n// │ assignConflictContent\n// ├─ handleStartMarker\n// └─ finalizeConflict\n// ---------------------------------------------------------------------------\n\n// Routes a single source line to the right emitter based on whether we're\n// inside a conflict and, if so, which section (current/base/incoming).\n// Outside conflicts, only the start marker (<<<<<<< / charCode 60) can\n// change state, so we skip the full marker check for non-'<' lines.\nfunction processLine(s: ParseState, line: string, index: number): void {\n const frame = s.conflictStack[s.conflictStack.length - 1];\n\n // Outside any conflict: only start markers (<<<<<<<) can transition state.\n // Skip the full marker check for lines that can't be start markers.\n if (frame == null) {\n if (\n line.length >= 7 &&\n line.charCodeAt(0) === 60 &&\n getMergeConflictMarkerType(line) === 'start'\n ) {\n handleStartMarker(s, line, index);\n return;\n }\n emitContextLine(s, line);\n return;\n }\n\n // Inside a conflict: all marker types must be checked.\n const markerType = getMergeConflictMarkerType(line);\n\n if (markerType === 'start') {\n handleStartMarker(s, line, index);\n return;\n }\n\n if (markerType === 'base') {\n frame.stage = 'base';\n frame.baseMarkerLineIndex = index;\n frame.markerLines.base = line;\n return;\n }\n\n if (markerType === 'separator') {\n frame.stage = 'incoming';\n frame.separatorLineIndex = index;\n frame.markerLines.separator = line;\n return;\n }\n\n if (markerType === 'end') {\n const completedFrame = s.conflictStack.pop();\n if (completedFrame == null) {\n throw new Error(\n 'parseMergeConflictDiffFromFile: encountered end marker before start marker'\n );\n }\n finalizeConflict(s, completedFrame, index, line);\n return;\n }\n\n if (frame.stage === 'current') {\n emitChangeLine(s, 'deletion', line, frame.conflictIndex, 'current');\n } else if (frame.stage === 'base') {\n emitContextLine(s, line, frame.conflictIndex);\n } else {\n emitChangeLine(s, 'addition', line, frame.conflictIndex, 'incoming');\n }\n}\n\n// Lazily creates the active HunkBuilder if one doesn't exist yet. The hunk's\n// start positions are derived from the current length of the line arrays\n// (1-based, matching unified diff conventions).\nfunction ensureActiveHunk(s: ParseState): HunkBuilder {\n s.activeHunk ??= createHunkBuilder(\n s.additionLines.length + 1,\n s.deletionLines.length + 1\n );\n return s.activeHunk;\n}\n\n// \"Anchors\" a conflict to its position in the hunk's content array. Each\n// conflict needs to know which hunk it lives in (hunkIndex) and which content\n// entries correspond to its current/base/incoming sections. This is called\n// every time we emit a change or context line that belongs to a conflict, and\n// it incrementally widens the start/end content range.\nfunction assignConflictContent(\n s: ParseState,\n conflictIndex: number,\n role: MergeConflictSide,\n contentIndex: number\n): void {\n const builder = s.conflictBuilders[conflictIndex];\n if (builder == null) {\n throw new Error(\n `parseMergeConflictDiffFromFile: failed to locate conflict action ${conflictIndex}`\n );\n }\n\n const action = builder.action;\n const hunkIndex = s.hunks.length;\n if (action.hunkIndex < 0) {\n action.hunkIndex = hunkIndex;\n } else if (action.hunkIndex !== hunkIndex) {\n throw new Error(\n `parseMergeConflictDiffFromFile: conflict ${conflictIndex} spans multiple hunks and cannot be anchored`\n );\n }\n\n if (action.startContentIndex < 0) {\n action.startContentIndex = contentIndex;\n }\n action.endContentIndex = contentIndex;\n action.endMarkerContentIndex = contentIndex;\n\n if (role === 'current') {\n action.currentContentIndex ??= contentIndex;\n return;\n }\n if (role === 'base') {\n action.baseContentIndex ??= contentIndex;\n return;\n }\n action.incomingContentIndex = contentIndex;\n}\n\n// Appends a change line to the hunk's content array. If the previous entry is\n// already a 'change' group, we just bump its addition/deletion count instead\n// of creating a new entry — this keeps hunkContent compact. Returns the\n// content index so the caller can anchor the conflict to it.\nfunction appendChangeLine(\n hunk: HunkBuilder,\n lineType: 'addition' | 'deletion',\n additionLineIndex: number,\n deletionLineIndex: number\n): number {\n const hunkContent = hunk.hunkContent;\n const lastContent = hunkContent[hunkContent.length - 1];\n if (lastContent?.type === 'change') {\n if (lineType === 'addition') {\n lastContent.additions++;\n } else {\n lastContent.deletions++;\n }\n return hunkContent.length - 1;\n }\n hunkContent.push({\n type: 'change',\n additions: lineType === 'addition' ? 1 : 0,\n deletions: lineType === 'deletion' ? 1 : 0,\n additionLineIndex,\n deletionLineIndex,\n });\n return hunkContent.length - 1;\n}\n\n// Drains the hunk's context buffer into hunkContent, applying mode-dependent\n// trimming. The buffer accumulates context lines without committing them,\n// because we don't know yet whether they'll be leading context (trim start),\n// trailing context (trim end), or mid-hunk context (keep all). The mode tells\n// us which case we're in:\n//\n// 'leading' — first change in a new hunk; drop lines beyond\n// maxContextLines from the front, and shift the hunk's\n// start position forward accordingly.\n// 'trailing' — last flush before hunk finalization; keep at most\n// maxContextLines from the front of the buffer.\n// 'before-change' — mid-hunk context between two changes; emit everything.\nfunction flushBufferedContext(\n s: ParseState,\n hunk: HunkBuilder,\n mode: ContextFlushMode\n): void {\n let count = hunk.contextBufferCount;\n let addStart = hunk.contextBufferAdditionStart;\n let delStart = hunk.contextBufferDeletionStart;\n\n if (mode === 'leading' && count > s.maxContextLines) {\n const difference = count - s.maxContextLines;\n addStart += difference;\n delStart += difference;\n count = s.maxContextLines;\n hunk.additionStart += difference;\n hunk.deletionStart += difference;\n hunk.additionLineIndex += difference;\n hunk.deletionLineIndex += difference;\n }\n\n if (mode === 'trailing' && count > s.maxContextLines) {\n count = s.maxContextLines;\n }\n\n if (count === 0) {\n hunk.contextBufferCount = 0;\n hunk.contextBufferBaseConflicts = undefined;\n return;\n }\n\n // Bulk-append context: coalesce with previous context entry or create new\n // one. This avoids a per-line loop — significant when maxContextLines is\n // large.\n const hunkContent = hunk.hunkContent;\n const lastContent = hunkContent[hunkContent.length - 1];\n let contentIndex: number;\n if (lastContent?.type === 'context') {\n lastContent.lines += count;\n contentIndex = hunkContent.length - 1;\n } else {\n hunkContent.push({\n type: 'context',\n lines: count,\n additionLineIndex: addStart,\n deletionLineIndex: delStart,\n });\n contentIndex = hunkContent.length - 1;\n }\n hunk.additionCount += count;\n hunk.deletionCount += count;\n\n // Assign base-section conflict anchors (rare — only when base lines exist)\n const baseConflicts = hunk.contextBufferBaseConflicts;\n if (baseConflicts != null) {\n const bufferStartOffset = addStart - hunk.contextBufferAdditionStart;\n for (const [offset, conflictIndex] of baseConflicts) {\n if (offset >= bufferStartOffset && offset < bufferStartOffset + count) {\n assignConflictContent(s, conflictIndex, 'base', contentIndex);\n }\n }\n }\n hunk.contextBufferCount = 0;\n hunk.contextBufferBaseConflicts = undefined;\n}\n\n// Converts the mutable HunkBuilder into an immutable Hunk and pushes it onto\n// s.hunks. Computes line counts for split and unified view, the collapsed-\n// before gap (lines between the previous hunk and this one), and the hunk\n// header string (e.g. \"@@ -1,5 +1,7 @@\").\nfunction finalizeActiveHunk(s: ParseState): void {\n if (s.activeHunk == null) {\n return;\n }\n\n const hunk = s.activeHunk;\n s.activeHunk = undefined;\n if (hunk.hunkContent.length === 0) {\n return;\n }\n\n let hunkSplitLineCount = 0;\n let hunkUnifiedLineCount = 0;\n for (const content of hunk.hunkContent) {\n if (content.type === 'context') {\n hunkSplitLineCount += content.lines;\n hunkUnifiedLineCount += content.lines;\n } else {\n hunkSplitLineCount += Math.max(content.additions, content.deletions);\n hunkUnifiedLineCount += content.additions + content.deletions;\n }\n }\n\n const collapsedBefore = Math.max(hunk.additionStart - 1 - s.lastHunkEnd, 0);\n const finalizedHunk: Hunk = {\n collapsedBefore,\n additionStart: hunk.additionStart,\n additionCount: hunk.additionCount,\n additionLines: hunk.additionLines,\n additionLineIndex: hunk.additionLineIndex,\n deletionStart: hunk.deletionStart,\n deletionCount: hunk.deletionCount,\n deletionLines: hunk.deletionLines,\n deletionLineIndex: hunk.deletionLineIndex,\n hunkContent: hunk.hunkContent,\n hunkContext: undefined,\n hunkSpecs: `@@ -${formatHunkRange(hunk.deletionStart, hunk.deletionCount)} +${formatHunkRange(hunk.additionStart, hunk.additionCount)} @@\\n`,\n splitLineStart: s.splitLineCount + collapsedBefore,\n splitLineCount: hunkSplitLineCount,\n unifiedLineStart: s.unifiedLineCount + collapsedBefore,\n unifiedLineCount: hunkUnifiedLineCount,\n noEOFCRAdditions: false,\n noEOFCRDeletions: false,\n };\n\n s.hunks.push(finalizedHunk);\n s.splitLineCount += collapsedBefore + hunkSplitLineCount;\n s.unifiedLineCount += collapsedBefore + hunkUnifiedLineCount;\n s.lastHunkEnd = hunk.additionStart + hunk.additionCount - 1;\n}\n\n// Called when the context buffer between two changes exceeds maxContextLines*2.\n// This means there's a big enough gap to warrant splitting into separate hunks\n// (just like `diff -U` does). The procedure:\n// 1. Flush the first maxContextLines of the buffer as trailing context\n// 2. Finalize the current hunk\n// 3. Start a new hunk pre-seeded with the last maxContextLines as leading context\n// The middle portion of the buffer (between the two maxContextLines slices) is\n// the \"collapsed\" region — lines omitted from the diff view.\nfunction splitHunkWithBufferedContext(s: ParseState): void {\n if (s.activeHunk == null) {\n return;\n }\n\n const hunk = s.activeHunk;\n const count = hunk.contextBufferCount;\n const omittedContextLineCount = count - s.maxContextLines2;\n\n // Save trailing context start indices for the next hunk.\n const nextAddStart =\n hunk.contextBufferAdditionStart + count - s.maxContextLines;\n const nextDelStart =\n hunk.contextBufferDeletionStart + count - s.maxContextLines;\n\n // Extract base conflicts that fall within the trailing portion.\n let nextBaseConflicts: Map<number, number> | undefined;\n if (hunk.contextBufferBaseConflicts != null) {\n const tailOffset = count - s.maxContextLines;\n for (const [offset, ci] of hunk.contextBufferBaseConflicts) {\n if (offset >= tailOffset) {\n nextBaseConflicts ??= new Map();\n nextBaseConflicts.set(offset - tailOffset, ci);\n }\n }\n }\n\n flushBufferedContext(s, hunk, 'trailing');\n const emittedAdditionCount = hunk.additionCount;\n const emittedDeletionCount = hunk.deletionCount;\n finalizeActiveHunk(s);\n\n s.activeHunk = createHunkBuilder(\n hunk.additionStart + emittedAdditionCount + omittedContextLineCount,\n hunk.deletionStart + emittedDeletionCount + omittedContextLineCount\n );\n s.activeHunk.contextBufferAdditionStart = nextAddStart;\n s.activeHunk.contextBufferDeletionStart = nextDelStart;\n s.activeHunk.contextBufferCount = s.maxContextLines;\n s.activeHunk.contextBufferBaseConflicts = nextBaseConflicts;\n}\n\n// Adds a context line (identical on both sides of the diff). The line is pushed\n// to both additionLines and deletionLines, then buffered in the hunk's context\n// buffer rather than committed to hunkContent immediately. This deferred write\n// is what enables the leading/trailing trim logic in flushBufferedContext.\n//\n// For base-section lines inside a diff3 conflict, pass the conflict index so\n// the buffer can record the association; when the buffer is flushed, those\n// lines get anchored to the conflict via assignConflictContent.\nfunction emitContextLine(\n s: ParseState,\n line: string,\n baseConflictIndex: number = -1\n): void {\n const hunk = ensureActiveHunk(s);\n // Reset buffer start on first line after a flush/creation.\n if (hunk.contextBufferCount === 0) {\n hunk.contextBufferAdditionStart = s.additionLines.length;\n hunk.contextBufferDeletionStart = s.deletionLines.length;\n }\n s.additionLines.push(line);\n s.deletionLines.push(line);\n if (baseConflictIndex >= 0) {\n hunk.contextBufferBaseConflicts ??= new Map();\n hunk.contextBufferBaseConflicts.set(\n hunk.contextBufferCount,\n baseConflictIndex\n );\n }\n hunk.contextBufferCount++;\n}\n\n// Adds a change line (addition or deletion) to the current hunk. This is the\n// main \"work\" function on the hot path and orchestrates several steps:\n// 1. If there's a large context gap since the last change, split the hunk\n// 2. Flush any buffered context lines (leading trim on first change, or\n// pass-through for mid-hunk context)\n// 3. Push the line to the appropriate line array (additions or deletions)\n// 4. Append/coalesce the change into hunkContent\n// 5. Anchor the conflict action to the content index\nfunction emitChangeLine(\n s: ParseState,\n lineType: 'addition' | 'deletion',\n line: string,\n conflictIndex: number,\n role: MergeConflictSide\n): void {\n let hunk = ensureActiveHunk(s);\n // If the context gap since the last change exceeds 2x maxContextLines,\n // split into two hunks: trailing context for the old, leading for the new.\n if (\n hunk.hunkContent.length > 0 &&\n hunk.contextBufferCount > s.maxContextLines2\n ) {\n splitHunkWithBufferedContext(s);\n hunk = s.activeHunk!;\n }\n\n flushBufferedContext(\n s,\n hunk,\n hunk.hunkContent.length === 0 ? 'leading' : 'before-change'\n );\n\n const additionLineIndex = s.additionLines.length;\n const deletionLineIndex = s.deletionLines.length;\n if (lineType === 'addition') {\n s.additionLines.push(line);\n } else {\n s.deletionLines.push(line);\n }\n\n const contentIndex = appendChangeLine(\n hunk,\n lineType,\n additionLineIndex,\n deletionLineIndex\n );\n\n if (lineType === 'addition') {\n hunk.additionCount++;\n hunk.additionLines++;\n } else {\n hunk.deletionCount++;\n hunk.deletionLines++;\n }\n assignConflictContent(s, conflictIndex, role, contentIndex);\n}\n\n// Called when we hit a >>>>>>> end marker. Takes the completed ConflictFrame\n// and writes the final source-line coordinates and marker text into the\n// conflict action. Also handles empty-side conflicts: if one side had no\n// content lines, we fall back to the other side's content index so the action\n// always has valid anchors. This is what makes conflicts like \"add vs nothing\"\n// or \"nothing vs add\" representable.\nfunction finalizeConflict(\n s: ParseState,\n frame: ConflictFrame,\n endLineIndex: number,\n endMarkerLine: string\n): void {\n if (frame.separatorLineIndex == null || frame.markerLines.separator == null) {\n throw new Error(\n `parseMergeConflictDiffFromFile: conflict ${frame.conflictIndex} is missing a separator marker`\n );\n }\n\n const builder = s.conflictBuilders[frame.conflictIndex];\n if (builder == null) {\n throw new Error(\n `parseMergeConflictDiffFromFile: failed to finalize conflict ${frame.conflictIndex}`\n );\n }\n\n const action = builder.action;\n action.markerLines.separator = frame.markerLines.separator;\n action.markerLines.end = endMarkerLine;\n if (frame.markerLines.base != null) {\n action.markerLines.base = frame.markerLines.base;\n }\n\n action.conflict = {\n conflictIndex: frame.conflictIndex,\n startLineIndex: frame.startLineIndex,\n startLineNumber: frame.startLineIndex + 1,\n separatorLineIndex: frame.separatorLineIndex,\n separatorLineNumber: frame.separatorLineIndex + 1,\n endLineIndex,\n endLineNumber: endLineIndex + 1,\n baseMarkerLineIndex: frame.baseMarkerLineIndex,\n baseMarkerLineNumber:\n frame.baseMarkerLineIndex != null\n ? frame.baseMarkerLineIndex + 1\n : undefined,\n };\n\n // If one side of the conflict was empty (e.g. \"add vs nothing\"), its content\n // index will be undefined. Use the other side as a fallback so the action\n // always has a valid anchor for the UI to render.\n const fallbackContentIndex =\n action.currentContentIndex ?? action.incomingContentIndex;\n action.currentContentIndex ??= fallbackContentIndex;\n action.incomingContentIndex ??= fallbackContentIndex;\n if (action.startContentIndex < 0 && fallbackContentIndex != null) {\n action.startContentIndex = fallbackContentIndex;\n }\n if (action.endContentIndex < 0 && fallbackContentIndex != null) {\n action.endContentIndex = fallbackContentIndex;\n }\n if (action.endMarkerContentIndex < 0 && fallbackContentIndex != null) {\n action.endMarkerContentIndex = fallbackContentIndex;\n }\n\n if (\n action.hunkIndex < 0 ||\n action.startContentIndex < 0 ||\n action.endContentIndex < 0 ||\n action.endMarkerContentIndex < 0\n ) {\n throw new Error(\n `parseMergeConflictDiffFromFile: failed to anchor merge conflict ${frame.conflictIndex}`\n );\n }\n\n s.actions[action.conflictIndex] = action;\n builder.completed = true;\n}\n\n// Pushes a new ConflictFrame onto the stack and creates a placeholder\n// ConflictActionBuilder. The builder starts with sentinel values (-1 for\n// indices) that get filled in as we encounter content lines and markers.\n// The frame tracks which section we're scanning (current → base → incoming);\n// the builder accumulates the final action that downstream consumers use.\nfunction handleStartMarker(\n s: ParseState,\n line: string,\n lineIndex: number\n): void {\n const conflictIndex = s.nextConflictIndex;\n s.nextConflictIndex++;\n s.conflictStack.push({\n conflictIndex,\n stage: 'current',\n startLineIndex: lineIndex,\n markerLines: { start: line },\n });\n s.conflictBuilders[conflictIndex] = {\n completed: false,\n action: {\n conflict: {\n conflictIndex,\n startLineIndex: lineIndex,\n startLineNumber: lineIndex + 1,\n separatorLineIndex: lineIndex,\n separatorLineNumber: lineIndex + 1,\n endLineIndex: lineIndex,\n endLineNumber: lineIndex + 1,\n baseMarkerLineIndex: undefined,\n baseMarkerLineNumber: undefined,\n },\n conflictIndex,\n hunkIndex: -1,\n startContentIndex: -1,\n endContentIndex: -1,\n endMarkerContentIndex: -1,\n markerLines: {\n start: line,\n separator: '',\n end: '',\n },\n },\n };\n}\n\nfunction createHunkBuilder(\n additionStart: number,\n deletionStart: number\n): HunkBuilder {\n return {\n additionStart,\n deletionStart,\n additionCount: 0,\n deletionCount: 0,\n additionLines: 0,\n deletionLines: 0,\n additionLineIndex: Math.max(additionStart - 1, 0),\n deletionLineIndex: Math.max(deletionStart - 1, 0),\n hunkContent: [],\n contextBufferAdditionStart: Math.max(additionStart - 1, 0),\n contextBufferDeletionStart: Math.max(deletionStart - 1, 0),\n contextBufferCount: 0,\n contextBufferBaseConflicts: undefined,\n };\n}\n\nfunction formatHunkRange(start: number, count: number): string {\n return count === 1 ? `${start}` : `${start},${count}`;\n}\n\n// Detects whether a line is a merge conflict marker by inspecting the first\n// character and counting consecutive repetitions. Git conflict markers are\n// 7+ repeated characters:\n// '<' (60) = start (<<<<<<< current)\n// '|' (124) = base (||||||| base)\n// '=' (61) = separator (=======)\n// '>' (62) = end (>>>>>>> incoming)\n// The separator must be exactly '=======' with no trailing text; other markers\n// allow an optional space + label (e.g. \"<<<<<<< HEAD\").\nfunction getMergeConflictMarkerType(\n line: string\n): MergeConflictMarkerType | undefined {\n if (line.length < 7) {\n return undefined;\n }\n\n const markerCode = line.charCodeAt(0);\n if (\n markerCode !== 60 &&\n markerCode !== 62 &&\n markerCode !== 61 &&\n markerCode !== 124\n ) {\n return undefined;\n }\n\n const lineEnd = getLineContentEndIndex(line);\n if (lineEnd < 7) {\n return undefined;\n }\n\n let markerLength = 1;\n while (\n markerLength < lineEnd &&\n line.charCodeAt(markerLength) === markerCode\n ) {\n markerLength++;\n }\n\n if (markerLength < 7) {\n return undefined;\n }\n\n if (markerCode === 61) {\n return markerLength === lineEnd ? 'separator' : undefined;\n }\n\n if (\n markerLength !== lineEnd &&\n !isWhitespaceCode(line.charCodeAt(markerLength))\n ) {\n return undefined;\n }\n\n if (markerCode === 60) {\n return 'start';\n }\n if (markerCode === 62) {\n return 'end';\n }\n return 'base';\n}\n\nfunction getLineContentEndIndex(line: string): number {\n let end = line.length;\n if (end > 0 && line.charCodeAt(end - 1) === 10) {\n end--;\n }\n if (end > 0 && line.charCodeAt(end - 1) === 13) {\n end--;\n }\n return end;\n}\n\nfunction isWhitespaceCode(code: number): boolean {\n return (\n code === 9 ||\n code === 10 ||\n code === 11 ||\n code === 12 ||\n code === 13 ||\n code === 32\n );\n}\n\nfunction createResolvedConflictFile(\n file: FileContents,\n side: 'current' | 'incoming',\n contents: string\n): FileContents {\n return {\n ...file,\n contents,\n cacheKey:\n file.cacheKey != null\n ? `${file.cacheKey}:merge-conflict-${side}`\n : undefined,\n };\n}\n\n// Builds the marker row array that tells the UI where to render conflict\n// decorations (start/base/separator/end lines) in the diff view. Each marker\n// row maps a conflict marker to a specific line index in unified view.\n//\n// This is a post-processing step over the finalized hunks and actions. It\n// caches cumulative line-start positions per hunk to avoid recomputing them\n// for every marker.\nexport function buildMergeConflictMarkerRows(\n fileDiff: FileDiffMetadata,\n actions: (MergeConflictDiffAction | undefined)[]\n): MergeConflictMarkerRow[] {\n const markerRows: MergeConflictMarkerRow[] = [];\n const hunkLineStartCache: (number[] | undefined)[] = new Array(\n fileDiff.hunks.length\n );\n\n const getLineStart = (hunkIndex: number, contentIndex: number): number => {\n const hunk = fileDiff.hunks[hunkIndex];\n if (hunk == null) {\n return 0;\n }\n let starts = hunkLineStartCache[hunkIndex];\n if (starts == null) {\n starts = new Array<number>(hunk.hunkContent.length + 1);\n let lineIndex = hunk.unifiedLineStart;\n starts[0] = lineIndex;\n for (let index = 0; index < hunk.hunkContent.length; index++) {\n const content = hunk.hunkContent[index];\n lineIndex +=\n content.type === 'context'\n ? content.lines\n : content.deletions + content.additions;\n starts[index + 1] = lineIndex;\n }\n hunkLineStartCache[hunkIndex] = starts;\n }\n return starts[Math.max(contentIndex, 0)] ?? hunk.unifiedLineStart;\n };\n\n const getLineEnd = (hunkIndex: number, contentIndex: number): number => {\n const lineStart = getLineStart(hunkIndex, contentIndex);\n const starts = hunkLineStartCache[hunkIndex];\n const lineEndExclusive =\n starts?.[Math.max(contentIndex + 1, 0)] ??\n getLineStart(hunkIndex, contentIndex + 1);\n return Math.max(lineStart, lineEndExclusive - 1);\n };\n\n for (const action of actions) {\n if (action == null) {\n continue;\n }\n\n const hunk = fileDiff.hunks[action.hunkIndex];\n if (hunk == null) {\n continue;\n }\n\n const actionLineIndex = getLineStart(\n action.hunkIndex,\n action.startContentIndex\n );\n markerRows.push(\n createMergeConflictMarkerRow(\n action,\n 'marker-start',\n action.startContentIndex,\n action.markerLines.start,\n actionLineIndex\n )\n );\n\n if (action.baseContentIndex != null) {\n const currentContentIndex = action.currentContentIndex;\n const incomingContentIndex = action.incomingContentIndex;\n if (currentContentIndex == null || incomingContentIndex == null) {\n continue;\n }\n\n const baseMarkerLine = action.markerLines.base;\n if (baseMarkerLine == null) {\n continue;\n }\n\n const currentChange = hunk.hunkContent[currentContentIndex];\n const baseContext = hunk.hunkContent[action.baseContentIndex];\n const incomingChange = hunk.hunkContent[incomingContentIndex];\n if (\n currentChange?.type !== 'change' ||\n baseContext?.type !== 'context' ||\n incomingChange?.type !== 'change'\n ) {\n continue;\n }\n\n const currentStart = getLineStart(action.hunkIndex, currentContentIndex);\n const incomingStart = getLineStart(\n action.hunkIndex,\n incomingContentIndex\n );\n markerRows.push(\n createMergeConflictMarkerRow(\n action,\n 'marker-base',\n action.baseContentIndex,\n baseMarkerLine,\n currentStart + currentChange.deletions\n )\n );\n\n markerRows.push(\n createMergeConflictMarkerRow(\n action,\n 'marker-separator',\n action.baseContentIndex,\n action.markerLines.separator,\n incomingStart\n ),\n createMergeConflictMarkerRow(\n action,\n 'marker-end',\n action.endMarkerContentIndex,\n action.markerLines.end,\n getLineEnd(action.hunkIndex, action.endMarkerContentIndex)\n )\n );\n continue;\n }\n\n const currentContentIndex = action.currentContentIndex;\n if (currentContentIndex == null) {\n continue;\n }\n const content = hunk.hunkContent[currentContentIndex];\n if (content?.type !== 'change') {\n continue;\n }\n\n const contentStart = getLineStart(action.hunkIndex, currentContentIndex);\n const separatorLineIndex =\n content.deletions > 0\n ? contentStart + content.deletions\n : actionLineIndex;\n\n markerRows.push(\n createMergeConflictMarkerRow(\n action,\n 'marker-separator',\n currentContentIndex,\n action.markerLines.separator,\n separatorLineIndex\n ),\n createMergeConflictMarkerRow(\n action,\n 'marker-end',\n action.endMarkerContentIndex,\n action.markerLines.end,\n getLineEnd(action.hunkIndex, action.endMarkerContentIndex)\n )\n );\n }\n\n return markerRows;\n}\n\nfunction createMergeConflictMarkerRow(\n action: MergeConflictDiffAction,\n type: MergeConflictMarkerRowType,\n contentIndex: number,\n lineText: string,\n lineIndex: number\n): MergeConflictMarkerRow {\n return {\n type,\n hunkIndex: action.hunkIndex,\n contentIndex,\n conflictIndex: action.conflictIndex,\n lineText,\n lineIndex,\n };\n}\n\nfunction getUnifiedLineStartForContent(\n hunk: Hunk,\n contentIndex: number\n): number {\n let lineIndex = hunk.unifiedLineStart;\n for (let index = 0; index < contentIndex; index++) {\n const content = hunk.hunkContent[index];\n lineIndex +=\n content.type === 'context'\n ? content.lines\n : content.deletions + content.additions;\n }\n return lineIndex;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ConflictResolverTypes, FileDiffMetadata, ProcessFileConflictData } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/resolveConflict.d.ts
|
|
4
|
+
declare function resolveConflict(diff: FileDiffMetadata, conflict: ProcessFileConflictData, type: ConflictResolverTypes): FileDiffMetadata;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { resolveConflict };
|
|
7
|
+
//# sourceMappingURL=resolveConflict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveConflict.d.ts","names":["ConflictResolverTypes","FileDiffMetadata","ProcessFileConflictData","resolveConflict"],"sources":["../../src/utils/resolveConflict.d.ts"],"sourcesContent":["import type { ConflictResolverTypes, FileDiffMetadata, ProcessFileConflictData } from '../types';\nexport declare function resolveConflict(diff: FileDiffMetadata, conflict: ProcessFileConflictData, type: ConflictResolverTypes): FileDiffMetadata;\n//# sourceMappingURL=resolveConflict.d.ts.map"],"mappings":";;;iBACwBG,eAAAA,OAAsBF,4BAA4BC,+BAA+BF,wBAAwBC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { normalizeDiffResolution } from "./normalizeDiffResolution.js";
|
|
2
|
+
import { resolveRegion } from "./resolveRegion.js";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/resolveConflict.ts
|
|
5
|
+
function resolveConflict(diff, conflict, type) {
|
|
6
|
+
return resolveRegion(diff, {
|
|
7
|
+
resolution: normalizeDiffResolution(type),
|
|
8
|
+
hunkIndex: conflict.hunkIndex,
|
|
9
|
+
startContentIndex: conflict.startContentIndex,
|
|
10
|
+
endContentIndex: conflict.endContentIndex,
|
|
11
|
+
indexesToDelete: getConflictDeleteContentIndexes(conflict)
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function getConflictDeleteContentIndexes(conflict) {
|
|
15
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
16
|
+
if (conflict.baseContentIndex != null) indexes.add(conflict.baseContentIndex);
|
|
17
|
+
if (conflict.endMarkerContentIndex !== conflict.endContentIndex) indexes.add(conflict.endMarkerContentIndex);
|
|
18
|
+
return indexes;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { resolveConflict };
|
|
23
|
+
//# sourceMappingURL=resolveConflict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveConflict.js","names":["indexes: Set<number>"],"sources":["../../src/utils/resolveConflict.ts"],"sourcesContent":["import type {\n ConflictResolverTypes,\n FileDiffMetadata,\n ProcessFileConflictData,\n} from '../types';\nimport { normalizeDiffResolution } from './normalizeDiffResolution';\nimport { resolveRegion } from './resolveRegion';\n\nexport function resolveConflict(\n diff: FileDiffMetadata,\n conflict: ProcessFileConflictData,\n type: ConflictResolverTypes\n): FileDiffMetadata {\n return resolveRegion(diff, {\n resolution: normalizeDiffResolution(type),\n hunkIndex: conflict.hunkIndex,\n startContentIndex: conflict.startContentIndex,\n endContentIndex: conflict.endContentIndex,\n indexesToDelete: getConflictDeleteContentIndexes(conflict),\n });\n}\n\nfunction getConflictDeleteContentIndexes(\n conflict: ProcessFileConflictData\n): Set<number> {\n const indexes: Set<number> = new Set();\n if (conflict.baseContentIndex != null) {\n indexes.add(conflict.baseContentIndex);\n }\n if (conflict.endMarkerContentIndex !== conflict.endContentIndex) {\n indexes.add(conflict.endMarkerContentIndex);\n }\n return indexes;\n}\n"],"mappings":";;;;AAQA,SAAgB,gBACd,MACA,UACA,MACkB;AAClB,QAAO,cAAc,MAAM;EACzB,YAAY,wBAAwB,KAAK;EACzC,WAAW,SAAS;EACpB,mBAAmB,SAAS;EAC5B,iBAAiB,SAAS;EAC1B,iBAAiB,gCAAgC,SAAS;EAC3D,CAAC;;AAGJ,SAAS,gCACP,UACa;CACb,MAAMA,0BAAuB,IAAI,KAAK;AACtC,KAAI,SAAS,oBAAoB,KAC/B,SAAQ,IAAI,SAAS,iBAAiB;AAExC,KAAI,SAAS,0BAA0B,SAAS,gBAC9C,SAAQ,IAAI,SAAS,sBAAsB;AAE7C,QAAO"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FileDiffMetadata } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/resolveRegion.d.ts
|
|
4
|
+
interface RegionResolutionTarget {
|
|
5
|
+
hunkIndex: number;
|
|
6
|
+
startContentIndex: number;
|
|
7
|
+
endContentIndex: number;
|
|
8
|
+
resolution: 'deletions' | 'additions' | 'both';
|
|
9
|
+
indexesToDelete?: Set<number>;
|
|
10
|
+
}
|
|
11
|
+
declare function resolveRegion(diff: FileDiffMetadata, target: RegionResolutionTarget): FileDiffMetadata;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { resolveRegion };
|
|
14
|
+
//# sourceMappingURL=resolveRegion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveRegion.d.ts","names":["FileDiffMetadata","RegionResolutionTarget","Set","resolveRegion"],"sources":["../../src/utils/resolveRegion.d.ts"],"sourcesContent":["import type { FileDiffMetadata } from '../types';\ninterface RegionResolutionTarget {\n hunkIndex: number;\n startContentIndex: number;\n endContentIndex: number;\n resolution: 'deletions' | 'additions' | 'both';\n indexesToDelete?: Set<number>;\n}\nexport declare function resolveRegion(diff: FileDiffMetadata, target: RegionResolutionTarget): FileDiffMetadata;\nexport {};\n//# sourceMappingURL=resolveRegion.d.ts.map"],"mappings":";;;UACUC,sBAAAA;;EAAAA,iBAAAA,EAAAA,MAAsB;EAORE,eAAAA,EAAa,MAAA;EAAOH,UAAAA,EAAAA,WAAAA,GAAAA,WAAAA,GAAAA,MAAAA;EAA0BC,eAAAA,CAAAA,EAFhDC,GAEgDD,CAAAA,MAAAA,CAAAA;;AAAyC,iBAAvFE,aAAAA,CAAuF,IAAA,EAAnEH,gBAAmE,EAAA,MAAA,EAAzCC,sBAAyC,CAAA,EAAhBD,gBAAgB"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
//#region src/utils/resolveRegion.ts
|
|
2
|
+
function resolveRegion(diff, target) {
|
|
3
|
+
const { resolution, hunkIndex, startContentIndex, endContentIndex, indexesToDelete = /* @__PURE__ */ new Set() } = target;
|
|
4
|
+
const currentHunk = diff.hunks[hunkIndex];
|
|
5
|
+
if (currentHunk == null) {
|
|
6
|
+
console.error({
|
|
7
|
+
diff,
|
|
8
|
+
hunkIndex
|
|
9
|
+
});
|
|
10
|
+
throw new Error(`resolveRegion: Invalid hunk index: ${hunkIndex}`);
|
|
11
|
+
}
|
|
12
|
+
if (startContentIndex < 0 || endContentIndex >= currentHunk.hunkContent.length || startContentIndex > endContentIndex) throw new Error(`resolveRegion: Invalid content range, ${startContentIndex}, ${endContentIndex}`);
|
|
13
|
+
const { hunks, additionLines, deletionLines } = diff;
|
|
14
|
+
const resolvedDiff = {
|
|
15
|
+
...diff,
|
|
16
|
+
hunks: [],
|
|
17
|
+
deletionLines: [],
|
|
18
|
+
additionLines: [],
|
|
19
|
+
splitLineCount: 0,
|
|
20
|
+
unifiedLineCount: 0,
|
|
21
|
+
cacheKey: diff.cacheKey != null ? `${diff.cacheKey}:${resolution[0]}-${hunkIndex}:${startContentIndex}-${endContentIndex}` : void 0
|
|
22
|
+
};
|
|
23
|
+
const cursor = {
|
|
24
|
+
nextAdditionLineIndex: 0,
|
|
25
|
+
nextDeletionLineIndex: 0,
|
|
26
|
+
nextAdditionStart: 1,
|
|
27
|
+
nextDeletionStart: 1,
|
|
28
|
+
splitLineCount: 0,
|
|
29
|
+
unifiedLineCount: 0
|
|
30
|
+
};
|
|
31
|
+
const updatesEOFState = hunkIndex === hunks.length - 1 && endContentIndex === currentHunk.hunkContent.length - 1;
|
|
32
|
+
for (const [index, hunk] of hunks.entries()) {
|
|
33
|
+
pushCollapsedContextLines(resolvedDiff, deletionLines, additionLines, hunk.deletionLineIndex - hunk.collapsedBefore, hunk.additionLineIndex - hunk.collapsedBefore, hunk.collapsedBefore);
|
|
34
|
+
cursor.nextAdditionLineIndex += hunk.collapsedBefore;
|
|
35
|
+
cursor.nextDeletionLineIndex += hunk.collapsedBefore;
|
|
36
|
+
cursor.nextAdditionStart += hunk.collapsedBefore;
|
|
37
|
+
cursor.nextDeletionStart += hunk.collapsedBefore;
|
|
38
|
+
cursor.splitLineCount += hunk.collapsedBefore;
|
|
39
|
+
cursor.unifiedLineCount += hunk.collapsedBefore;
|
|
40
|
+
const newHunk = {
|
|
41
|
+
...hunk,
|
|
42
|
+
hunkContent: [],
|
|
43
|
+
additionStart: cursor.nextAdditionStart,
|
|
44
|
+
deletionStart: cursor.nextDeletionStart,
|
|
45
|
+
additionLineIndex: cursor.nextAdditionLineIndex,
|
|
46
|
+
deletionLineIndex: cursor.nextDeletionLineIndex,
|
|
47
|
+
additionCount: 0,
|
|
48
|
+
deletionCount: 0,
|
|
49
|
+
deletionLines: 0,
|
|
50
|
+
additionLines: 0,
|
|
51
|
+
splitLineStart: cursor.splitLineCount,
|
|
52
|
+
unifiedLineStart: cursor.unifiedLineCount,
|
|
53
|
+
splitLineCount: 0,
|
|
54
|
+
unifiedLineCount: 0
|
|
55
|
+
};
|
|
56
|
+
for (const [contentIndex, content] of hunk.hunkContent.entries()) if (index !== hunkIndex || contentIndex < startContentIndex || contentIndex > endContentIndex) {
|
|
57
|
+
pushContentLinesToDiff(content, resolvedDiff, deletionLines, additionLines);
|
|
58
|
+
const newContent = {
|
|
59
|
+
...content,
|
|
60
|
+
additionLineIndex: cursor.nextAdditionLineIndex,
|
|
61
|
+
deletionLineIndex: cursor.nextDeletionLineIndex
|
|
62
|
+
};
|
|
63
|
+
newHunk.hunkContent.push(newContent);
|
|
64
|
+
advanceCursor(newContent, cursor, newHunk);
|
|
65
|
+
} else if (indexesToDelete.has(contentIndex)) newHunk.hunkContent.push({
|
|
66
|
+
type: "context",
|
|
67
|
+
lines: 0,
|
|
68
|
+
deletionLineIndex: cursor.nextDeletionLineIndex,
|
|
69
|
+
additionLineIndex: cursor.nextAdditionLineIndex
|
|
70
|
+
});
|
|
71
|
+
else if (content.type === "context") {
|
|
72
|
+
pushContentLinesToDiff(content, resolvedDiff, deletionLines, additionLines);
|
|
73
|
+
const newContent = {
|
|
74
|
+
...content,
|
|
75
|
+
deletionLineIndex: cursor.nextDeletionLineIndex,
|
|
76
|
+
additionLineIndex: cursor.nextAdditionLineIndex
|
|
77
|
+
};
|
|
78
|
+
newHunk.hunkContent.push(newContent);
|
|
79
|
+
advanceCursor(newContent, cursor, newHunk);
|
|
80
|
+
} else {
|
|
81
|
+
pushResolveLinesToDiff(resolution, content, resolvedDiff, deletionLines, additionLines);
|
|
82
|
+
const newContent = {
|
|
83
|
+
type: "context",
|
|
84
|
+
lines: resolution === "deletions" ? content.deletions : resolution === "additions" ? content.additions : content.deletions + content.additions,
|
|
85
|
+
deletionLineIndex: cursor.nextDeletionLineIndex,
|
|
86
|
+
additionLineIndex: cursor.nextAdditionLineIndex
|
|
87
|
+
};
|
|
88
|
+
newHunk.hunkContent.push(newContent);
|
|
89
|
+
advanceCursor(newContent, cursor, newHunk);
|
|
90
|
+
}
|
|
91
|
+
if (index === hunkIndex && updatesEOFState) {
|
|
92
|
+
const noEOFCR = resolution === "deletions" ? hunk.noEOFCRDeletions : hunk.noEOFCRAdditions;
|
|
93
|
+
newHunk.noEOFCRAdditions = noEOFCR;
|
|
94
|
+
newHunk.noEOFCRDeletions = noEOFCR;
|
|
95
|
+
}
|
|
96
|
+
resolvedDiff.hunks.push(newHunk);
|
|
97
|
+
}
|
|
98
|
+
const finalHunk = hunks.at(-1);
|
|
99
|
+
if (finalHunk != null && !diff.isPartial) pushCollapsedContextLines(resolvedDiff, deletionLines, additionLines, finalHunk.deletionLineIndex + finalHunk.deletionCount, finalHunk.additionLineIndex + finalHunk.additionCount, Math.min(deletionLines.length - (finalHunk.deletionLineIndex + finalHunk.deletionCount), additionLines.length - (finalHunk.additionLineIndex + finalHunk.additionCount)));
|
|
100
|
+
resolvedDiff.splitLineCount = cursor.splitLineCount;
|
|
101
|
+
resolvedDiff.unifiedLineCount = cursor.unifiedLineCount;
|
|
102
|
+
return resolvedDiff;
|
|
103
|
+
}
|
|
104
|
+
function pushCollapsedContextLines(diff, deletionLines, additionLines, deletionLineIndex, additionLineIndex, lineCount) {
|
|
105
|
+
for (let index = 0; index < lineCount; index++) {
|
|
106
|
+
const deletionLine = deletionLines[deletionLineIndex + index];
|
|
107
|
+
const additionLine = additionLines[additionLineIndex + index];
|
|
108
|
+
if (deletionLine == null || additionLine == null) throw new Error("pushCollapsedContextLines: missing collapsed context line");
|
|
109
|
+
diff.deletionLines.push(deletionLine);
|
|
110
|
+
diff.additionLines.push(additionLine);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function pushContentLinesToDiff(content, diff, deletionLines, additionLines) {
|
|
114
|
+
if (content.type === "context") for (let i = 0; i < content.lines; i++) {
|
|
115
|
+
const line = additionLines[content.additionLineIndex + i];
|
|
116
|
+
if (line == null) {
|
|
117
|
+
console.error({
|
|
118
|
+
additionLines,
|
|
119
|
+
content,
|
|
120
|
+
i
|
|
121
|
+
});
|
|
122
|
+
throw new Error("pushContentLinesToDiff: Context line does not exist");
|
|
123
|
+
}
|
|
124
|
+
diff.deletionLines.push(line);
|
|
125
|
+
diff.additionLines.push(line);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const len = Math.max(content.deletions, content.additions);
|
|
129
|
+
for (let i = 0; i < len; i++) {
|
|
130
|
+
if (i < content.deletions) {
|
|
131
|
+
const line = deletionLines[content.deletionLineIndex + i];
|
|
132
|
+
if (line == null) {
|
|
133
|
+
console.error({
|
|
134
|
+
deletionLines,
|
|
135
|
+
content,
|
|
136
|
+
i
|
|
137
|
+
});
|
|
138
|
+
throw new Error("pushContentLinesToDiff: Deletion line does not exist");
|
|
139
|
+
}
|
|
140
|
+
diff.deletionLines.push(line);
|
|
141
|
+
}
|
|
142
|
+
if (i < content.additions) {
|
|
143
|
+
const line = additionLines[content.additionLineIndex + i];
|
|
144
|
+
if (line == null) {
|
|
145
|
+
console.error({
|
|
146
|
+
additionLines,
|
|
147
|
+
content,
|
|
148
|
+
i
|
|
149
|
+
});
|
|
150
|
+
throw new Error("pushContentLinesToDiff: Addition line does not exist");
|
|
151
|
+
}
|
|
152
|
+
diff.additionLines.push(line);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function pushResolveLinesToDiff(resolution, content, diff, deletionLines, additionLines) {
|
|
158
|
+
if (resolution === "deletions" || resolution === "both") for (let i = 0; i < content.deletions; i++) {
|
|
159
|
+
const line = deletionLines[content.deletionLineIndex + i];
|
|
160
|
+
if (line == null) {
|
|
161
|
+
console.error({
|
|
162
|
+
deletionLines,
|
|
163
|
+
content,
|
|
164
|
+
i
|
|
165
|
+
});
|
|
166
|
+
throw new Error("pushResolveLinesToDiff: Deletion line does not exist");
|
|
167
|
+
}
|
|
168
|
+
diff.deletionLines.push(line);
|
|
169
|
+
diff.additionLines.push(line);
|
|
170
|
+
}
|
|
171
|
+
if (resolution === "additions" || resolution === "both") for (let i = 0; i < content.additions; i++) {
|
|
172
|
+
const line = additionLines[content.additionLineIndex + i];
|
|
173
|
+
if (line == null) {
|
|
174
|
+
console.error({
|
|
175
|
+
additionLines,
|
|
176
|
+
content,
|
|
177
|
+
i
|
|
178
|
+
});
|
|
179
|
+
throw new Error("pushResolveLinesToDiff: Addition line does not exist");
|
|
180
|
+
}
|
|
181
|
+
diff.deletionLines.push(line);
|
|
182
|
+
diff.additionLines.push(line);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function advanceCursor(content, cursor, hunk) {
|
|
186
|
+
if (content.type === "context") {
|
|
187
|
+
cursor.nextAdditionLineIndex += content.lines;
|
|
188
|
+
cursor.nextDeletionLineIndex += content.lines;
|
|
189
|
+
cursor.nextAdditionStart += content.lines;
|
|
190
|
+
cursor.nextDeletionStart += content.lines;
|
|
191
|
+
cursor.splitLineCount += content.lines;
|
|
192
|
+
cursor.unifiedLineCount += content.lines;
|
|
193
|
+
hunk.additionCount += content.lines;
|
|
194
|
+
hunk.deletionCount += content.lines;
|
|
195
|
+
hunk.splitLineCount += content.lines;
|
|
196
|
+
hunk.unifiedLineCount += content.lines;
|
|
197
|
+
} else {
|
|
198
|
+
cursor.nextAdditionLineIndex += content.additions;
|
|
199
|
+
cursor.nextDeletionLineIndex += content.deletions;
|
|
200
|
+
cursor.nextAdditionStart += content.additions;
|
|
201
|
+
cursor.nextDeletionStart += content.deletions;
|
|
202
|
+
cursor.splitLineCount += Math.max(content.deletions, content.additions);
|
|
203
|
+
cursor.unifiedLineCount += content.deletions + content.additions;
|
|
204
|
+
hunk.deletionCount += content.deletions;
|
|
205
|
+
hunk.deletionLines += content.deletions;
|
|
206
|
+
hunk.additionCount += content.additions;
|
|
207
|
+
hunk.additionLines += content.additions;
|
|
208
|
+
hunk.splitLineCount += Math.max(content.deletions, content.additions);
|
|
209
|
+
hunk.unifiedLineCount += content.deletions + content.additions;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
export { resolveRegion };
|
|
215
|
+
//# sourceMappingURL=resolveRegion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveRegion.js","names":["resolvedDiff: FileDiffMetadata","cursor: CursorState","newHunk: Hunk","newContent: ContextContent"],"sources":["../../src/utils/resolveRegion.ts"],"sourcesContent":["import type {\n ChangeContent,\n ContextContent,\n FileDiffMetadata,\n Hunk,\n} from '../types';\n\ninterface RegionResolutionTarget {\n hunkIndex: number;\n startContentIndex: number;\n endContentIndex: number;\n resolution: 'deletions' | 'additions' | 'both';\n indexesToDelete?: Set<number>;\n}\n\ninterface CursorState {\n nextAdditionLineIndex: number;\n nextDeletionLineIndex: number;\n nextAdditionStart: number;\n nextDeletionStart: number;\n splitLineCount: number;\n unifiedLineCount: number;\n}\n\nexport function resolveRegion(\n diff: FileDiffMetadata,\n target: RegionResolutionTarget\n): FileDiffMetadata {\n const {\n resolution,\n hunkIndex,\n startContentIndex,\n endContentIndex,\n indexesToDelete = new Set(),\n } = target;\n const currentHunk = diff.hunks[hunkIndex];\n if (currentHunk == null) {\n console.error({ diff, hunkIndex });\n throw new Error(`resolveRegion: Invalid hunk index: ${hunkIndex}`);\n }\n\n if (\n startContentIndex < 0 ||\n endContentIndex >= currentHunk.hunkContent.length ||\n startContentIndex > endContentIndex\n ) {\n throw new Error(\n `resolveRegion: Invalid content range, ${startContentIndex}, ${endContentIndex}`\n );\n }\n\n const { hunks, additionLines, deletionLines } = diff;\n const resolvedDiff: FileDiffMetadata = {\n ...diff,\n hunks: [],\n deletionLines: [],\n additionLines: [],\n splitLineCount: 0,\n unifiedLineCount: 0,\n cacheKey:\n diff.cacheKey != null\n ? `${diff.cacheKey}:${resolution[0]}-${hunkIndex}:${startContentIndex}-${endContentIndex}`\n : undefined,\n };\n\n const cursor: CursorState = {\n nextAdditionLineIndex: 0,\n nextDeletionLineIndex: 0,\n nextAdditionStart: 1,\n nextDeletionStart: 1,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n const updatesEOFState =\n hunkIndex === hunks.length - 1 &&\n endContentIndex === currentHunk.hunkContent.length - 1;\n\n for (const [index, hunk] of hunks.entries()) {\n pushCollapsedContextLines(\n resolvedDiff,\n deletionLines,\n additionLines,\n hunk.deletionLineIndex - hunk.collapsedBefore,\n hunk.additionLineIndex - hunk.collapsedBefore,\n hunk.collapsedBefore\n );\n cursor.nextAdditionLineIndex += hunk.collapsedBefore;\n cursor.nextDeletionLineIndex += hunk.collapsedBefore;\n cursor.nextAdditionStart += hunk.collapsedBefore;\n cursor.nextDeletionStart += hunk.collapsedBefore;\n cursor.splitLineCount += hunk.collapsedBefore;\n cursor.unifiedLineCount += hunk.collapsedBefore;\n\n const newHunk: Hunk = {\n ...hunk,\n hunkContent: [],\n additionStart: cursor.nextAdditionStart,\n deletionStart: cursor.nextDeletionStart,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionCount: 0,\n deletionCount: 0,\n deletionLines: 0,\n additionLines: 0,\n splitLineStart: cursor.splitLineCount,\n unifiedLineStart: cursor.unifiedLineCount,\n splitLineCount: 0,\n unifiedLineCount: 0,\n };\n\n for (const [contentIndex, content] of hunk.hunkContent.entries()) {\n // If we are outside of the targeted hunk or content region\n if (\n index !== hunkIndex ||\n contentIndex < startContentIndex ||\n contentIndex > endContentIndex\n ) {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent = {\n ...content,\n additionLineIndex: cursor.nextAdditionLineIndex,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // If we are at an index to delete, replace with an empty context node\n else if (indexesToDelete.has(contentIndex)) {\n newHunk.hunkContent.push({\n type: 'context',\n lines: 0,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n });\n }\n // There's nothing to `resolve` with context nodes, so just push them as\n // they are\n else if (content.type === 'context') {\n pushContentLinesToDiff(\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n ...content,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n // Looks like we have a change to resolve and push\n else {\n pushResolveLinesToDiff(\n resolution,\n content,\n resolvedDiff,\n deletionLines,\n additionLines\n );\n const newContent: ContextContent = {\n type: 'context',\n lines:\n resolution === 'deletions'\n ? content.deletions\n : resolution === 'additions'\n ? content.additions\n : content.deletions + content.additions,\n deletionLineIndex: cursor.nextDeletionLineIndex,\n additionLineIndex: cursor.nextAdditionLineIndex,\n };\n newHunk.hunkContent.push(newContent);\n advanceCursor(newContent, cursor, newHunk);\n }\n }\n\n if (index === hunkIndex && updatesEOFState) {\n const noEOFCR =\n resolution === 'deletions'\n ? hunk.noEOFCRDeletions\n : hunk.noEOFCRAdditions;\n newHunk.noEOFCRAdditions = noEOFCR;\n newHunk.noEOFCRDeletions = noEOFCR;\n }\n\n resolvedDiff.hunks.push(newHunk);\n }\n\n const finalHunk = hunks.at(-1);\n if (finalHunk != null && !diff.isPartial) {\n pushCollapsedContextLines(\n resolvedDiff,\n deletionLines,\n additionLines,\n finalHunk.deletionLineIndex + finalHunk.deletionCount,\n finalHunk.additionLineIndex + finalHunk.additionCount,\n Math.min(\n deletionLines.length -\n (finalHunk.deletionLineIndex + finalHunk.deletionCount),\n additionLines.length -\n (finalHunk.additionLineIndex + finalHunk.additionCount)\n )\n );\n }\n\n resolvedDiff.splitLineCount = cursor.splitLineCount;\n resolvedDiff.unifiedLineCount = cursor.unifiedLineCount;\n\n return resolvedDiff;\n}\n\nfunction pushCollapsedContextLines(\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[],\n deletionLineIndex: number,\n additionLineIndex: number,\n lineCount: number\n) {\n for (let index = 0; index < lineCount; index++) {\n const deletionLine = deletionLines[deletionLineIndex + index];\n const additionLine = additionLines[additionLineIndex + index];\n if (deletionLine == null || additionLine == null) {\n throw new Error(\n 'pushCollapsedContextLines: missing collapsed context line'\n );\n }\n diff.deletionLines.push(deletionLine);\n diff.additionLines.push(additionLine);\n }\n}\n\nfunction pushContentLinesToDiff(\n content: ContextContent | ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (content.type === 'context') {\n for (let i = 0; i < content.lines; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushContentLinesToDiff: Context line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n } else {\n const len = Math.max(content.deletions, content.additions);\n for (let i = 0; i < len; i++) {\n if (i < content.deletions) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Deletion line does not exist'\n );\n }\n diff.deletionLines.push(line);\n }\n if (i < content.additions) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error(\n 'pushContentLinesToDiff: Addition line does not exist'\n );\n }\n diff.additionLines.push(line);\n }\n }\n }\n}\n\nfunction pushResolveLinesToDiff(\n resolution: 'deletions' | 'additions' | 'both',\n content: ChangeContent,\n diff: FileDiffMetadata,\n deletionLines: string[],\n additionLines: string[]\n) {\n if (resolution === 'deletions' || resolution === 'both') {\n for (let i = 0; i < content.deletions; i++) {\n const line = deletionLines[content.deletionLineIndex + i];\n if (line == null) {\n console.error({ deletionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Deletion line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n if (resolution === 'additions' || resolution === 'both') {\n for (let i = 0; i < content.additions; i++) {\n const line = additionLines[content.additionLineIndex + i];\n if (line == null) {\n console.error({ additionLines, content, i });\n throw new Error('pushResolveLinesToDiff: Addition line does not exist');\n }\n diff.deletionLines.push(line);\n diff.additionLines.push(line);\n }\n }\n}\n\nfunction advanceCursor(\n content: ChangeContent | ContextContent,\n cursor: CursorState,\n hunk: Hunk\n) {\n if (content.type === 'context') {\n cursor.nextAdditionLineIndex += content.lines;\n cursor.nextDeletionLineIndex += content.lines;\n cursor.nextAdditionStart += content.lines;\n cursor.nextDeletionStart += content.lines;\n cursor.splitLineCount += content.lines;\n cursor.unifiedLineCount += content.lines;\n\n hunk.additionCount += content.lines;\n hunk.deletionCount += content.lines;\n hunk.splitLineCount += content.lines;\n hunk.unifiedLineCount += content.lines;\n } else {\n cursor.nextAdditionLineIndex += content.additions;\n cursor.nextDeletionLineIndex += content.deletions;\n cursor.nextAdditionStart += content.additions;\n cursor.nextDeletionStart += content.deletions;\n cursor.splitLineCount += Math.max(content.deletions, content.additions);\n cursor.unifiedLineCount += content.deletions + content.additions;\n\n hunk.deletionCount += content.deletions;\n hunk.deletionLines += content.deletions;\n hunk.additionCount += content.additions;\n hunk.additionLines += content.additions;\n hunk.splitLineCount += Math.max(content.deletions, content.additions);\n hunk.unifiedLineCount += content.deletions + content.additions;\n }\n}\n"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ function trimPatchContext(patch, contextSize = 10) {
|
|
|
15
15
|
if (parsedHunkHeader != null) {
|
|
16
16
|
if (currentHunk != null) {
|
|
17
17
|
if (currentHunk.hunkLines.length > 0) {
|
|
18
|
-
flushContextLines(currentHunk, contextSize);
|
|
18
|
+
flushContextLines(currentHunk, contextSize, "trailing");
|
|
19
19
|
flushHunk(currentHunk, lines);
|
|
20
20
|
}
|
|
21
21
|
currentHunk = void 0;
|
|
@@ -26,7 +26,6 @@ function trimPatchContext(patch, contextSize = 10) {
|
|
|
26
26
|
const deletionCount = parseInt(parsedHunkHeader[2] ?? "1");
|
|
27
27
|
if (isNaN(additionStart) || isNaN(deletionStart) || isNaN(additionCount) || isNaN(deletionCount)) lines.push(line);
|
|
28
28
|
else currentHunk = {
|
|
29
|
-
hunkContextString: parsedHunkHeader[5] ?? "",
|
|
30
29
|
additionStart,
|
|
31
30
|
deletionStart,
|
|
32
31
|
additionCount: 0,
|
|
@@ -40,44 +39,44 @@ function trimPatchContext(patch, contextSize = 10) {
|
|
|
40
39
|
lines.push(line);
|
|
41
40
|
continue;
|
|
42
41
|
}
|
|
43
|
-
if (line.startsWith(" "))
|
|
44
|
-
|
|
45
|
-
if (currentHunk.hunkLines.length > 0 && currentHunk.contextLines.length
|
|
46
|
-
const
|
|
47
|
-
|
|
42
|
+
if (line.startsWith(" ")) currentHunk.contextLines.push(line);
|
|
43
|
+
else if (line !== "") {
|
|
44
|
+
if (currentHunk.hunkLines.length > 0 && currentHunk.contextLines.length > contextSize * 2) {
|
|
45
|
+
const omittedContextLineCount = currentHunk.contextLines.length - contextSize * 2;
|
|
46
|
+
const nextContextLines = currentHunk.contextLines.slice(-contextSize);
|
|
47
|
+
flushContextLines(currentHunk, contextSize, "trailing");
|
|
48
48
|
const { additionCount: emittedAdditionCount, deletionCount: emittedDeletionCount } = currentHunk;
|
|
49
49
|
flushHunk(currentHunk, lines);
|
|
50
|
-
removedItems.shift();
|
|
51
50
|
currentHunk = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
deletionStart: currentHunk.deletionStart + emittedDeletionCount + 1,
|
|
51
|
+
additionStart: currentHunk.additionStart + emittedAdditionCount + omittedContextLineCount,
|
|
52
|
+
deletionStart: currentHunk.deletionStart + emittedDeletionCount + omittedContextLineCount,
|
|
55
53
|
deletionCount: 0,
|
|
56
54
|
additionCount: 0,
|
|
57
|
-
contextLines:
|
|
55
|
+
contextLines: nextContextLines,
|
|
58
56
|
hunkLines: []
|
|
59
57
|
};
|
|
60
58
|
}
|
|
61
|
-
|
|
62
|
-
flushContextLines(currentHunk, contextSize);
|
|
59
|
+
flushContextLines(currentHunk, contextSize, currentHunk.hunkLines.length === 0 ? "leading" : "before-change");
|
|
63
60
|
currentHunk.hunkLines.push(line);
|
|
64
61
|
if (line.startsWith("+")) currentHunk.additionCount += 1;
|
|
65
62
|
else if (line.startsWith("-")) currentHunk.deletionCount += 1;
|
|
66
63
|
}
|
|
67
64
|
}
|
|
68
65
|
if (currentHunk != null && currentHunk.hunkLines.length > 0) {
|
|
69
|
-
flushContextLines(currentHunk, contextSize);
|
|
66
|
+
flushContextLines(currentHunk, contextSize, "trailing");
|
|
70
67
|
flushHunk(currentHunk, lines);
|
|
71
68
|
}
|
|
72
|
-
|
|
69
|
+
const result = lines.join("\n");
|
|
70
|
+
return patch.endsWith("\n") ? `${result}\n` : result;
|
|
73
71
|
}
|
|
74
|
-
function flushContextLines(hunk, contextSize) {
|
|
75
|
-
if (
|
|
72
|
+
function flushContextLines(hunk, contextSize, mode) {
|
|
73
|
+
if (mode === "leading" && hunk.contextLines.length > contextSize) {
|
|
76
74
|
const difference = hunk.contextLines.length - contextSize;
|
|
77
75
|
hunk.contextLines.splice(0, difference);
|
|
78
76
|
hunk.additionStart += difference;
|
|
79
77
|
hunk.deletionStart += difference;
|
|
80
|
-
}
|
|
78
|
+
}
|
|
79
|
+
if (mode === "trailing" && hunk.contextLines.length > contextSize) hunk.contextLines.length = contextSize;
|
|
81
80
|
if (hunk.contextLines.length > 0) {
|
|
82
81
|
hunk.hunkLines.push(...hunk.contextLines);
|
|
83
82
|
hunk.additionCount += hunk.contextLines.length;
|
|
@@ -87,7 +86,7 @@ function flushContextLines(hunk, contextSize) {
|
|
|
87
86
|
return hunk;
|
|
88
87
|
}
|
|
89
88
|
function flushHunk(hunk, lines) {
|
|
90
|
-
lines.push(`@@ -${formatHunkRange(hunk.deletionStart, hunk.deletionCount)} +${formatHunkRange(hunk.additionStart, hunk.additionCount)}
|
|
89
|
+
lines.push(`@@ -${formatHunkRange(hunk.deletionStart, hunk.deletionCount)} +${formatHunkRange(hunk.additionStart, hunk.additionCount)} @@`);
|
|
91
90
|
lines.push(...hunk.hunkLines);
|
|
92
91
|
}
|
|
93
92
|
function formatHunkRange(start, count) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trimPatchContext.js","names":["lines: string[]","currentHunk: CurrentHunk | undefined"],"sources":["../../src/utils/trimPatchContext.ts"],"sourcesContent":["import { HUNK_HEADER } from '../constants';\n\ninterface CurrentHunk {\n
|
|
1
|
+
{"version":3,"file":"trimPatchContext.js","names":["lines: string[]","currentHunk: CurrentHunk | undefined"],"sources":["../../src/utils/trimPatchContext.ts"],"sourcesContent":["import { HUNK_HEADER } from '../constants';\n\ninterface CurrentHunk {\n additionStart: number;\n deletionStart: number;\n additionCount: number;\n deletionCount: number;\n hunkLines: string[];\n contextLines: string[];\n}\n\ntype ContextFlushMode = 'before-change' | 'leading' | 'trailing';\n\n/**\n * A utility function to trim out excess context lines from a patch file. It\n * will maintain line numbers, and properly update the hunk context markers, as\n * well as be able to create new hunks where necessary if there's excessive\n * context between changes\n */\nexport function trimPatchContext(patch: string, contextSize = 10): string {\n const lines: string[] = [];\n\n let currentHunk: CurrentHunk | undefined;\n for (const line of patch.split('\\n')) {\n const parsedHunkHeader = line.match(HUNK_HEADER);\n // We've come across a new hunk boundary\n if (parsedHunkHeader != null) {\n // If we have an existing hunk, lets close it out\n // before setting up a new one\n if (currentHunk != null) {\n if (currentHunk.hunkLines.length > 0) {\n flushContextLines(currentHunk, contextSize, 'trailing');\n flushHunk(currentHunk, lines);\n }\n currentHunk = undefined;\n }\n\n const additionStart = parseInt(parsedHunkHeader[3]);\n const deletionStart = parseInt(parsedHunkHeader[1]);\n const additionCount = parseInt(parsedHunkHeader[4] ?? '1');\n const deletionCount = parseInt(parsedHunkHeader[2] ?? '1');\n\n // If we can't parse valid numbers out of the hunk header\n // lets just skip the hunk altogether\n if (\n isNaN(additionStart) ||\n isNaN(deletionStart) ||\n isNaN(additionCount) ||\n isNaN(deletionCount)\n ) {\n lines.push(line);\n } else {\n currentHunk = {\n additionStart,\n deletionStart,\n additionCount: 0,\n deletionCount: 0,\n hunkLines: [],\n contextLines: [],\n };\n }\n continue;\n }\n\n // If we don't have a current hunk, then we should just assume this is\n // general metadata\n if (currentHunk == null) {\n lines.push(line);\n continue;\n }\n\n // If we are dealing with a context line...\n if (line.startsWith(' ')) {\n currentHunk.contextLines.push(line);\n } else if (line !== '') {\n if (\n currentHunk.hunkLines.length > 0 &&\n currentHunk.contextLines.length > contextSize * 2\n ) {\n const omittedContextLineCount =\n currentHunk.contextLines.length - contextSize * 2;\n const nextContextLines = currentHunk.contextLines.slice(-contextSize);\n flushContextLines(currentHunk, contextSize, 'trailing');\n const {\n additionCount: emittedAdditionCount,\n deletionCount: emittedDeletionCount,\n } = currentHunk;\n flushHunk(currentHunk, lines);\n\n currentHunk = {\n additionStart:\n currentHunk.additionStart +\n emittedAdditionCount +\n omittedContextLineCount,\n deletionStart:\n currentHunk.deletionStart +\n emittedDeletionCount +\n omittedContextLineCount,\n deletionCount: 0,\n additionCount: 0,\n contextLines: nextContextLines,\n hunkLines: [],\n };\n }\n\n flushContextLines(\n currentHunk,\n contextSize,\n currentHunk.hunkLines.length === 0 ? 'leading' : 'before-change'\n );\n currentHunk.hunkLines.push(line);\n if (line.startsWith('+')) {\n currentHunk.additionCount += 1;\n } else if (line.startsWith('-')) {\n currentHunk.deletionCount += 1;\n }\n }\n }\n\n if (currentHunk != null && currentHunk.hunkLines.length > 0) {\n flushContextLines(currentHunk, contextSize, 'trailing');\n flushHunk(currentHunk, lines);\n }\n\n const result = lines.join('\\n');\n return patch.endsWith('\\n') ? `${result}\\n` : result;\n}\n\nfunction flushContextLines(\n hunk: CurrentHunk,\n contextSize: number,\n mode: ContextFlushMode\n) {\n if (mode === 'leading' && hunk.contextLines.length > contextSize) {\n const difference = hunk.contextLines.length - contextSize;\n hunk.contextLines.splice(0, difference);\n hunk.additionStart += difference;\n hunk.deletionStart += difference;\n }\n\n if (mode === 'trailing' && hunk.contextLines.length > contextSize) {\n hunk.contextLines.length = contextSize;\n }\n\n if (hunk.contextLines.length > 0) {\n hunk.hunkLines.push(...hunk.contextLines);\n hunk.additionCount += hunk.contextLines.length;\n hunk.deletionCount += hunk.contextLines.length;\n hunk.contextLines.length = 0;\n }\n return hunk;\n}\n\nfunction flushHunk(hunk: CurrentHunk, lines: string[]) {\n lines.push(\n `@@ -${formatHunkRange(hunk.deletionStart, hunk.deletionCount)} +${formatHunkRange(hunk.additionStart, hunk.additionCount)} @@`\n );\n lines.push(...hunk.hunkLines);\n}\n\nfunction formatHunkRange(start: number, count: number): string {\n return count === 1 ? `${start}` : `${start},${count}`;\n}\n"],"mappings":"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"}
|