mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Fix(grep): memory overflow in grep search and enhance test coverage (#5911)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
@@ -1562,6 +1562,46 @@ describe('loadCliConfig chatCompression', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig useRipgrep', () => {
|
||||
const originalArgv = process.argv;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = originalArgv;
|
||||
vi.unstubAllEnvs();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should be false by default when useRipgrep is not set in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = {};
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getUseRipgrep()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be true when useRipgrep is set to true in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = { useRipgrep: true };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getUseRipgrep()).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false when useRipgrep is explicitly set to false in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = { useRipgrep: false };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getUseRipgrep()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig tool exclusions', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalIsTTY = process.stdin.isTTY;
|
||||
|
||||
@@ -552,6 +552,7 @@ export async function loadCliConfig(
|
||||
folderTrust,
|
||||
interactive,
|
||||
trustedFolder,
|
||||
useRipgrep: settings.useRipgrep,
|
||||
shouldUseNodePtyShell: settings.shouldUseNodePtyShell,
|
||||
skipNextSpeakerCheck: settings.skipNextSpeakerCheck,
|
||||
enablePromptCompletion: settings.enablePromptCompletion ?? false,
|
||||
|
||||
@@ -52,6 +52,7 @@ describe('SettingsSchema', () => {
|
||||
'model',
|
||||
'hasSeenIdeIntegrationNudge',
|
||||
'folderTrustFeature',
|
||||
'useRipgrep',
|
||||
];
|
||||
|
||||
expectedSettings.forEach((setting) => {
|
||||
|
||||
@@ -534,6 +534,16 @@ export const SETTINGS_SCHEMA = {
|
||||
description: 'Skip the next speaker check.',
|
||||
showInDialog: true,
|
||||
},
|
||||
useRipgrep: {
|
||||
type: 'boolean',
|
||||
label: 'Use Ripgrep',
|
||||
category: 'Tools',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.',
|
||||
showInDialog: true,
|
||||
},
|
||||
enablePromptCompletion: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Prompt Completion',
|
||||
|
||||
@@ -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 { EOL } from 'os';
|
||||
|
||||
describe('<OverflowProvider><DiffRenderer /></OverflowProvider>', () => {
|
||||
const mockColorizeCode = vi.spyOn(CodeColorizer, 'colorizeCode');
|
||||
@@ -29,7 +30,7 @@ index 0000000..e69de29
|
||||
+++ b/test.py
|
||||
@@ -0,0 +1 @@
|
||||
+print("hello world")
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -57,7 +58,7 @@ index 0000000..e69de29
|
||||
+++ b/test.unknown
|
||||
@@ -0,0 +1 @@
|
||||
+some content
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -85,7 +86,7 @@ index 0000000..e69de29
|
||||
+++ b/test.txt
|
||||
@@ -0,0 +1 @@
|
||||
+some text content
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer diffContent={newFileDiffContent} terminalWidth={80} />
|
||||
@@ -109,7 +110,7 @@ index 0000001..0000002 100644
|
||||
@@ -1 +1 @@
|
||||
-old line
|
||||
+new line
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -139,7 +140,7 @@ index 0000001..0000002 100644
|
||||
index 1234567..1234567 100644
|
||||
--- a/file.txt
|
||||
+++ b/file.txt
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -176,7 +177,7 @@ index 123..456 100644
|
||||
@@ -10,2 +10,2 @@
|
||||
context line 10
|
||||
context line 11
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -213,7 +214,7 @@ index abc..def 100644
|
||||
context line 13
|
||||
context line 14
|
||||
context line 15
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -247,7 +248,7 @@ index 123..789 100644
|
||||
-const anotherOld = 'test';
|
||||
+const anotherNew = 'test';
|
||||
console.log('end of second hunk');
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
|
||||
it.each([
|
||||
{
|
||||
@@ -317,7 +318,7 @@ fileDiff Index: file.txt
|
||||
-const anotherOld = 'test';
|
||||
+const anotherNew = 'test';
|
||||
\\ No newline at end of file
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
@@ -347,7 +348,7 @@ fileDiff Index: Dockerfile
|
||||
+RUN npm install
|
||||
+RUN npm run build
|
||||
\\ No newline at end of file
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<OverflowProvider>
|
||||
<DiffRenderer
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { EOL } from 'os';
|
||||
import { Colors } from '../../colors.js';
|
||||
import crypto from 'crypto';
|
||||
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
||||
@@ -20,7 +21,7 @@ interface DiffLine {
|
||||
}
|
||||
|
||||
function parseDiffWithLineNumbers(diffContent: string): DiffLine[] {
|
||||
const lines = diffContent.split('\n');
|
||||
const lines = diffContent.split(EOL);
|
||||
const result: DiffLine[] = [];
|
||||
let currentOldLine = 0;
|
||||
let currentNewLine = 0;
|
||||
|
||||
@@ -9,6 +9,7 @@ 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';
|
||||
import { EOL } from 'os';
|
||||
|
||||
describe('<MarkdownDisplay />', () => {
|
||||
const baseProps = {
|
||||
@@ -54,7 +55,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
## Header 2
|
||||
### Header 3
|
||||
#### Header 4
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -64,7 +65,10 @@ describe('<MarkdownDisplay />', () => {
|
||||
});
|
||||
|
||||
it('renders a fenced code block with a language', () => {
|
||||
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```';
|
||||
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```'.replace(
|
||||
/\n/g,
|
||||
EOL,
|
||||
);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -74,7 +78,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
});
|
||||
|
||||
it('renders a fenced code block without a language', () => {
|
||||
const text = '```\nplain text\n```';
|
||||
const text = '```\nplain text\n```'.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -84,7 +88,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
});
|
||||
|
||||
it('handles unclosed (pending) code blocks', () => {
|
||||
const text = '```typescript\nlet y = 2;';
|
||||
const text = '```typescript\nlet y = 2;'.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} isPending={true} />
|
||||
@@ -98,7 +102,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
- item A
|
||||
* item B
|
||||
+ item C
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -112,7 +116,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
* Level 1
|
||||
* Level 2
|
||||
* Level 3
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -125,7 +129,7 @@ describe('<MarkdownDisplay />', () => {
|
||||
const text = `
|
||||
1. First item
|
||||
2. Second item
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -141,7 +145,7 @@ Hello
|
||||
World
|
||||
***
|
||||
Test
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -156,7 +160,7 @@ Test
|
||||
|----------|:--------:|
|
||||
| Cell 1 | Cell 2 |
|
||||
| Cell 3 | Cell 4 |
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -170,7 +174,7 @@ Test
|
||||
Some text before.
|
||||
| A | B |
|
||||
|---|
|
||||
| 1 | 2 |`;
|
||||
| 1 | 2 |`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -182,7 +186,7 @@ Some text before.
|
||||
it('inserts a single space between paragraphs', () => {
|
||||
const text = `Paragraph 1.
|
||||
|
||||
Paragraph 2.`;
|
||||
Paragraph 2.`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -205,7 +209,7 @@ some code
|
||||
\`\`\`
|
||||
|
||||
Another paragraph.
|
||||
`;
|
||||
`.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
@@ -215,7 +219,7 @@ Another paragraph.
|
||||
});
|
||||
|
||||
it('hides line numbers in code blocks when showLineNumbers is false', () => {
|
||||
const text = '```javascript\nconst x = 1;\n```';
|
||||
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
|
||||
const settings = new LoadedSettings(
|
||||
{ path: '', settings: {} },
|
||||
{ path: '', settings: { showLineNumbers: false } },
|
||||
@@ -234,7 +238,7 @@ Another paragraph.
|
||||
});
|
||||
|
||||
it('shows line numbers in code blocks by default', () => {
|
||||
const text = '```javascript\nconst x = 1;\n```';
|
||||
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
|
||||
const { lastFrame } = render(
|
||||
<SettingsContext.Provider value={mockSettings}>
|
||||
<MarkdownDisplay {...baseProps} text={text} />
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Text, Box } from 'ink';
|
||||
import { EOL } from 'os';
|
||||
import { Colors } from '../colors.js';
|
||||
import { colorizeCode } from './CodeColorizer.js';
|
||||
import { TableRenderer } from './TableRenderer.js';
|
||||
@@ -34,7 +35,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
}) => {
|
||||
if (!text) return <></>;
|
||||
|
||||
const lines = text.split('\n');
|
||||
const lines = text.split(EOL);
|
||||
const headerRegex = /^ *(#{1,4}) +(.*)/;
|
||||
const codeFenceRegex = /^ *(`{3,}|~{3,}) *(\w*?) *$/;
|
||||
const ulItemRegex = /^([ \t]*)([-*+]) +(.*)/;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
/* ACP defines a schema for a simple (experimental) JSON-RPC protocol that allows GUI applications to interact with agents. */
|
||||
|
||||
import { z } from 'zod';
|
||||
import { EOL } from 'os';
|
||||
import * as schema from './schema.js';
|
||||
export * from './schema.js';
|
||||
|
||||
@@ -172,7 +173,7 @@ class Connection {
|
||||
const decoder = new TextDecoder();
|
||||
for await (const chunk of output) {
|
||||
content += decoder.decode(chunk, { stream: true });
|
||||
const lines = content.split('\n');
|
||||
const lines = content.split(EOL);
|
||||
content = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
|
||||
Reference in New Issue
Block a user