mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
fix(tui): include surrounding context in edit approval preview (#1410)
This commit is contained in:
parent
b9258ee07d
commit
1c817df1e5
5 changed files with 61 additions and 10 deletions
5
.changeset/fix-edit-preview-context.md
Normal file
5
.changeset/fix-edit-preview-context.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Fix the edit approval preview shown by ctrl+e to include surrounding context lines, matching the summary panel.
|
||||
|
|
@ -27,7 +27,7 @@ import {
|
|||
} from '@moonshot-ai/pi-tui';
|
||||
|
||||
import { highlightLines, langFromPath } from '#/tui/components/media/code-highlight';
|
||||
import { renderDiffLines } from '#/tui/components/media/diff-preview';
|
||||
import { renderDiffLinesClustered } from '#/tui/components/media/diff-preview';
|
||||
import type { DiffDisplayBlock, FileContentDisplayBlock } from '#/tui/reverse-rpc/types';
|
||||
import { currentTheme } from '#/tui/theme';
|
||||
import { printableChar } from '#/tui/utils/printable-key';
|
||||
|
|
@ -218,17 +218,19 @@ function buildBody(block: ApprovalPreviewBlock): BuiltBody {
|
|||
}
|
||||
|
||||
function buildDiffBody(block: DiffDisplayBlock): BuiltBody {
|
||||
// renderDiffLines emits a `+N -M path` header on its first line followed
|
||||
// by every changed line. We pull the header out into the viewer chrome so
|
||||
// the body is purely scrollable diff content; this also means we don't
|
||||
// double-render the path.
|
||||
const rendered = renderDiffLines(
|
||||
// renderDiffLinesClustered emits a `+N -M path` header on its first line
|
||||
// followed by every changed line plus surrounding context. We pull the
|
||||
// header out into the viewer chrome so the body is purely scrollable diff
|
||||
// content; this also means we don't double-render the path.
|
||||
const rendered = renderDiffLinesClustered(
|
||||
block.old_text,
|
||||
block.new_text,
|
||||
block.path,
|
||||
false,
|
||||
block.old_start ?? 1,
|
||||
block.new_start ?? 1,
|
||||
{
|
||||
contextLines: 3,
|
||||
oldStart: block.old_start ?? 1,
|
||||
newStart: block.new_start ?? 1,
|
||||
},
|
||||
);
|
||||
const [header = '', ...rest] = rendered;
|
||||
return { lines: rest, title: stripLeadingSpace(header) };
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ export interface ClusteredDiffOptions {
|
|||
readonly maxLines?: number;
|
||||
readonly isIncomplete?: boolean;
|
||||
readonly expandKeyHint?: string;
|
||||
readonly oldStart?: number;
|
||||
readonly newStart?: number;
|
||||
}
|
||||
|
||||
interface Cluster {
|
||||
|
|
@ -239,7 +241,13 @@ export function renderDiffLinesClustered(
|
|||
const s = makeDiffStyles();
|
||||
const contextLines = opts.contextLines ?? 3;
|
||||
const maxLines = opts.maxLines;
|
||||
const diffLines = computeDiffLines(oldText, newText, 1, 1, opts.isIncomplete ?? false);
|
||||
const diffLines = computeDiffLines(
|
||||
oldText,
|
||||
newText,
|
||||
opts.oldStart ?? 1,
|
||||
opts.newStart ?? 1,
|
||||
opts.isIncomplete ?? false,
|
||||
);
|
||||
const { clusters, changedCount, addedCount, removedCount } = buildClusters(
|
||||
diffLines,
|
||||
contextLines,
|
||||
|
|
|
|||
|
|
@ -138,6 +138,26 @@ describe('ApprovalPreviewViewer', () => {
|
|||
expect(text).toContain('BETA');
|
||||
});
|
||||
|
||||
it('shows surrounding context lines for a diff block', () => {
|
||||
const viewer = makeViewer({
|
||||
block: {
|
||||
type: 'diff',
|
||||
path: 'src/foo.ts',
|
||||
old_text: ['before1', 'before2', 'old', 'after1', 'after2'].join('\n'),
|
||||
new_text: ['before1', 'before2', 'new', 'after1', 'after2'].join('\n'),
|
||||
},
|
||||
rows: 24,
|
||||
});
|
||||
|
||||
const text = strip(viewer.render(100).join('\n'));
|
||||
expect(text).toContain('before1');
|
||||
expect(text).toContain('before2');
|
||||
expect(text).toContain('old');
|
||||
expect(text).toContain('new');
|
||||
expect(text).toContain('after1');
|
||||
expect(text).toContain('after2');
|
||||
});
|
||||
|
||||
// Sanity: rendering is a pure slice — repeated render() calls without
|
||||
// input changes produce the same output, no incremental state drift.
|
||||
it('renders deterministically across repeated calls', () => {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,22 @@ describe('renderDiffLinesClustered', () => {
|
|||
expect(text).toContain('ctrl+o to expand');
|
||||
});
|
||||
|
||||
it('respects oldStart and newStart for line numbers', () => {
|
||||
const text = stripAnsi(
|
||||
renderDiffLinesClustered('A\nB\nC', 'A\nX\nC', 'f.ts', {
|
||||
contextLines: 1,
|
||||
oldStart: 10,
|
||||
newStart: 20,
|
||||
}).join('\n'),
|
||||
);
|
||||
// Context lines keep the new (post-edit) line numbers from newStart;
|
||||
// deleted lines use oldStart; added lines use newStart.
|
||||
expect(text).toContain(' 20 A');
|
||||
expect(text).toContain(' 11 - B');
|
||||
expect(text).toContain(' 21 + X');
|
||||
expect(text).toContain(' 22 C');
|
||||
});
|
||||
|
||||
it('truncates at cluster boundary and appends the ctrl+o footer when maxLines is set', () => {
|
||||
const oldLines: string[] = [];
|
||||
for (let i = 1; i <= 50; i++) oldLines.push(`L${String(i)}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue