@@ -0,0 +1,116 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Ensure server/src is on the path for imports
|
||||
THIS_DIR = Path(__file__).parent
|
||||
SRC_DIR = THIS_DIR.parent.parent / "src"
|
||||
if str(SRC_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SRC_DIR))
|
||||
|
||||
import lsprotocol.types as lsp # type: ignore
|
||||
from lsp_server import LSP_SERVER, goto_definition # type: ignore
|
||||
|
||||
|
||||
def _loc_to_tuple(loc: lsp.Location) -> tuple[str, int, int, int, int]:
|
||||
"""Helper to normalize Location into a tuple for easy asserts."""
|
||||
return (
|
||||
loc.uri,
|
||||
loc.range.start.line,
|
||||
loc.range.start.character,
|
||||
loc.range.end.line,
|
||||
loc.range.end.character,
|
||||
)
|
||||
|
||||
|
||||
def _extract_first_location(result) -> lsp.Location | None:
|
||||
if result is None:
|
||||
return None
|
||||
if isinstance(result, list):
|
||||
return result[0] if result else None
|
||||
return result
|
||||
|
||||
|
||||
def test_goto_definition_same_file(tmp_path: Path):
|
||||
source_lines = [
|
||||
"proc add {a b} {",
|
||||
" return [expr {$a + $b}]",
|
||||
"}",
|
||||
"",
|
||||
"set x [add 1 2]",
|
||||
]
|
||||
source = "\n".join(source_lines)
|
||||
|
||||
uri = Path(tmp_path / "same.tcl").as_uri()
|
||||
# Put a text document into the workspace
|
||||
LSP_SERVER.workspace.put_text_document(
|
||||
lsp.TextDocumentItem(uri=uri, language_id="tcl", version=1, text=source)
|
||||
)
|
||||
|
||||
# Position on the word 'add' in the last line
|
||||
line_idx = 4
|
||||
char_idx = source_lines[line_idx].index("add") + 1 # somewhere inside token
|
||||
|
||||
params = lsp.DefinitionParams(
|
||||
text_document=lsp.TextDocumentIdentifier(uri=uri),
|
||||
position=lsp.Position(line=line_idx, character=char_idx),
|
||||
)
|
||||
|
||||
result = goto_definition(params)
|
||||
loc = _extract_first_location(result)
|
||||
|
||||
assert loc is not None
|
||||
assert loc.uri == uri
|
||||
# Definition should be on line 0 at the token 'add'
|
||||
start = loc.range.start
|
||||
end = loc.range.end
|
||||
assert start.line == 0
|
||||
assert end.line == 0
|
||||
assert source_lines[0][start.character : end.character] == "add"
|
||||
|
||||
|
||||
def test_goto_definition_cross_file(tmp_path: Path):
|
||||
# File A declares the proc
|
||||
a_lines = [
|
||||
"proc myproc {arg} {",
|
||||
" return $arg",
|
||||
"}",
|
||||
]
|
||||
a_src = "\n".join(a_lines)
|
||||
a_path = tmp_path / "a.tcl"
|
||||
a_uri = a_path.as_uri()
|
||||
LSP_SERVER.workspace.put_text_document(
|
||||
lsp.TextDocumentItem(uri=a_uri, language_id="tcl", version=1, text=a_src)
|
||||
)
|
||||
|
||||
# Update indices for file A so proc_signatures gets populated
|
||||
doc_a = LSP_SERVER.workspace.get_text_document(a_uri)
|
||||
LSP_SERVER.update_poco_completion_for_file(doc_a)
|
||||
|
||||
# File B calls the proc
|
||||
b_lines = [
|
||||
"set y [myproc 42]",
|
||||
]
|
||||
b_src = "\n".join(b_lines)
|
||||
b_uri = (tmp_path / "b.tcl").as_uri()
|
||||
LSP_SERVER.workspace.put_text_document(
|
||||
lsp.TextDocumentItem(uri=b_uri, language_id="tcl", version=1, text=b_src)
|
||||
)
|
||||
|
||||
call_line = 0
|
||||
call_char = b_lines[0].index("myproc") + 2
|
||||
|
||||
params = lsp.DefinitionParams(
|
||||
text_document=lsp.TextDocumentIdentifier(uri=b_uri),
|
||||
position=lsp.Position(line=call_line, character=call_char),
|
||||
)
|
||||
|
||||
result = goto_definition(params)
|
||||
loc = _extract_first_location(result)
|
||||
|
||||
assert loc is not None
|
||||
assert loc.uri == a_uri
|
||||
start = loc.range.start
|
||||
end = loc.range.end
|
||||
assert start.line == 0
|
||||
assert a_lines[start.line][start.character : end.character] == "myproc"
|
||||
|
||||
Reference in New Issue
Block a user