feat(lsp): 增加 UTF-16 位置往返验证

- viper/lspPosition:line/character -> offset -> line/character
- probe Gate 1:随机 10k 个有效位置往返,覆盖 CRLF/中文/emoji
This commit is contained in:
TermiNexus committed 2026-10-26 14:27:33 +08:00
1 parent c42ae889d1
commit 93cd0f1ea5
2 files changed
+50

No files matched your search

+27
View File
@@ -198,6 +198,31 @@ def _HandleDocument(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
return LspRpc.WriteResponse(pool, tr, id_node, result)
def _HandlePosition(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
docs: LspDocuments.LspDocuments*, id_node: json.JsonValue*,
params: json.JsonValue*) -> int:
uri: str = None
if params is not None:
uri = LspRpc.GetStringField(params, "uri")
doc: LspDocuments.LspDocument* = docs.Find(uri)
if doc is None or doc.index is None:
return LspRpc.WriteResponse(pool, tr, id_node, json.null(pool))
line: int = LspRpc.GetIntField(params, "line", 0)
character: int = LspRpc.GetIntField(params, "character", 0)
offset: sizet = doc.index.PositionToOffset(line, character)
out_line: int = 0
out_char: int = 0
if doc.index.OffsetToPosition(offset, &out_line, &out_char) != 0:
return -1
result: json.JsonValue* = json.object(pool)
if result is None:
return -1
json.object_set(pool, result, "offset", json.int_val(pool, int64(offset)))
json.object_set(pool, result, "line", json.int_val(pool, int64(out_line)))
json.object_set(pool, result, "character", json.int_val(pool, int64(out_char)))
return LspRpc.WriteResponse(pool, tr, id_node, result)
def Run(pool: memhub.MemBuddy*, perm_pool: memhub.MemBuddy*) -> int:
os._mbuddy = perm_pool
os._win32._mbuddy = perm_pool
@@ -262,6 +287,8 @@ def Run(pool: memhub.MemBuddy*, perm_pool: memhub.MemBuddy*) -> int:
rc = _HandleStats(pool, tr, docs, ws, id_node)
elif string.strcmp(method, "viper/lspDocument") == 0:
rc = _HandleDocument(pool, tr, docs, id_node, params)
elif string.strcmp(method, "viper/lspPosition") == 0:
rc = _HandlePosition(pool, tr, docs, id_node, params)
elif string.strcmp(method, LspProtocol.LSP_METHOD_SHUTDOWN) == 0:
shutdown_requested = 1
rc = LspRpc.WriteResponse(pool, tr, id_node, json.null(pool))
+23
View File
@@ -7,6 +7,7 @@ import argparse
import json
import os
import queue
import random
import subprocess
import sys
import threading
@@ -181,6 +182,28 @@ def documents(exe: str) -> int:
if not isinstance(doc, dict) or doc.get("text") != text or doc.get("version") != 1:
raise AssertionError(f"initial document text/version mismatch: {doc}")
lines = text.splitlines()
choices: list[tuple[int, int]] = []
for line_no, line_text in enumerate(lines):
offsets = [0]
units = 0
for ch in line_text:
units += 2 if ord(ch) > 0xFFFF else 1
offsets.append(units)
for character in offsets:
choices.append((line_no, character))
random.seed(1234)
for _ in range(10000):
line_no, character = random.choice(choices)
result = client.request(
"viper/lspPosition",
{"uri": uri, "line": line_no, "character": character},
).get("result")
if not isinstance(result, dict) or result.get("line") != line_no or result.get("character") != character:
raise AssertionError(
f"UTF-16 round-trip failed for ({line_no},{character}): {result}"
)
client.notify(
"textDocument/didChange",
{