Skip to content

Commit 25b4bae

Browse files
committed
clean
1 parent e2852eb commit 25b4bae

3 files changed

Lines changed: 99 additions & 29 deletions

File tree

packages/opencode/src/lsp/client.ts

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import { Filesystem } from "../util"
1616
const DIAGNOSTICS_DEBOUNCE_MS = 150
1717
const DIAGNOSTICS_POLL_MS = 500
1818
const DIAGNOSTICS_REQUEST_TIMEOUT_MS = 2_000
19-
const DIAGNOSTICS_WAIT_TIMEOUT_MS = 10_000
19+
const DIAGNOSTICS_WAIT_TIMEOUT_MS = 3_000
20+
const DIAGNOSTICS_PULL_WAIT_TIMEOUT_MS = 10_000
2021
const DIAGNOSTICS_SETTLE_MS = 1_500
2122

2223
const log = Log.create({ service: "lsp.client" })
@@ -62,6 +63,7 @@ type ServerCapabilities = {
6263
| {
6364
change?: number
6465
}
66+
diagnosticProvider?: unknown
6567
[key: string]: unknown
6668
}
6769

@@ -210,6 +212,7 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
210212
)
211213
})
212214
const syncKind = getSyncKind(initialized.capabilities)
215+
const hasStaticPullDiagnostics = Boolean(initialized.capabilities?.diagnosticProvider)
213216

214217
await connection.sendNotification("initialized", {})
215218

@@ -257,10 +260,14 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
257260
}
258261

259262
async function requestDiagnostics(filePath: string) {
263+
const registrations = [...diagnosticRegistrations.values()].filter(
264+
(registration) => registration.registerOptions?.workspaceDiagnostics !== true,
265+
)
266+
if (!hasStaticPullDiagnostics && registrations.length === 0) return false
267+
260268
const results = [await requestDiagnosticReport(filePath)]
261269
const identifiers = new Set(
262-
[...diagnosticRegistrations.values()]
263-
.filter((registration) => registration.registerOptions?.workspaceDiagnostics !== true)
270+
registrations
264271
.map((registration) => registration.registerOptions?.identifier)
265272
.filter((identifier): identifier is string => Boolean(identifier)),
266273
)
@@ -374,40 +381,47 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
374381
path.isAbsolute(request.path) ? request.path : path.resolve(input.directory, request.path),
375382
)
376383
log.info("waiting for diagnostics", { path: normalizedPath })
377-
let unsub: () => void
384+
let unsub: (() => void) | undefined
378385
let debounceTimer: ReturnType<typeof setTimeout> | undefined
379-
let pushed = false
380-
let firstHandledAt: number | undefined
386+
let done = false
387+
let resolvePushed: (() => void) | undefined
388+
const timeout = hasStaticPullDiagnostics || diagnosticRegistrations.size > 0
389+
? DIAGNOSTICS_PULL_WAIT_TIMEOUT_MS
390+
: DIAGNOSTICS_WAIT_TIMEOUT_MS
381391
return await withTimeout(
382-
(async () => {
383-
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
384-
if (event.properties.path !== normalizedPath || event.properties.serverID !== result.serverID) return
385-
if (debounceTimer) clearTimeout(debounceTimer)
386-
debounceTimer = setTimeout(() => {
387-
pushed = true
388-
}, DIAGNOSTICS_DEBOUNCE_MS)
389-
})
390-
await new Promise((resolve) => setTimeout(resolve, DIAGNOSTICS_DEBOUNCE_MS))
391-
while (true) {
392-
if (pushed) {
393-
log.info("got diagnostics", { path: normalizedPath })
394-
return
395-
}
396-
const handled = await requestDiagnostics(normalizedPath)
397-
if (handled) {
398-
firstHandledAt = firstHandledAt ?? Date.now()
399-
if (Date.now() - firstHandledAt >= DIAGNOSTICS_SETTLE_MS) {
392+
Promise.race([
393+
new Promise<void>((resolve) => {
394+
resolvePushed = resolve
395+
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
396+
if (event.properties.path !== normalizedPath || event.properties.serverID !== result.serverID) return
397+
if (debounceTimer) clearTimeout(debounceTimer)
398+
debounceTimer = setTimeout(() => {
400399
log.info("got diagnostics", { path: normalizedPath })
401-
return
400+
resolve()
401+
}, DIAGNOSTICS_DEBOUNCE_MS)
402+
})
403+
}),
404+
(async () => {
405+
let firstHandledAt: number | undefined
406+
while (!done) {
407+
const handled = await requestDiagnostics(normalizedPath)
408+
if (handled) {
409+
firstHandledAt = firstHandledAt ?? Date.now()
410+
if (Date.now() - firstHandledAt >= DIAGNOSTICS_SETTLE_MS) {
411+
log.info("got diagnostics", { path: normalizedPath })
412+
return
413+
}
402414
}
415+
await new Promise((resolve) => setTimeout(resolve, DIAGNOSTICS_POLL_MS))
403416
}
404-
await new Promise((resolve) => setTimeout(resolve, DIAGNOSTICS_POLL_MS))
405-
}
406-
})(),
407-
DIAGNOSTICS_WAIT_TIMEOUT_MS,
417+
})(),
418+
]),
419+
timeout,
408420
)
409421
.catch(() => {})
410422
.finally(() => {
423+
done = true
424+
resolvePushed?.()
411425
if (debounceTimer) clearTimeout(debounceTimer)
412426
unsub?.()
413427
})

packages/opencode/test/fixture/lsp/fake-lsp-server.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
let nextId = 1
55
let lastDidChange = null
6+
let diagnosticRequests = 0
67

78
function encode(message) {
89
const json = JSON.stringify(message)
@@ -78,10 +79,32 @@ function handle(raw) {
7879
lastDidChange = data.params
7980
return
8081
}
82+
if (data.method === "textDocument/diagnostic") {
83+
diagnosticRequests += 1
84+
send({ jsonrpc: "2.0", id: data.id, result: null })
85+
return
86+
}
8187
if (data.method === "test/get-last-change") {
8288
send({ jsonrpc: "2.0", id: data.id, result: lastDidChange })
8389
return
8490
}
91+
if (data.method === "test/get-diagnostic-request-count") {
92+
send({ jsonrpc: "2.0", id: data.id, result: diagnosticRequests })
93+
return
94+
}
95+
if (data.method === "test/publish-diagnostics") {
96+
const uri = data.params && data.params.uri
97+
if (!uri) return
98+
send({
99+
jsonrpc: "2.0",
100+
method: "textDocument/publishDiagnostics",
101+
params: {
102+
uri,
103+
diagnostics: (data.params && data.params.diagnostics) || [],
104+
},
105+
})
106+
return
107+
}
85108
if (data.method === "test/trigger") {
86109
const method = data.params && data.params.method
87110
if (method) sendRequest(method, {})

packages/opencode/test/lsp/client.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, test, beforeEach } from "bun:test"
22
import path from "path"
3+
import { pathToFileURL } from "url"
34
import { tmpdir } from "../fixture/fixture"
45
import { LSPClient } from "../../src/lsp"
56
import { LSPServer } from "../../src/lsp"
@@ -142,4 +143,36 @@ describe("LSPClient interop", () => {
142143
},
143144
})
144145
})
146+
147+
test("waitForDiagnostics avoids pull requests on push-only servers", async () => {
148+
const handle = spawnFakeServer() as any
149+
await using tmp = await tmpdir()
150+
const file = path.join(tmp.path, "client.ts")
151+
await Bun.write(file, "const x = 1\n")
152+
153+
await Instance.provide({
154+
directory: tmp.path,
155+
fn: async () => {
156+
const client = await LSPClient.create({
157+
serverID: "fake",
158+
server: handle as unknown as LSPServer.Handle,
159+
root: tmp.path,
160+
directory: tmp.path,
161+
})
162+
163+
const wait = client.waitForDiagnostics({ path: file })
164+
await client.notify.open({ path: file })
165+
await client.connection.sendNotification("test/publish-diagnostics", {
166+
uri: pathToFileURL(file).href,
167+
diagnostics: [],
168+
})
169+
await wait
170+
171+
const count = await client.connection.sendRequest<number>("test/get-diagnostic-request-count", {})
172+
expect(count).toBe(0)
173+
174+
await client.shutdown()
175+
},
176+
})
177+
})
145178
})

0 commit comments

Comments
 (0)