mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 17:27:54 +00:00
chore: sync gemini-cli v0.1.19
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
MaxSizedBox,
|
||||
MINIMUM_MAX_HEIGHT,
|
||||
} from '../components/shared/MaxSizedBox.js';
|
||||
import { LoadedSettings } from '../../config/settings.js';
|
||||
|
||||
// Configure theming and parsing utilities.
|
||||
const lowlight = createLowlight(common);
|
||||
@@ -129,9 +130,11 @@ export function colorizeCode(
|
||||
availableHeight?: number,
|
||||
maxWidth?: number,
|
||||
theme?: Theme,
|
||||
settings?: LoadedSettings,
|
||||
): React.ReactNode {
|
||||
const codeToHighlight = code.replace(/\n$/, '');
|
||||
const activeTheme = theme || themeManager.getActiveTheme();
|
||||
const showLineNumbers = settings?.merged.showLineNumbers ?? true;
|
||||
|
||||
try {
|
||||
// Render the HAST tree using the adapted theme
|
||||
@@ -167,12 +170,14 @@ export function colorizeCode(
|
||||
|
||||
return (
|
||||
<Box key={index}>
|
||||
<Text color={activeTheme.colors.Gray}>
|
||||
{`${String(index + 1 + hiddenLinesCount).padStart(
|
||||
padWidth,
|
||||
' ',
|
||||
)} `}
|
||||
</Text>
|
||||
{showLineNumbers && (
|
||||
<Text color={activeTheme.colors.Gray}>
|
||||
{`${String(index + 1 + hiddenLinesCount).padStart(
|
||||
padWidth,
|
||||
' ',
|
||||
)} `}
|
||||
</Text>
|
||||
)}
|
||||
<Text color={activeTheme.defaultColor} wrap="wrap">
|
||||
{contentToRender}
|
||||
</Text>
|
||||
@@ -198,9 +203,11 @@ export function colorizeCode(
|
||||
>
|
||||
{lines.map((line, index) => (
|
||||
<Box key={index}>
|
||||
<Text color={activeTheme.defaultColor}>
|
||||
{`${String(index + 1).padStart(padWidth, ' ')} `}
|
||||
</Text>
|
||||
{showLineNumbers && (
|
||||
<Text color={activeTheme.defaultColor}>
|
||||
{`${String(index + 1).padStart(padWidth, ' ')} `}
|
||||
</Text>
|
||||
)}
|
||||
<Text color={activeTheme.colors.Gray}>{line}</Text>
|
||||
</Box>
|
||||
))}
|
||||
|
||||
@@ -18,6 +18,7 @@ export class ConsolePatcher {
|
||||
private originalConsoleWarn = console.warn;
|
||||
private originalConsoleError = console.error;
|
||||
private originalConsoleDebug = console.debug;
|
||||
private originalConsoleInfo = console.info;
|
||||
|
||||
private params: ConsolePatcherParams;
|
||||
|
||||
@@ -30,6 +31,7 @@ export class ConsolePatcher {
|
||||
console.warn = this.patchConsoleMethod('warn', this.originalConsoleWarn);
|
||||
console.error = this.patchConsoleMethod('error', this.originalConsoleError);
|
||||
console.debug = this.patchConsoleMethod('debug', this.originalConsoleDebug);
|
||||
console.info = this.patchConsoleMethod('info', this.originalConsoleInfo);
|
||||
}
|
||||
|
||||
cleanup = () => {
|
||||
@@ -37,13 +39,14 @@ export class ConsolePatcher {
|
||||
console.warn = this.originalConsoleWarn;
|
||||
console.error = this.originalConsoleError;
|
||||
console.debug = this.originalConsoleDebug;
|
||||
console.info = this.originalConsoleInfo;
|
||||
};
|
||||
|
||||
private formatArgs = (args: unknown[]): string => util.format(...args);
|
||||
|
||||
private patchConsoleMethod =
|
||||
(
|
||||
type: 'log' | 'warn' | 'error' | 'debug',
|
||||
type: 'log' | 'warn' | 'error' | 'debug' | 'info',
|
||||
originalMethod: (...args: unknown[]) => void,
|
||||
) =>
|
||||
(...args: unknown[]) => {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import { render } from 'ink-testing-library';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { MarkdownDisplay } from './MarkdownDisplay.js';
|
||||
import { LoadedSettings } from '../../config/settings.js';
|
||||
import { SettingsContext } from '../contexts/SettingsContext.js';
|
||||
|
||||
describe('<MarkdownDisplay />', () => {
|
||||
const baseProps = {
|
||||
@@ -15,19 +17,32 @@ describe('<MarkdownDisplay />', () => {
|
||||
availableTerminalHeight: 40,
|
||||
};
|
||||
|
||||
const mockSettings = new LoadedSettings(
|
||||
{ path: '', settings: {} },
|
||||
{ path: '', settings: {} },
|
||||
{ path: '', settings: {} },
|
||||
[],
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders nothing for empty text', () => {
|
||||
const { lastFrame } = render(<MarkdownDisplay {...baseProps} text="" />);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text="" />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders a simple paragraph', () => {
|
||||
const text = 'Hello, world.';
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -40,7 +55,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
#### Header 4
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -48,7 +65,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
it('renders a fenced code block with a language', () => {
|
||||
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```';
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -56,7 +75,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
it('renders a fenced code block without a language', () => {
|
||||
const text = '```\nplain text\n```';
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -64,7 +85,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
it('handles unclosed (pending) code blocks', () => {
|
||||
const text = '```typescript\nlet y = 2;';
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} isPending={true} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} isPending={true} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -76,7 +99,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
+ item C
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -88,7 +113,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
* Level 3
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -99,7 +126,9 @@ describe('<MarkdownDisplay />', () => {
|
||||
2. Second item
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -113,7 +142,9 @@ World
|
||||
Test
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -126,7 +157,9 @@ Test
|
||||
| Cell 3 | Cell 4 |
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -138,7 +171,9 @@ Some text before.
|
||||
|---|
|
||||
| 1 | 2 |`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -148,7 +183,9 @@ Some text before.
|
||||
|
||||
Paragraph 2.`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
@@ -169,8 +206,39 @@ some code
|
||||
Another paragraph.
|
||||
`;
|
||||
const { lastFrame } = render(
|
||||
<MarkdownDisplay {...baseProps} text={text} />,
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('hides line numbers in code blocks when showLineNumbers is false', () => {
|
||||
const text = '```javascript\nconst x = 1;\n```';
|
||||
const settings = new LoadedSettings(
|
||||
{ path: '', settings: {} },
|
||||
{ path: '', settings: { showLineNumbers: false } },
|
||||
{ path: '', settings: {} },
|
||||
[],
|
||||
);
|
||||
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
expect(lastFrame()).not.toContain(' 1 ');
|
||||
});
|
||||
|
||||
it('shows line numbers in code blocks by default', () => {
|
||||
const text = '```javascript\nconst x = 1;\n```';
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
</SettingsContext.Provider>,
|
||||
);
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
expect(lastFrame()).toContain(' 1 ');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Colors } from '../colors.js';
|
||||
import { colorizeCode } from './CodeColorizer.js';
|
||||
import { TableRenderer } from './TableRenderer.js';
|
||||
import { RenderInline } from './InlineMarkdownRenderer.js';
|
||||
import { useSettings } from '../contexts/SettingsContext.js';
|
||||
|
||||
interface MarkdownDisplayProps {
|
||||
text: string;
|
||||
@@ -298,6 +299,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
}) => {
|
||||
const settings = useSettings();
|
||||
const MIN_LINES_FOR_MESSAGE = 1; // Minimum lines to show before the "generating more" message
|
||||
const RESERVED_LINES = 2; // Lines reserved for the message itself and potential padding
|
||||
|
||||
@@ -322,6 +324,8 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
lang,
|
||||
availableTerminalHeight,
|
||||
terminalWidth - CODE_BLOCK_PREFIX_PADDING,
|
||||
undefined,
|
||||
settings,
|
||||
);
|
||||
return (
|
||||
<Box paddingLeft={CODE_BLOCK_PREFIX_PADDING} flexDirection="column">
|
||||
@@ -338,6 +342,8 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
lang,
|
||||
availableTerminalHeight,
|
||||
terminalWidth - CODE_BLOCK_PREFIX_PADDING,
|
||||
undefined,
|
||||
settings,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -23,6 +23,8 @@ exports[`<MarkdownDisplay /> > handles a table at the end of the input 1`] = `
|
||||
|
||||
exports[`<MarkdownDisplay /> > handles unclosed (pending) code blocks 1`] = `" 1 let y = 2;"`;
|
||||
|
||||
exports[`<MarkdownDisplay /> > hides line numbers in code blocks when showLineNumbers is false 1`] = `" const x = 1;"`;
|
||||
|
||||
exports[`<MarkdownDisplay /> > inserts a single space between paragraphs 1`] = `
|
||||
"Paragraph 1.
|
||||
|
||||
@@ -87,3 +89,5 @@ exports[`<MarkdownDisplay /> > renders unordered lists with different markers 1`
|
||||
+ item C
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<MarkdownDisplay /> > shows line numbers in code blocks by default 1`] = `" 1 const x = 1;"`;
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
isAtCommand,
|
||||
isSlashCommand,
|
||||
copyToClipboard,
|
||||
getUrlOpenCommand,
|
||||
} from './commandUtils.js';
|
||||
|
||||
// Mock child_process
|
||||
@@ -342,4 +343,42 @@ describe('commandUtils', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUrlOpenCommand', () => {
|
||||
describe('on macOS (darwin)', () => {
|
||||
beforeEach(() => {
|
||||
mockProcess.platform = 'darwin';
|
||||
});
|
||||
it('should return open', () => {
|
||||
expect(getUrlOpenCommand()).toBe('open');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on Windows (win32)', () => {
|
||||
beforeEach(() => {
|
||||
mockProcess.platform = 'win32';
|
||||
});
|
||||
it('should return start', () => {
|
||||
expect(getUrlOpenCommand()).toBe('start');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on Linux (linux)', () => {
|
||||
beforeEach(() => {
|
||||
mockProcess.platform = 'linux';
|
||||
});
|
||||
it('should return xdg-open', () => {
|
||||
expect(getUrlOpenCommand()).toBe('xdg-open');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on unmatched OS', () => {
|
||||
beforeEach(() => {
|
||||
mockProcess.platform = 'unmatched';
|
||||
});
|
||||
it('should return xdg-open', () => {
|
||||
expect(getUrlOpenCommand()).toBe('xdg-open');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ export const isAtCommand = (query: string): boolean =>
|
||||
*/
|
||||
export const isSlashCommand = (query: string): boolean => query.startsWith('/');
|
||||
|
||||
//Copies a string snippet to the clipboard for different platforms
|
||||
// Copies a string snippet to the clipboard for different platforms
|
||||
export const copyToClipboard = async (text: string): Promise<void> => {
|
||||
const run = (cmd: string, args: string[]) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
@@ -80,3 +80,27 @@ export const copyToClipboard = async (text: string): Promise<void> => {
|
||||
throw new Error(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const getUrlOpenCommand = (): string => {
|
||||
// --- Determine the OS-specific command to open URLs ---
|
||||
let openCmd: string;
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
openCmd = 'open';
|
||||
break;
|
||||
case 'win32':
|
||||
openCmd = 'start';
|
||||
break;
|
||||
case 'linux':
|
||||
openCmd = 'xdg-open';
|
||||
break;
|
||||
default:
|
||||
// Default to xdg-open, which appears to be supported for the less popular operating systems.
|
||||
openCmd = 'xdg-open';
|
||||
console.warn(
|
||||
`Unknown platform: ${process.platform}. Attempting to open URLs with: ${openCmd}.`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
return openCmd;
|
||||
};
|
||||
|
||||
9
packages/cli/src/ui/utils/isNarrowWidth.ts
Normal file
9
packages/cli/src/ui/utils/isNarrowWidth.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
export function isNarrowWidth(width: number): boolean {
|
||||
return width < 80;
|
||||
}
|
||||
Reference in New Issue
Block a user