From f61a4943bdad0874a662f6fbfb8389cc140fd38f Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:29:01 +0800 Subject: [PATCH] fix(lsp): preserve POSIX backslash paths --- packages/lsp/tool-lsp/src/render.ts | 5 +++-- packages/lsp/tool-lsp/tests/render.spec.ts | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/lsp/tool-lsp/src/render.ts b/packages/lsp/tool-lsp/src/render.ts index 1e73e7ec6a..2f376707db 100644 --- a/packages/lsp/tool-lsp/src/render.ts +++ b/packages/lsp/tool-lsp/src/render.ts @@ -149,6 +149,7 @@ export function renderUri(uri: string, workspaceUri: string): string { if (targetSegments === undefined || workspaceSegments === undefined) return uri const sameAuthority = target.hostname === workspace.hostname const windowsWorld = isWindowsFileWorld(workspace, workspaceSegments) + if (windowsWorld && [...targetSegments, ...workspaceSegments].some(segment => segment.includes('\\'))) return uri const inside = sameAuthority && targetSegments.length >= workspaceSegments.length && workspaceSegments.every((segment, index) => samePathSegment(segment, targetSegments[index] as string, windowsWorld)) @@ -164,11 +165,11 @@ function isWindowsFileWorld(url: URL, segments: readonly string[]): boolean { return url.hostname.length > 0 || /^[A-Za-z]:$/.test(segments[0] ?? '') } -/** Decode URI path segments while rejecting encoded separators that would change path structure. */ +/** Decode URI path segments while rejecting encoded POSIX separators and NUL. */ function decodeFileSegments(url: URL): string[] | undefined { try { const decoded = url.pathname.split('/').map(segment => decodeURIComponent(segment)) - if (decoded.some(segment => /[/\\\0]/u.test(segment))) return undefined + if (decoded.some(segment => /[/\0]/u.test(segment))) return undefined while (decoded.at(-1) === '') decoded.pop() decoded.shift() return decoded diff --git a/packages/lsp/tool-lsp/tests/render.spec.ts b/packages/lsp/tool-lsp/tests/render.spec.ts index 953f7ef4c1..f09297d3d4 100644 --- a/packages/lsp/tool-lsp/tests/render.spec.ts +++ b/packages/lsp/tool-lsp/tests/render.spec.ts @@ -82,11 +82,15 @@ describe('renderUri', () => { expect(renderUri('file:///a.ts', 'file:///')).toBe('a.ts') }) + it('preserves backslashes as ordinary POSIX filename characters', () => { + expect(renderUri('file:///home/u/proj/dir%5Cname/a.ts', WS_URI)).toBe('dir\\name/a.ts') + }) + it('keeps malformed or mismatched URI coordinates verbatim', () => { expect(renderUri('file://[', WS_URI)).toBe('file://[') expect(renderUri('file:///a.ts', 'https://example.com/workspace')).toBe('file:///a.ts') expect(renderUri('file:///a.ts', 'file:///bad%ZZ')).toBe('file:///a.ts') - expect(renderUri('file:///bad%5Cpath', WS_URI)).toBe('file:///bad%5Cpath') + expect(renderUri('file:///C:/workspace/bad%5Cpath', 'file:///C:/workspace')).toBe('file:///C:/workspace/bad%5Cpath') expect(renderUri('file:///short', 'file:///short/deeper')).toBe('/short') expect(renderUri('file:///', 'file:///C:/workspace')).toBe('/') })