mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-13 23:51:16 -07:00
feat(core): Include 50 lines of context in grep_search when matches <= 3
This commit is contained in:
@@ -493,10 +493,12 @@ describe('GrepTool', () => {
|
||||
// sub/fileC.txt has 1 world, so total matches = 2.
|
||||
expect(result.llmContent).toContain('Found 2 matches');
|
||||
expect(result.llmContent).toContain('File: fileA.txt');
|
||||
expect(result.llmContent).toContain('L1: hello world');
|
||||
expect(result.llmContent).not.toContain('L2: second line with world');
|
||||
// Should be a match
|
||||
expect(result.llmContent).toContain('> L1: hello world');
|
||||
// Should NOT be a match (but might be in context)
|
||||
expect(result.llmContent).not.toContain('> L2: second line with world');
|
||||
expect(result.llmContent).toContain('File: sub/fileC.txt');
|
||||
expect(result.llmContent).toContain('L1: another world in sub dir');
|
||||
expect(result.llmContent).toContain('> L1: another world in sub dir');
|
||||
});
|
||||
|
||||
it('should return only file paths when names_only is true', async () => {
|
||||
@@ -530,8 +532,36 @@ describe('GrepTool', () => {
|
||||
|
||||
expect(result.llmContent).toContain('Found 1 match');
|
||||
expect(result.llmContent).toContain('copyright.txt');
|
||||
expect(result.llmContent).toContain('Copyright 2025 Google LLC');
|
||||
expect(result.llmContent).not.toContain('Copyright 2026 Google LLC');
|
||||
// Should be a match
|
||||
expect(result.llmContent).toContain('> L1: Copyright 2025 Google LLC');
|
||||
// Should NOT be a match (but might be in context)
|
||||
expect(result.llmContent).not.toContain(
|
||||
'> L2: Copyright 2026 Google LLC',
|
||||
);
|
||||
});
|
||||
|
||||
it('should include context when matches are <= 3', async () => {
|
||||
const lines = Array.from({ length: 100 }, (_, i) => `Line ${i + 1}`);
|
||||
lines[50] = 'Target match';
|
||||
await fs.writeFile(
|
||||
path.join(tempRootDir, 'context.txt'),
|
||||
lines.join('\n'),
|
||||
);
|
||||
|
||||
const params: GrepToolParams = { pattern: 'Target match' };
|
||||
const invocation = grepTool.build(params);
|
||||
const result = await invocation.execute(abortSignal);
|
||||
|
||||
expect(result.llmContent).toContain(
|
||||
'Found 1 match for pattern "Target match"',
|
||||
);
|
||||
expect(result.llmContent).toContain('Match at line 51:');
|
||||
// Verify context before
|
||||
expect(result.llmContent).toContain(' L40: Line 40');
|
||||
// Verify match line
|
||||
expect(result.llmContent).toContain('> L51: Target match');
|
||||
// Verify context after
|
||||
expect(result.llmContent).toContain(' L60: Line 60');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export interface GrepToolParams {
|
||||
*/
|
||||
interface GrepMatch {
|
||||
filePath: string;
|
||||
absolutePath: string;
|
||||
lineNumber: number;
|
||||
line: string;
|
||||
}
|
||||
@@ -130,6 +131,7 @@ class GrepToolInvocation extends BaseToolInvocation<
|
||||
|
||||
return {
|
||||
filePath: relativeFilePath || path.basename(absoluteFilePath),
|
||||
absolutePath: absoluteFilePath,
|
||||
lineNumber,
|
||||
line: lineContent,
|
||||
};
|
||||
@@ -309,14 +311,61 @@ class GrepToolInvocation extends BaseToolInvocation<
|
||||
|
||||
llmContent += `:\n---\n`;
|
||||
|
||||
for (const filePath in matchesByFile) {
|
||||
llmContent += `File: ${filePath}
|
||||
if (matchCount <= 3 && matchCount > 0 && !this.params.names_only) {
|
||||
for (const filePath in matchesByFile) {
|
||||
const fileMatches = matchesByFile[filePath];
|
||||
let fileLines: string[] | null = null;
|
||||
try {
|
||||
const content = await fsPromises.readFile(
|
||||
fileMatches[0].absolutePath,
|
||||
'utf8',
|
||||
);
|
||||
fileLines = content.split(/\r?\n/);
|
||||
} catch (err) {
|
||||
debugLogger.warn(
|
||||
`Failed to read file for context: ${fileMatches[0].absolutePath}`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
llmContent += `File: ${filePath}\n`;
|
||||
|
||||
for (const match of fileMatches) {
|
||||
if (fileLines) {
|
||||
const startLine = Math.max(0, match.lineNumber - 1 - 50);
|
||||
const endLine = Math.min(
|
||||
fileLines.length,
|
||||
match.lineNumber - 1 + 50 + 1,
|
||||
);
|
||||
const contextLines = fileLines.slice(startLine, endLine);
|
||||
|
||||
llmContent += `Match at line ${match.lineNumber}:\n`;
|
||||
contextLines.forEach((line, index) => {
|
||||
const currentLineNumber = startLine + index + 1;
|
||||
if (currentLineNumber === match.lineNumber) {
|
||||
llmContent += `> L${currentLineNumber}: ${line}\n`;
|
||||
} else {
|
||||
llmContent += ` L${currentLineNumber}: ${line}\n`;
|
||||
}
|
||||
});
|
||||
llmContent += '\n';
|
||||
} else {
|
||||
const trimmedLine = match.line.trim();
|
||||
llmContent += `L${match.lineNumber}: ${trimmedLine}\n`;
|
||||
}
|
||||
}
|
||||
llmContent += '---\n';
|
||||
}
|
||||
} else {
|
||||
for (const filePath in matchesByFile) {
|
||||
llmContent += `File: ${filePath}
|
||||
`;
|
||||
matchesByFile[filePath].forEach((match) => {
|
||||
const trimmedLine = match.line.trim();
|
||||
llmContent += `L${match.lineNumber}: ${trimmedLine}\n`;
|
||||
});
|
||||
llmContent += '---\n';
|
||||
matchesByFile[filePath].forEach((match) => {
|
||||
const trimmedLine = match.line.trim();
|
||||
llmContent += `L${match.lineNumber}: ${trimmedLine}\n`;
|
||||
});
|
||||
llmContent += '---\n';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -569,6 +618,7 @@ class GrepToolInvocation extends BaseToolInvocation<
|
||||
filePath:
|
||||
path.relative(absolutePath, fileAbsolutePath) ||
|
||||
path.basename(fileAbsolutePath),
|
||||
absolutePath: fileAbsolutePath,
|
||||
lineNumber: index + 1,
|
||||
line,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user