fix(lsp): 解码百分号编码盘符后去掉前导分隔符

VS Code 发送 file:///d%3A/...,DecodeUri 只在字面冒号时去前导 /,解出 /d:/... 后 FindProject 失败,项目未采纳、索引 roots 落到工作区根,跨模块成员全部失去高亮与跳转。改为解码完成后判断盘符;gate10 增加百分号编码 rootUri 的回归断言。
This commit is contained in:
TermiNexus committed 2026-10-27 17:19:00 +08:00
1 parent e761a85ca7
commit 4770f57567
2 files changed
+48 -2

No files matched your search

+4 -2
View File
@@ -136,8 +136,6 @@ def DecodeUri(pool: memhub.MemManager*, uri: str) -> str:
p: str = uri
if string.strncmp(uri, "file://", 7) == 0:
p = uri + 7
if p[0] == '/' and len(p) >= 3 and p[2] == ':':
p = p + 1
n: sizet = string.strlen(p)
out: bytes = pool.alloc(n + 1)
if out is None:
@@ -158,6 +156,10 @@ def DecodeUri(pool: memhub.MemManager*, uri: str) -> str:
o += 1
i += 1
out[o] = 0
# VS Code sends the drive colon percent-encoded (file:///d%3A/...), so the leading
# separator must be dropped after decoding, not before.
if string.strncmp(uri, "file://", 7) == 0 and o >= 3 and out[0] == '/' and out[2] == ':':
return str(out + 1)
return str(out)
+44
View File
@@ -1765,6 +1765,50 @@ def gate10(exe: str) -> int:
code = client.close()
if code != 0:
raise AssertionError(f"exit code is {code}, expected 0")
# VS Code's URI serializer percent-encodes the drive colon (file:///d%3A/...). A decoded
# path starting with "/" prevents project adoption and leaves the index roots empty.
def vscode_uri(path: Path) -> str:
p = path.resolve().as_posix()
if len(p) >= 2 and p[1] == ":":
p = p[0].lower() + "%3A" + p[2:]
return "file:///" + p
enc_client = Client(exe)
enc_init = enc_client.request(
"initialize",
{"processId": os.getpid(), "rootUri": vscode_uri(workspace), "capabilities": {}},
)
if not isinstance(enc_init.get("result"), dict):
raise AssertionError(f"encoded-URI initialize failed: {enc_init}")
enc_client.notify("initialized", {})
enc_client.notify(
"textDocument/didOpen",
{"textDocument": {"uri": vscode_uri(use_path), "languageId": "viper",
"version": 1, "text": text}},
)
enc_client.wait_notification("textDocument/publishDiagnostics")
enc_stats = enc_client.request("viper/lspStats").get("result") or {}
enc_source = str(enc_stats.get("sourceDir") or "")
if enc_source.startswith("/"):
raise AssertionError(f"encoded URI root was not decoded: {enc_stats}")
if Path(enc_source).resolve() != workspace.resolve():
raise AssertionError(f"encoded URI sourceDir mismatch: {enc_stats}")
enc_sem = enc_client.request(
"textDocument/semanticTokens/full", {"textDocument": {"uri": vscode_uri(use_path)}},
timeout=120.0,
).get("result")
if not isinstance(enc_sem, dict):
raise AssertionError(f"encoded URI semanticTokens failed: {enc_sem}")
enc_tokens = _decode_tokens(enc_sem.get("data", []))
enc_pos = _position(text, "s.value")
enc_pos["character"] += 2
if not _find_token(enc_tokens, enc_pos["line"], enc_pos["character"], sem_types.index("property")):
raise AssertionError(f"encoded URI lost cross-file member token: {enc_pos}")
enc_client.request("shutdown")
if enc_client.close() != 0:
raise AssertionError("encoded URI client did not exit cleanly")
shutil.rmtree(workspace, ignore_errors=True)
print("member-annotations: OK")
print("member-module-chain: OK")