feat: support pull diagnostics in the LSP client (C#, Kotlin, etc) (#23771)

This commit is contained in:
Luke Parker
2026-04-23 09:24:11 +10:00
committed by Aiden Cline
parent 58db41b4b9
commit e383df4b17
12 changed files with 1123 additions and 121 deletions

View File

@@ -34,7 +34,7 @@ export interface Interface {
},
) => Effect.Effect<void, EffectFlock.LockError | InstallFailedError>
readonly outdated: (pkg: string, cachedVersion: string) => Effect.Effect<boolean>
readonly which: (pkg: string) => Effect.Effect<Option.Option<string>>
readonly which: (pkg: string, bin?: string) => Effect.Effect<Option.Option<string>>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/Npm") {}
@@ -207,7 +207,7 @@ export const layer = Layer.effect(
return
}, Effect.scoped)
const which = Effect.fn("Npm.which")(function* (pkg: string) {
const which = Effect.fn("Npm.which")(function* (pkg: string, bin?: string) {
const dir = directory(pkg)
const binDir = path.join(dir, "node_modules", ".bin")
@@ -215,6 +215,9 @@ export const layer = Layer.effect(
const files = yield* fs.readDirectory(binDir).pipe(Effect.catch(() => Effect.succeed([] as string[])))
if (files.length === 0) return Option.none<string>()
// Caller picked a specific bin (e.g. pyright exposes both `pyright` and
// `pyright-langserver`); trust the hint if the package provides it.
if (bin) return files.includes(bin) ? Option.some(bin) : Option.none<string>()
if (files.length === 1) return Option.some(files[0])
const pkgJson = yield* afs.readJson(path.join(dir, "node_modules", pkg, "package.json")).pipe(Effect.option)
@@ -223,11 +226,11 @@ export const layer = Layer.effect(
const parsed = pkgJson.value as { bin?: string | Record<string, string> }
if (parsed?.bin) {
const unscoped = pkg.startsWith("@") ? pkg.split("/")[1] : pkg
const bin = parsed.bin
if (typeof bin === "string") return Option.some(unscoped)
const keys = Object.keys(bin)
const parsedBin = parsed.bin
if (typeof parsedBin === "string") return Option.some(unscoped)
const keys = Object.keys(parsedBin)
if (keys.length === 1) return Option.some(keys[0])
return bin[unscoped] ? Option.some(unscoped) : Option.some(keys[0])
return parsedBin[unscoped] ? Option.some(unscoped) : Option.some(keys[0])
}
}