feat(server): index PSC scripts and provide cross-file TclOO navigation/completions

Add PSC (.psc) indexing and share TclOO class metadata across files so class
definitions discovered via PSC layers can be used for completions, signature
help, inlay hints, and "go to definition". Key behavior changes:

- Client file watcher now includes *.psc and .vscode launch paths/tests updated
  to use the postprocessor test folder; .gitignore updated to ignore that folder.
- Server watches .psc changes and refreshes a PSC script index; new
  tools/tcloo_navigation.py exposes tcloo_definition used by the language server
  to resolve cross-file class/constructor/method definitions.
- Language server uses class_snapshot(document.path) when producing TclOO
  completions, signature help, and inlay hints so resolved class metadata is
  available across files.

Also includes related docs/changelog updates, minor code formatting cleanups,
and added tests for PSC/TclOO behavior.
This commit is contained in:
Christoph Brandau
2026-09-21 20:47:51 +02:00
parent f88a50d4ab
commit 757b885f28
21 changed files with 699 additions and 378 deletions
+35
View File
@@ -0,0 +1,35 @@
"""Definition targets for literal TclOO classes and resolved method calls."""
from tools.tcloo_completion import _analyze, name_location, parse_completion_source
from tools.tcloo_symbols import class_symbols
def tcloo_definition(source, uri, position, external_classes=None):
tree = parse_completion_source(source)
if tree is None:
return None
classes, _, calls = _analyze(tree, external_classes=external_classes, uri=uri, source=source)
def contains(location):
if location is None:
return False
start, end = location.range.start, location.range.end
return (start.line, start.character) <= (position.line, position.character) < (end.line, end.character)
targets = {}
declarations, references = class_symbols(tree, classes, targets)
for node in references:
if contains(name_location(node, uri, source)):
return classes[targets[node.pos]].definition
for name, node in declarations.items():
if contains(name_location(node, uri, source)):
return classes[name].definition
for call in calls:
if call.command.args and contains(name_location(call.command.args[0], uri, source)):
return call.definition
# F12 on a declaration itself should stay on that declaration.
for info in classes.values():
for location in [*info.method_definitions.values(), info.constructor_definition]:
if location is not None and location.uri == uri and contains(location):
return location
return None