Просмотр исходного кода

fix(win32): Normalise LSP paths on windows (fixes lua) (#5597)

Luke Parker 2 месяцев назад
Родитель
Сommit
27e826eba6

+ 7 - 2
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)
     })
 

+ 13 - 9
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()
               }

+ 8 - 10
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 = {

+ 2 - 1
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
       }

+ 14 - 0
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)