mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 12:40:44 +00:00
feat: make DiffRenderer respect ui.showLineNumbers setting
This commit is contained in:
parent
6eb16c0bcf
commit
4c6780b79d
4 changed files with 123 additions and 14 deletions
|
|
@ -9,6 +9,7 @@ import { render } from 'ink-testing-library';
|
|||
import { DiffRenderer } from './DiffRenderer.js';
|
||||
import * as CodeColorizer from '../../utils/CodeColorizer.js';
|
||||
import { vi } from 'vitest';
|
||||
import type { LoadedSettings } from '../../../config/settings.js';
|
||||
|
||||
describe('<OverflowProvider><DiffRenderer /></OverflowProvider>', () => {
|
||||
const mockColorizeCode = vi.spyOn(CodeColorizer, 'colorizeCode');
|
||||
|
|
@ -45,6 +46,7 @@ index 0000000..e69de29
|
|||
undefined,
|
||||
80,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -73,6 +75,7 @@ index 0000000..e69de29
|
|||
undefined,
|
||||
80,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -97,6 +100,7 @@ index 0000000..e69de29
|
|||
undefined,
|
||||
80,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -362,4 +366,86 @@ fileDiff Index: Dockerfile
|
|||
2 RUN npm install
|
||||
3 RUN npm run build`);
|
||||
});
|
||||
|
||||
describe('showLineNumbers setting', () => {
|
||||
const diffContent = `
|
||||
diff --git a/test.txt b/test.txt
|
||||
index 0000001..0000002 100644
|
||||
--- a/test.txt
|
||||
+++ b/test.txt
|
||||
@@ -1,2 +1,2 @@
|
||||
-old line 1
|
||||
+new line 1
|
||||
context line 2
|
||||
`;
|
||||
|
||||
it('should show line numbers by default when settings is undefined', () => {
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
diffContent={diffContent}
|
||||
filename="test.txt"
|
||||
terminalWidth={80}
|
||||
/>
|
||||
</OverflowProvider>,
|
||||
);
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('1 -');
|
||||
expect(output).toContain('1 +');
|
||||
expect(output).toContain('2 ');
|
||||
});
|
||||
|
||||
it('should show line numbers when showLineNumbers is true', () => {
|
||||
const mockSettings = {
|
||||
merged: {
|
||||
ui: {
|
||||
showLineNumbers: true,
|
||||
},
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
diffContent={diffContent}
|
||||
filename="test.txt"
|
||||
terminalWidth={80}
|
||||
settings={mockSettings}
|
||||
/>
|
||||
</OverflowProvider>,
|
||||
);
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('1 -');
|
||||
expect(output).toContain('1 +');
|
||||
expect(output).toContain('2 ');
|
||||
});
|
||||
|
||||
it('should hide line numbers when showLineNumbers is false', () => {
|
||||
const mockSettings = {
|
||||
merged: {
|
||||
ui: {
|
||||
showLineNumbers: false,
|
||||
},
|
||||
},
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
diffContent={diffContent}
|
||||
filename="test.txt"
|
||||
terminalWidth={80}
|
||||
settings={mockSettings}
|
||||
/>
|
||||
</OverflowProvider>,
|
||||
);
|
||||
const output = lastFrame();
|
||||
// Line numbers should not be present
|
||||
expect(output).not.toMatch(/^\s*\d+\s*[-+]/m);
|
||||
// But the content should still be there
|
||||
expect(output).toContain('old line 1');
|
||||
expect(output).toContain('new line 1');
|
||||
expect(output).toContain('context line 2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
|||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||
import { theme as semanticTheme } from '../../semantic-colors.js';
|
||||
import type { Theme } from '../../themes/theme.js';
|
||||
import type { LoadedSettings } from '../../../config/settings.js';
|
||||
|
||||
interface DiffLine {
|
||||
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
||||
|
|
@ -86,6 +87,7 @@ interface DiffRendererProps {
|
|||
availableTerminalHeight?: number;
|
||||
terminalWidth: number;
|
||||
theme?: Theme;
|
||||
settings?: LoadedSettings;
|
||||
}
|
||||
|
||||
const DEFAULT_TAB_WIDTH = 4; // Spaces per tab for normalization
|
||||
|
|
@ -97,6 +99,7 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
|||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
theme,
|
||||
settings,
|
||||
}) => {
|
||||
const screenReaderEnabled = useIsScreenReaderEnabled();
|
||||
if (!diffContent || typeof diffContent !== 'string') {
|
||||
|
|
@ -157,6 +160,7 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
|||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
theme,
|
||||
settings,
|
||||
);
|
||||
} else {
|
||||
renderedOutput = renderDiffContent(
|
||||
|
|
@ -165,6 +169,7 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
|||
tabWidth,
|
||||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
settings,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +182,7 @@ const renderDiffContent = (
|
|||
tabWidth = DEFAULT_TAB_WIDTH,
|
||||
availableTerminalHeight: number | undefined,
|
||||
terminalWidth: number,
|
||||
settings?: LoadedSettings,
|
||||
) => {
|
||||
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
||||
const normalizedLines = parsedLines.map((line) => ({
|
||||
|
|
@ -201,6 +207,8 @@ const renderDiffContent = (
|
|||
);
|
||||
}
|
||||
|
||||
const showLineNumbers = settings?.merged.ui?.showLineNumbers ?? true;
|
||||
|
||||
const maxLineNumber = Math.max(
|
||||
0,
|
||||
...displayableLines.map((l) => l.oldLine ?? 0),
|
||||
|
|
@ -299,18 +307,20 @@ const renderDiffContent = (
|
|||
|
||||
acc.push(
|
||||
<Box key={lineKey} flexDirection="row">
|
||||
<Text
|
||||
color={semanticTheme.text.secondary}
|
||||
backgroundColor={
|
||||
line.type === 'add'
|
||||
? semanticTheme.background.diff.added
|
||||
: line.type === 'del'
|
||||
? semanticTheme.background.diff.removed
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{gutterNumStr.padStart(gutterWidth)}{' '}
|
||||
</Text>
|
||||
{showLineNumbers && (
|
||||
<Text
|
||||
color={semanticTheme.text.secondary}
|
||||
backgroundColor={
|
||||
line.type === 'add'
|
||||
? semanticTheme.background.diff.added
|
||||
: line.type === 'del'
|
||||
? semanticTheme.background.diff.removed
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{gutterNumStr.padStart(gutterWidth)}{' '}
|
||||
</Text>
|
||||
)}
|
||||
{line.type === 'context' ? (
|
||||
<>
|
||||
<Text>{prefixSymbol} </Text>
|
||||
|
|
|
|||
|
|
@ -58,10 +58,17 @@ vi.mock('../GeminiRespondingSpinner.js', () => ({
|
|||
vi.mock('./DiffRenderer.js', () => ({
|
||||
DiffRenderer: function MockDiffRenderer({
|
||||
diffContent,
|
||||
settings,
|
||||
}: {
|
||||
diffContent: string;
|
||||
settings?: unknown;
|
||||
}) {
|
||||
return <Text>MockDiff:{diffContent}</Text>;
|
||||
return (
|
||||
<Text>
|
||||
MockDiff:{diffContent}
|
||||
{settings ? ':withSettings' : ''}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}));
|
||||
vi.mock('../../utils/MarkdownDisplay.js', () => ({
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import {
|
|||
TOOL_STATUS,
|
||||
} from '../../constants.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { SettingsContext } from '../../contexts/SettingsContext.js';
|
||||
import type { LoadedSettings } from '../../../config/settings.js';
|
||||
|
||||
const STATIC_HEIGHT = 1;
|
||||
const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc.
|
||||
|
|
@ -210,12 +212,14 @@ const DiffResultRenderer: React.FC<{
|
|||
data: { fileDiff: string; fileName: string };
|
||||
availableHeight?: number;
|
||||
childWidth: number;
|
||||
}> = ({ data, availableHeight, childWidth }) => (
|
||||
settings?: LoadedSettings;
|
||||
}> = ({ data, availableHeight, childWidth, settings }) => (
|
||||
<DiffRenderer
|
||||
diffContent={data.fileDiff}
|
||||
filename={data.fileName}
|
||||
availableTerminalHeight={availableHeight}
|
||||
terminalWidth={childWidth}
|
||||
settings={settings}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -243,6 +247,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
|||
ptyId,
|
||||
config,
|
||||
}) => {
|
||||
const settings = React.useContext(SettingsContext);
|
||||
const isThisShellFocused =
|
||||
(name === SHELL_COMMAND_NAME || name === 'Shell') &&
|
||||
status === ToolCallStatus.Executing &&
|
||||
|
|
@ -349,6 +354,7 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
|||
data={displayRenderer.data}
|
||||
availableHeight={availableHeight}
|
||||
childWidth={childWidth}
|
||||
settings={settings}
|
||||
/>
|
||||
)}
|
||||
{displayRenderer.type === 'ansi' && (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue