Skip to content

Commit b924b70

Browse files
committed
fix: stop overstating minimal LSP client support
The client was advertising publish-diagnostics version support and workspace diagnostic refresh support without implementing either behavior, and workspace/configuration always returned a single initialization blob instead of one result per requested item. Downgrade the unsupported capability claims and return configuration entries in the shape servers expect so they don't assume features we don't actually have.
1 parent 5f182c0 commit b924b70

3 files changed

Lines changed: 89 additions & 4 deletions

File tree

packages/opencode/src/lsp/client.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ function dedupeDiagnostics(items: Diagnostic[]) {
114114
})
115115
}
116116

117+
function configurationValue(settings: unknown, section?: string) {
118+
if (!section) return settings ?? null
119+
const result = section.split(".").reduce<unknown>((acc, key) => {
120+
if (!acc || typeof acc !== "object" || !(key in acc)) return undefined
121+
return (acc as Record<string, unknown>)[key]
122+
}, settings)
123+
return result ?? null
124+
}
125+
117126
export async function create(input: { serverID: string; server: LSPServer.Handle; root: string; directory: string }) {
118127
const l = log.clone().tag("serverID", input.serverID)
119128
l.info("starting client")
@@ -171,8 +180,9 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
171180
l.info("window/workDoneProgress/create", params)
172181
return null
173182
})
174-
connection.onRequest("workspace/configuration", async () => {
175-
return [input.server.initialization ?? {}]
183+
connection.onRequest("workspace/configuration", async (params) => {
184+
const items = (params as { items?: { section?: string }[] }).items ?? []
185+
return items.map((item) => configurationValue(input.server.initialization, item.section))
176186
})
177187
connection.onRequest("client/registerCapability", async (params) => {
178188
const registrations = (params as { registrations?: CapabilityRegistration[] }).registrations ?? []
@@ -227,7 +237,7 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
227237
dynamicRegistration: true,
228238
},
229239
diagnostics: {
230-
refreshSupport: true,
240+
refreshSupport: false,
231241
},
232242
},
233243
textDocument: {
@@ -240,7 +250,7 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
240250
relatedDocumentSupport: true,
241251
},
242252
publishDiagnostics: {
243-
versionSupport: true,
253+
versionSupport: false,
244254
},
245255
},
246256
},

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
let nextId = 1
44
let readBuffer = Buffer.alloc(0)
55
let lastChange = null
6+
let initializeParams = null
67
let diagnosticRequestCount = 0
78
let registeredCapability = false
9+
const pendingClientRequests = new Map()
810
let pullConfig = {
911
delayMs: 0,
1012
registerOn: undefined,
@@ -101,7 +103,16 @@ function handle(raw) {
101103
return
102104
}
103105

106+
if (typeof data.method === "undefined" && typeof data.id !== "undefined") {
107+
const pending = pendingClientRequests.get(data.id)
108+
if (!pending) return
109+
pendingClientRequests.delete(data.id)
110+
sendResponse(pending, data.result ?? null)
111+
return
112+
}
113+
104114
if (data.method === "initialize") {
115+
initializeParams = data.params
105116
sendResponse(data.id, {
106117
capabilities: {
107118
textDocumentSync: {
@@ -112,6 +123,17 @@ function handle(raw) {
112123
return
113124
}
114125

126+
if (data.method === "test/get-initialize-params") {
127+
sendResponse(data.id, initializeParams)
128+
return
129+
}
130+
131+
if (data.method === "test/request-configuration") {
132+
const id = sendRequest("workspace/configuration", data.params)
133+
pendingClientRequests.set(id, data.id)
134+
return
135+
}
136+
115137
if (data.method === "initialized" || data.method === "workspace/didChangeConfiguration") {
116138
return
117139
}

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,59 @@ describe("LSPClient interop", () => {
9191
await client.shutdown()
9292
})
9393

94+
test("initialize does not overclaim unsupported diagnostics capabilities", async () => {
95+
const handle = spawnFakeServer() as any
96+
97+
const client = await Instance.provide({
98+
directory: process.cwd(),
99+
fn: () =>
100+
LSPClient.create({
101+
serverID: "fake",
102+
server: handle as unknown as LSPServer.Handle,
103+
root: process.cwd(),
104+
directory: process.cwd(),
105+
}),
106+
})
107+
108+
const params = await client.connection.sendRequest<any>("test/get-initialize-params", {})
109+
expect(params.capabilities.workspace.diagnostics.refreshSupport).toBe(false)
110+
expect(params.capabilities.textDocument.publishDiagnostics.versionSupport).toBe(false)
111+
112+
await client.shutdown()
113+
})
114+
115+
test("workspace/configuration returns one result per requested item", async () => {
116+
const handle = spawnFakeServer() as any
117+
const initialization = {
118+
alpha: {
119+
beta: 1,
120+
},
121+
gamma: true,
122+
}
123+
124+
const client = await Instance.provide({
125+
directory: process.cwd(),
126+
fn: () =>
127+
LSPClient.create({
128+
serverID: "fake",
129+
server: {
130+
...(handle as unknown as LSPServer.Handle),
131+
initialization,
132+
},
133+
root: process.cwd(),
134+
directory: process.cwd(),
135+
}),
136+
})
137+
138+
const response = await client.connection.sendRequest<any[]>("test/request-configuration", {
139+
items: [{ section: "alpha" }, { section: "alpha.beta" }, { section: "missing" }, {}],
140+
})
141+
142+
expect(response).toEqual([{ beta: 1 }, 1, null, initialization])
143+
144+
await client.shutdown()
145+
})
146+
94147
test("sends ranged didChange for incremental sync servers", async () => {
95148
const handle = spawnFakeServer() as any
96149
await using tmp = await tmpdir()

0 commit comments

Comments
 (0)