Skip to content

Commit 41fa167

Browse files
committed
tool: trim persisted LSP diagnostics
1 parent 66bc046 commit 41fa167

File tree

3 files changed

+119
-14
lines changed

3 files changed

+119
-14
lines changed

packages/opencode/src/tool/edit.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ export const EditTool = Tool.define("edit", {
131131

132132
let output = ""
133133
await LSP.touchFile(filePath, true)
134-
const diagnostics = await LSP.diagnostics()
134+
const allDiagnostics = await LSP.diagnostics()
135135
const normalizedFilePath = Filesystem.normalizePath(filePath)
136-
const issues = diagnostics[normalizedFilePath] ?? []
136+
const issues = allDiagnostics[normalizedFilePath] ?? []
137137
const errors = issues.filter((item) => item.severity === 1)
138138
if (errors.length > 0) {
139139
const limited = errors.slice(0, MAX_DIAGNOSTICS_PER_FILE)
@@ -144,7 +144,9 @@ export const EditTool = Tool.define("edit", {
144144

145145
return {
146146
metadata: {
147-
diagnostics,
147+
diagnostics: {
148+
[normalizedFilePath]: issues,
149+
},
148150
diff,
149151
filediff,
150152
},

packages/opencode/src/tool/write.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,28 +53,36 @@ export const WriteTool = Tool.define("write", {
5353

5454
let output = ""
5555
await LSP.touchFile(filepath, true)
56-
const diagnostics = await LSP.diagnostics()
56+
const allDiagnostics = await LSP.diagnostics()
5757
const normalizedFilepath = Filesystem.normalizePath(filepath)
58-
let projectDiagnosticsCount = 0
59-
for (const [file, issues] of Object.entries(diagnostics)) {
60-
const errors = issues.filter((item) => item.severity === 1)
58+
const issues = allDiagnostics[normalizedFilepath] ?? []
59+
const errors = issues.filter((item) => item.severity === 1)
60+
if (errors.length > 0) {
61+
const limited = errors.slice(0, MAX_DIAGNOSTICS_PER_FILE)
62+
const suffix =
63+
errors.length > MAX_DIAGNOSTICS_PER_FILE ? `\n... and ${errors.length - MAX_DIAGNOSTICS_PER_FILE} more` : ""
64+
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</file_diagnostics>\n`
65+
}
66+
67+
const project: string[] = []
68+
for (const [file, diagnostics] of Object.entries(allDiagnostics)) {
69+
if (file === normalizedFilepath) continue
70+
if (project.length >= MAX_PROJECT_DIAGNOSTICS_FILES) break
71+
const errors = diagnostics.filter((item) => item.severity === 1)
6172
if (errors.length === 0) continue
73+
project.push(file)
6274
const limited = errors.slice(0, MAX_DIAGNOSTICS_PER_FILE)
6375
const suffix =
6476
errors.length > MAX_DIAGNOSTICS_PER_FILE ? `\n... and ${errors.length - MAX_DIAGNOSTICS_PER_FILE} more` : ""
65-
if (file === normalizedFilepath) {
66-
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</file_diagnostics>\n`
67-
continue
68-
}
69-
if (projectDiagnosticsCount >= MAX_PROJECT_DIAGNOSTICS_FILES) continue
70-
projectDiagnosticsCount++
7177
output += `\n<project_diagnostics>\n${file}\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</project_diagnostics>\n`
7278
}
7379

7480
return {
7581
title: path.relative(Instance.worktree, filepath),
7682
metadata: {
77-
diagnostics,
83+
diagnostics: {
84+
[normalizedFilepath]: issues,
85+
},
7886
filepath,
7987
exists: exists,
8088
},
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { describe, expect, mock, test } from "bun:test"
2+
import path from "path"
3+
import { Instance } from "../../src/project/instance"
4+
import { Filesystem } from "../../src/util/filesystem"
5+
import { FileTime } from "../../src/file/time"
6+
import { tmpdir } from "../fixture/fixture"
7+
8+
const state = {
9+
diagnostics: {} as Record<string, Array<{ severity: number }>>,
10+
}
11+
12+
mock.module("../../src/lsp", () => ({
13+
LSP: {
14+
touchFile: () => Promise.resolve(),
15+
diagnostics: () => Promise.resolve(state.diagnostics),
16+
Diagnostic: {
17+
pretty: () => "",
18+
},
19+
},
20+
}))
21+
22+
const { EditTool } = await import("../../src/tool/edit")
23+
const { WriteTool } = await import("../../src/tool/write")
24+
25+
const ctx = {
26+
sessionID: "test",
27+
messageID: "",
28+
callID: "",
29+
agent: "build",
30+
abort: AbortSignal.any([]),
31+
metadata: () => {},
32+
ask: async () => {},
33+
}
34+
35+
describe("tool diagnostics metadata", () => {
36+
test("write stores only touched file diagnostics", async () => {
37+
await using tmp = await tmpdir({ git: true })
38+
await Instance.provide({
39+
directory: tmp.path,
40+
fn: async () => {
41+
const filePath = path.join(tmp.path, "a.lua")
42+
const normalizedFilepath = Filesystem.normalizePath(filePath)
43+
const otherPath = Filesystem.normalizePath(path.join(tmp.path, "b.lua"))
44+
state.diagnostics = {
45+
[normalizedFilepath]: [{ severity: 1 }, { severity: 1 }],
46+
[otherPath]: Array.from({ length: 250 }, () => ({ severity: 1 })),
47+
}
48+
49+
const tool = await WriteTool.init()
50+
const result = await tool.execute(
51+
{
52+
filePath,
53+
content: "print('hi')",
54+
},
55+
ctx,
56+
)
57+
58+
expect(Object.keys(result.metadata.diagnostics)).toEqual([normalizedFilepath])
59+
expect(result.metadata.diagnostics[normalizedFilepath]?.length).toBe(2)
60+
},
61+
})
62+
})
63+
64+
test("edit stores only touched file diagnostics", async () => {
65+
await using tmp = await tmpdir({ git: true })
66+
await Instance.provide({
67+
directory: tmp.path,
68+
fn: async () => {
69+
const filePath = path.join(tmp.path, "a.lua")
70+
await Bun.write(filePath, "hello\n")
71+
FileTime.read(ctx.sessionID, filePath)
72+
73+
const normalizedFilepath = Filesystem.normalizePath(filePath)
74+
const otherPath = Filesystem.normalizePath(path.join(tmp.path, "b.lua"))
75+
state.diagnostics = {
76+
[normalizedFilepath]: [{ severity: 1 }],
77+
[otherPath]: Array.from({ length: 250 }, () => ({ severity: 1 })),
78+
}
79+
80+
const tool = await EditTool.init()
81+
const result = await tool.execute(
82+
{
83+
filePath,
84+
oldString: "hello",
85+
newString: "world",
86+
},
87+
ctx,
88+
)
89+
90+
expect(Object.keys(result.metadata.diagnostics)).toEqual([normalizedFilepath])
91+
expect(result.metadata.diagnostics[normalizedFilepath]?.length).toBe(1)
92+
},
93+
})
94+
})
95+
})

0 commit comments

Comments
 (0)