diff --git a/packages/diff-view/src/parts/ApplyEditInput/ApplyEditInput.ts b/packages/diff-view/src/parts/ApplyEditInput/ApplyEditInput.ts index be8ad1f9..6fdc2919 100644 --- a/packages/diff-view/src/parts/ApplyEditInput/ApplyEditInput.ts +++ b/packages/diff-view/src/parts/ApplyEditInput/ApplyEditInput.ts @@ -29,7 +29,7 @@ export const applyEditInput = async (state: DiffViewState, inputValue: string): const totalLineCountRight = getLineCount(contentRight) const canComputeInlineDiff = state.renderModeLeft === 'text' && !state.errorLeftMessage const { inlineChanges, totalLineCount } = canComputeInlineDiff - ? await getInlineDiffState(state.contentLeft, contentRight) + ? await getInlineDiffState(state.contentLeft, contentRight, state.diffMode) : { inlineChanges: [], totalLineCount: Math.max(totalLineCountLeft, totalLineCountRight), diff --git a/packages/diff-view/src/parts/DeleteLeft/DeleteLeft.ts b/packages/diff-view/src/parts/DeleteLeft/DeleteLeft.ts index d2a48ea6..e4c5e0d6 100644 --- a/packages/diff-view/src/parts/DeleteLeft/DeleteLeft.ts +++ b/packages/diff-view/src/parts/DeleteLeft/DeleteLeft.ts @@ -42,7 +42,7 @@ export const deleteLeft = async (state: DiffViewState): Promise = const totalLineCountRight = getLineCount(contentRight) const canComputeInlineDiff = state.renderModeLeft === 'text' && !state.errorLeftMessage const { inlineChanges, totalLineCount } = canComputeInlineDiff - ? await getInlineDiffState(state.contentLeft, contentRight) + ? await getInlineDiffState(state.contentLeft, contentRight, state.diffMode) : { inlineChanges: [], totalLineCount: Math.max(totalLineCountLeft, totalLineCountRight), diff --git a/packages/diff-view/src/parts/DeleteRight/DeleteRight.ts b/packages/diff-view/src/parts/DeleteRight/DeleteRight.ts index 71175ca5..e7421418 100644 --- a/packages/diff-view/src/parts/DeleteRight/DeleteRight.ts +++ b/packages/diff-view/src/parts/DeleteRight/DeleteRight.ts @@ -42,7 +42,7 @@ export const deleteRight = async (state: DiffViewState): Promise const totalLineCountRight = getLineCount(contentRight) const canComputeInlineDiff = state.renderModeLeft === 'text' && !state.errorLeftMessage const { inlineChanges, totalLineCount } = canComputeInlineDiff - ? await getInlineDiffState(state.contentLeft, contentRight) + ? await getInlineDiffState(state.contentLeft, contentRight, state.diffMode) : { inlineChanges: [], totalLineCount: Math.max(totalLineCountLeft, totalLineCountRight), diff --git a/packages/diff-view/src/parts/GetInlineDiffState/GetInlineDiffState.ts b/packages/diff-view/src/parts/GetInlineDiffState/GetInlineDiffState.ts index d6c9b05c..34d53fc7 100644 --- a/packages/diff-view/src/parts/GetInlineDiffState/GetInlineDiffState.ts +++ b/packages/diff-view/src/parts/GetInlineDiffState/GetInlineDiffState.ts @@ -1,16 +1,19 @@ +import type { DiffMode } from '../DiffViewState/DiffViewState.ts' import type { InlineDiffChange } from '../InlineDiffChange/InlineDiffChange.ts' import { getInlineDiffChanges } from '../GetInlineDiffChanges/GetInlineDiffChanges.ts' +import { getInlineDiffRows } from '../GetInlineDiffRows/GetInlineDiffRows.ts' import { getVisibleInlineDiffRows } from '../GetVisibleInlineDiffRows/GetVisibleInlineDiffRows.ts' export const getInlineDiffState = async ( contentLeft: string, contentRight: string, + diffMode: DiffMode, ): Promise<{ readonly inlineChanges: readonly InlineDiffChange[]; readonly totalLineCount: number }> => { const linesLeft = contentLeft ? contentLeft.split('\n') : [''] const linesRight = contentRight ? contentRight.split('\n') : [''] const inlineChanges = await getInlineDiffChanges(linesLeft, linesRight) return { inlineChanges, - totalLineCount: Math.max(getVisibleInlineDiffRows(inlineChanges).length, 1), + totalLineCount: diffMode === 'inline' ? getInlineDiffRows(contentLeft, contentRight).length : Math.max(getVisibleInlineDiffRows(inlineChanges).length, 1), } } diff --git a/packages/diff-view/src/parts/ReloadContent/ReloadContent.ts b/packages/diff-view/src/parts/ReloadContent/ReloadContent.ts index 4a5d1184..cdfa9553 100644 --- a/packages/diff-view/src/parts/ReloadContent/ReloadContent.ts +++ b/packages/diff-view/src/parts/ReloadContent/ReloadContent.ts @@ -35,7 +35,7 @@ export const reloadContent = async ( errorRightCodeFrame: string, errorRightStack: string, ): Promise => { - const { assetDir, height, itemHeight, knownImageExtensions, minimumSliderSize, minLineY, platform, uriLeft, uriRight } = state + const { assetDir, diffMode, height, itemHeight, knownImageExtensions, minimumSliderSize, minLineY, platform, uriLeft, uriRight } = state const renderModeLeft = getRenderMode(uriLeft, knownImageExtensions) const renderModeRight = getRenderMode(uriRight, knownImageExtensions) const displayedContentLeft = getDisplayedContent(contentLeft, errorLeftMessage, errorLeftCodeFrame, errorLeftStack) @@ -45,7 +45,7 @@ export const reloadContent = async ( const gutterWidthVariable = getGutterWidthVariable(Math.max(totalLineCountLeft, totalLineCountRight)) const canComputeInlineDiff = renderModeLeft === 'text' && renderModeRight === 'text' && !errorLeftMessage && !errorRightMessage const { inlineChanges, totalLineCount } = canComputeInlineDiff - ? await getInlineDiffState(contentLeft, contentRight) + ? await getInlineDiffState(contentLeft, contentRight, diffMode) : { inlineChanges: [], totalLineCount: Math.max(totalLineCountLeft, totalLineCountRight), diff --git a/packages/diff-view/test/GetInlineDiffState.test.ts b/packages/diff-view/test/GetInlineDiffState.test.ts new file mode 100644 index 00000000..f2aaa74f --- /dev/null +++ b/packages/diff-view/test/GetInlineDiffState.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from '@jest/globals' +import { DiffWorker } from '@lvce-editor/rpc-registry' +import { getInlineDiffState } from '../src/parts/GetInlineDiffState/GetInlineDiffState.ts' + +test('inline replacements use separate display rows for the original and modified lines', async () => { + using mockRpc = DiffWorker.registerMockRpc({ + 'Diff.diffInline': async () => [ + { leftIndex: 0, rightIndex: 0, type: 2 }, + { leftIndex: 0, rightIndex: 0, type: 1 }, + ], + }) + const inline = await getInlineDiffState('old', 'new', 'inline') + const sideBySide = await getInlineDiffState('old', 'new', 'side-by-side') + expect(inline.totalLineCount).toBe(2) + expect(sideBySide.totalLineCount).toBe(1) + expect(mockRpc.invocations).toHaveLength(2) +}) + +test('inline scrolling includes conflict decoration rows', async () => { + using mockRpc = DiffWorker.registerMockRpc({ 'Diff.diffInline': async () => [] }) + const content = '<<<<<<< HEAD\ncurrent\n=======\nincoming\n>>>>>>> branch' + const result = await getInlineDiffState(content, content, 'inline') + expect(result.totalLineCount).toBe(7) + expect(mockRpc.invocations).toHaveLength(1) +}) diff --git a/packages/diff-view/test/LoadContent.test.ts b/packages/diff-view/test/LoadContent.test.ts index 0a7f2c14..c778c62e 100644 --- a/packages/diff-view/test/LoadContent.test.ts +++ b/packages/diff-view/test/LoadContent.test.ts @@ -279,10 +279,10 @@ test('loadContent expands total line count for inline mode when replacements spl contentLeft: 'same\nbefore\nshared', contentRight: 'same\nafter\nshared', diffMode: 'inline', - finalDeltaY: 0, + finalDeltaY: 20, maxLineY: 3, minLineY: 0, - totalLineCount: 3, + totalLineCount: 4, visibleLinesLeft: [ { lineNumber: 1, diff --git a/packages/e2e/src/diff.inline-scroll-after-edit.ts b/packages/e2e/src/diff.inline-scroll-after-edit.ts new file mode 100644 index 00000000..90f9e107 --- /dev/null +++ b/packages/e2e/src/diff.inline-scroll-after-edit.ts @@ -0,0 +1,21 @@ +import type { Test } from '@lvce-editor/test-with-playwright' + +export const name = 'diff.inline-scroll-after-edit' + +export const test: Test = async ({ Command, DiffView, expect, FileSystem, Locator, Workspace }) => { + const tmpDir = await FileSystem.getTmpDir() + const before = Array.from({ length: 120 }, (_, index) => `old line ${index}`).join('\n') + const after = Array.from({ length: 120 }, (_, index) => `new line ${index}`).join('\n') + await FileSystem.writeFile(`${tmpDir}/before.txt`, before) + await FileSystem.writeFile(`${tmpDir}/after.txt`, after) + await Workspace.setPath(tmpDir) + await DiffView.open(`${tmpDir}/before.txt`, `${tmpDir}/after.txt`) + await Command.execute('DiffView.setDiffMode', 'inline') + await Command.execute('DiffView.setCursorPosition', 0, 0) + await Command.execute('DiffView.insertLineBreak') + + const rows = Locator('.InlineDiffEditor .DiffEditorRows') + await rows.dispatchEvent('wheel', { bubbles: true, deltaMode: 0, deltaY: 9_999_999 } as unknown as string) + await expect(rows).toContainText('- old line 119') + await expect(rows).toContainText('+ new line 119') +}