summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLuke Parker <[email protected]>2025-12-16 10:01:03 +1000
committerGitHub <[email protected]>2025-12-15 18:01:03 -0600
commit27e826eba63a3d8d1111f13dc4934d3f54d90cf8 (patch)
tree833aa802413997f90faceb755f2f9c49c0e29f8b
parent89a4f1c1ae38c8fa8999fe082641e504b179684a (diff)
downloadopencode-27e826eba63a3d8d1111f13dc4934d3f54d90cf8.tar.gz
opencode-27e826eba63a3d8d1111f13dc4934d3f54d90cf8.zip
fix(win32): Normalise LSP paths on windows (fixes lua) (#5597)
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/index.tsx9
-rw-r--r--packages/opencode/src/lsp/client.ts22
-rw-r--r--packages/opencode/src/tool/edit.ts18
-rw-r--r--packages/opencode/src/tool/write.ts3
-rw-r--r--packages/opencode/src/util/filesystem.ts14
5 files changed, 44 insertions, 22 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
index 48f7db054..b9ef2580b 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -64,6 +64,7 @@ import { Editor } from "../../util/editor"
import stripAnsi from "strip-ansi"
import { Footer } from "./footer.tsx"
import { usePromptRef } from "../../context/prompt"
+import { Filesystem } from "@/util/filesystem"
addDefaultParsers(parsers.parsers)
@@ -1414,7 +1415,10 @@ ToolRegistry.register<typeof WriteTool>({
return props.input.content
})
- const diagnostics = createMemo(() => props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? [])
+ const diagnostics = createMemo(() => {
+ const filePath = Filesystem.normalizePath(props.input.filePath ?? "")
+ return props.metadata.diagnostics?.[filePath] ?? []
+ })
return (
<>
@@ -1587,7 +1591,8 @@ ToolRegistry.register<typeof EditTool>({
const diffContent = createMemo(() => props.metadata.diff ?? props.permission["diff"])
const diagnostics = createMemo(() => {
- const arr = props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? []
+ const filePath = Filesystem.normalizePath(props.input.filePath ?? "")
+ const arr = props.metadata.diagnostics?.[filePath] ?? []
return arr.filter((x) => x.severity === 1).slice(0, 3)
})
diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts
index ce426cf62..f06c2c938 100644
--- a/packages/opencode/src/lsp/client.ts
+++ b/packages/opencode/src/lsp/client.ts
@@ -11,6 +11,7 @@ import type { LSPServer } from "./server"
import { NamedError } from "@opencode-ai/util/error"
import { withTimeout } from "../util/timeout"
import { Instance } from "../project/instance"
+import { Filesystem } from "../util/filesystem"
export namespace LSPClient {
const log = Log.create({ service: "lsp.client" })
@@ -47,14 +48,15 @@ export namespace LSPClient {
const diagnostics = new Map<string, Diagnostic[]>()
connection.onNotification("textDocument/publishDiagnostics", (params) => {
- const path = fileURLToPath(params.uri)
+ const filePath = Filesystem.normalizePath(fileURLToPath(params.uri))
l.info("textDocument/publishDiagnostics", {
- path,
+ path: filePath,
+ count: params.diagnostics.length,
})
- const exists = diagnostics.has(path)
- diagnostics.set(path, params.diagnostics)
+ const exists = diagnostics.has(filePath)
+ diagnostics.set(filePath, params.diagnostics)
if (!exists && input.serverID === "typescript") return
- Bus.publish(Event.Diagnostics, { path, serverID: input.serverID })
+ Bus.publish(Event.Diagnostics, { path: filePath, serverID: input.serverID })
})
connection.onRequest("window/workDoneProgress/create", (params) => {
l.info("window/workDoneProgress/create", params)
@@ -181,14 +183,16 @@ export namespace LSPClient {
return diagnostics
},
async waitForDiagnostics(input: { path: string }) {
- input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path)
- log.info("waiting for diagnostics", input)
+ const normalizedPath = Filesystem.normalizePath(
+ path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path),
+ )
+ log.info("waiting for diagnostics", { path: normalizedPath })
let unsub: () => void
return await withTimeout(
new Promise<void>((resolve) => {
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
- if (event.properties.path === input.path && event.properties.serverID === result.serverID) {
- log.info("got diagnostics", input)
+ if (event.properties.path === normalizedPath && event.properties.serverID === result.serverID) {
+ log.info("got diagnostics", { path: normalizedPath })
unsub?.()
resolve()
}
diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts
index fdf115ac4..b49bd7abe 100644
--- a/packages/opencode/src/tool/edit.ts
+++ b/packages/opencode/src/tool/edit.ts
@@ -140,16 +140,14 @@ export const EditTool = Tool.define("edit", {
let output = ""
await LSP.touchFile(filePath, true)
const diagnostics = await LSP.diagnostics()
- for (const [file, issues] of Object.entries(diagnostics)) {
- if (issues.length === 0) continue
- if (file === filePath) {
- const errors = issues.filter((item) => item.severity === 1)
- const limited = errors.slice(0, MAX_DIAGNOSTICS_PER_FILE)
- const suffix =
- errors.length > MAX_DIAGNOSTICS_PER_FILE ? `\n... and ${errors.length - MAX_DIAGNOSTICS_PER_FILE} more` : ""
- output += `\nThis file has errors, please fix\n<file_diagnostics>\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</file_diagnostics>\n`
- continue
- }
+ const normalizedFilePath = Filesystem.normalizePath(filePath)
+ const issues = diagnostics[normalizedFilePath] ?? []
+ if (issues.length > 0) {
+ const errors = issues.filter((item) => item.severity === 1)
+ const limited = errors.slice(0, MAX_DIAGNOSTICS_PER_FILE)
+ const suffix =
+ errors.length > MAX_DIAGNOSTICS_PER_FILE ? `\n... and ${errors.length - MAX_DIAGNOSTICS_PER_FILE} more` : ""
+ output += `\nThis file has errors, please fix\n<file_diagnostics>\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</file_diagnostics>\n`
}
const filediff: Snapshot.FileDiff = {
diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts
index 03f2ba891..6b8fd3dd1 100644
--- a/packages/opencode/src/tool/write.ts
+++ b/packages/opencode/src/tool/write.ts
@@ -80,6 +80,7 @@ export const WriteTool = Tool.define("write", {
let output = ""
await LSP.touchFile(filepath, true)
const diagnostics = await LSP.diagnostics()
+ const normalizedFilepath = Filesystem.normalizePath(filepath)
let projectDiagnosticsCount = 0
for (const [file, issues] of Object.entries(diagnostics)) {
if (issues.length === 0) continue
@@ -87,7 +88,7 @@ export const WriteTool = Tool.define("write", {
const limited = sorted.slice(0, MAX_DIAGNOSTICS_PER_FILE)
const suffix =
issues.length > MAX_DIAGNOSTICS_PER_FILE ? `\n... and ${issues.length - MAX_DIAGNOSTICS_PER_FILE} more` : ""
- if (file === filepath) {
+ if (file === normalizedFilepath) {
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${limited.map(LSP.Diagnostic.pretty).join("\n")}${suffix}\n</file_diagnostics>\n`
continue
}
diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts
index a3dcfc703..98fbe533d 100644
--- a/packages/opencode/src/util/filesystem.ts
+++ b/packages/opencode/src/util/filesystem.ts
@@ -1,7 +1,21 @@
+import { realpathSync } from "fs"
import { exists } from "fs/promises"
import { dirname, join, relative } from "path"
export namespace Filesystem {
+ /**
+ * On Windows, normalize a path to its canonical casing using the filesystem.
+ * This is needed because Windows paths are case-insensitive but LSP servers
+ * may return paths with different casing than what we send them.
+ */
+ export function normalizePath(p: string): string {
+ if (process.platform !== "win32") return p
+ try {
+ return realpathSync.native(p)
+ } catch {
+ return p
+ }
+ }
export function overlaps(a: string, b: string) {
const relA = relative(a, b)
const relB = relative(b, a)