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.
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
import xml.etree.ElementTree as ET
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import List, Optional
|
|
from pathlib import Path
|
|
|
|
|
|
@dataclass
|
|
class SourcedFile:
|
|
layer_name: str
|
|
subfolder: Optional[str]
|
|
files: List[str]
|
|
|
|
|
|
def read_psc_file(psc_file: Path) -> List[SourcedFile]:
|
|
tree = ET.parse(psc_file)
|
|
root = tree.getroot()
|
|
# PSC exports may use a default XML namespace.
|
|
for element in root.iter():
|
|
element.tag = element.tag.rsplit("}", 1)[-1]
|
|
|
|
layers = root.findall(".//Layer")
|
|
|
|
layer_info_list: List[SourcedFile] = []
|
|
for layer in layers:
|
|
layer_name = layer.attrib.get("Name")
|
|
subfolder = layer.attrib.get("SubFolder")
|
|
# Scripts-Filenames
|
|
scripts = layer.find("Scripts")
|
|
script_names = []
|
|
if scripts is not None:
|
|
for filename in scripts.findall("Filename"):
|
|
name = filename.attrib.get("Name")
|
|
if name:
|
|
script_names.append(name)
|
|
|
|
layer_info_list.append(
|
|
SourcedFile(layer_name=layer_name, subfolder=subfolder, files=script_names)
|
|
)
|
|
return layer_info_list
|
|
|
|
|
|
def get_all_psc_files(root_path: Path) -> list[Path]:
|
|
return sorted(root_path.rglob("*.psc"), key=lambda path: str(path).casefold())
|
|
|
|
|
|
def psc_script_files(psc_file: Path) -> list[Path]:
|
|
"""Resolve layer script paths relative to the PSC, preserving load order."""
|
|
def expanded(value):
|
|
return Path(os.path.expandvars(value).replace("\\", "/"))
|
|
|
|
paths = []
|
|
for layer in read_psc_file(psc_file):
|
|
folder = layer.subfolder or "."
|
|
base = psc_file.parent / expanded(os.environ.get(folder, folder))
|
|
for name in layer.files:
|
|
filename = expanded(name)
|
|
if not filename.suffix:
|
|
filename = filename.with_suffix(".tcl")
|
|
path = (base / filename).resolve()
|
|
if path.suffix.lower() == ".tcl":
|
|
paths.append(path)
|
|
return paths
|