fix(coding-agent): added gap separators between noncontiguous search matches
- SearchTool now tracked the last emitted line and inserted ellipsis markers when noncontiguous match blocks were output. - Display output gap markers were padded to align with code-frame gutters. - Added a regression test that verified a no-context search emits an ellipsis between separated matches in the same file.
This commit is contained in:
@@ -328,11 +328,18 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|||||||
return nextWidth;
|
return nextWidth;
|
||||||
}, 0);
|
}, 0);
|
||||||
const cacheEntries: Array<readonly [number, string]> = [];
|
const cacheEntries: Array<readonly [number, string]> = [];
|
||||||
|
let lastEmittedLine: number | undefined;
|
||||||
|
const gutterPad = " ".repeat(lineNumberWidth + 1);
|
||||||
for (const match of fileMatches) {
|
for (const match of fileMatches) {
|
||||||
const pushLine = (lineNumber: number, line: string, isMatch: boolean, recordable: boolean) => {
|
const pushLine = (lineNumber: number, line: string, isMatch: boolean, recordable: boolean) => {
|
||||||
|
if (lastEmittedLine !== undefined && lineNumber > lastEmittedLine + 1) {
|
||||||
|
modelOut.push("...");
|
||||||
|
displayOut.push(`${gutterPad}│...`);
|
||||||
|
}
|
||||||
modelOut.push(formatMatchLine(lineNumber, line, isMatch, { useHashLines }));
|
modelOut.push(formatMatchLine(lineNumber, line, isMatch, { useHashLines }));
|
||||||
displayOut.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
|
displayOut.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
|
||||||
if (recordable) cacheEntries.push([lineNumber, line] as const);
|
if (recordable) cacheEntries.push([lineNumber, line] as const);
|
||||||
|
lastEmittedLine = lineNumber;
|
||||||
};
|
};
|
||||||
if (match.contextBefore) {
|
if (match.contextBefore) {
|
||||||
for (const ctx of match.contextBefore) {
|
for (const ctx of match.contextBefore) {
|
||||||
|
|||||||
@@ -1419,6 +1419,24 @@ function b() {
|
|||||||
expect(output).toMatch(/\*5\|match two/);
|
expect(output).toMatch(/\*5\|match two/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("inserts a gap separator between non-contiguous match blocks", async () => {
|
||||||
|
const testFile = path.join(testDir, "gaps.txt");
|
||||||
|
const lines = Array.from({ length: 10 }, (_, idx) => (idx === 0 || idx === 5 ? "match" : `filler ${idx}`));
|
||||||
|
fs.writeFileSync(testFile, lines.join("\n"));
|
||||||
|
|
||||||
|
const noContextSettings = Settings.isolated({ "search.contextBefore": 0, "search.contextAfter": 0 });
|
||||||
|
const noContextSearchTool = wrapToolWithMetaNotice(
|
||||||
|
new SearchTool(createTestToolSession(testDir, noContextSettings)),
|
||||||
|
);
|
||||||
|
const result = await noContextSearchTool.execute("test-call-12-gap", {
|
||||||
|
pattern: "match",
|
||||||
|
paths: [testFile],
|
||||||
|
});
|
||||||
|
|
||||||
|
const output = getTextOutput(result);
|
||||||
|
expect(output).toMatch(/\*1\|match\n\.\.\.\n\*6\|match/);
|
||||||
|
});
|
||||||
|
|
||||||
it("should paginate files via the skip parameter", async () => {
|
it("should paginate files via the skip parameter", async () => {
|
||||||
const skipDir = path.join(testDir, "skip-dir");
|
const skipDir = path.join(testDir, "skip-dir");
|
||||||
fs.mkdirSync(skipDir, { recursive: true });
|
fs.mkdirSync(skipDir, { recursive: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user