feat(tcloo): add document-local TclOO completions, signature help and hints
Add conservative, document-local TclOO type inference and tooling so the server can offer method completions, signature help, and inlay parameter hints for statically resolvable TclOO receivers (including `new`/`create`, `my`, and simple return-chains). Also surface class names as completion items and emit semantic tokens for class declarations/references. Notable changes: - New tcloo_* tools: completion, symbols, and argument parsing; integrated into on_completion, signature_help, inlay hint generation and semantic token highlighting. Completions are returned early when an OO receiver context is detected. - Use a completion-friendly parser fallback when the main AST fails (TclSyntaxError) so editing-in-progress code still yields useful completions. - Add CompletionItemKind.Class to command kinds, exclude class items from the poco completion name cache, and include new unit tests for the TclOO helpers.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Parameter presentation for statically resolved TclOO calls."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import lsprotocol.types as lsp
|
||||
from tclint.lexer import TclSyntaxError
|
||||
from tclint.syntax_tree import BracedWord, Command
|
||||
|
||||
from tools.parser import CustomParser
|
||||
from tools.signature_help import _active_argument, _contains_cursor
|
||||
from tools.tcloo_completion import resolved_method_calls
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MethodParameter:
|
||||
name: str
|
||||
label: str
|
||||
variadic: bool = False
|
||||
|
||||
|
||||
def method_parameters(parameters: str) -> list[MethodParameter]:
|
||||
parser = CustomParser()
|
||||
try:
|
||||
words = parser.parse_list(BracedWord(parameters, pos=(1, 1))).children
|
||||
result = []
|
||||
for index, word in enumerate(words):
|
||||
parts = parser.parse_list(word).children
|
||||
if not parts or len(parts) > 2:
|
||||
return []
|
||||
name = parts[0].contents
|
||||
if name is None:
|
||||
return []
|
||||
variadic = name == "args" and len(parts) == 1 and index == len(words) - 1
|
||||
label = "{" + word.contents + "}" if len(parts) == 2 else name
|
||||
result.append(MethodParameter(name, label, variadic))
|
||||
return result
|
||||
except TclSyntaxError:
|
||||
return []
|
||||
|
||||
|
||||
def method_signature_help(source: str, position: lsp.Position) -> lsp.SignatureHelp | None:
|
||||
lines = source.split("\n")
|
||||
if position.line >= len(lines):
|
||||
return None
|
||||
# AST columns are codepoints; LSP columns are UTF-16 code units.
|
||||
prefix = lines[position.line].encode("utf-16-le")[:position.character * 2].decode("utf-16-le", errors="ignore")
|
||||
cursor = (position.line, len(prefix))
|
||||
candidates = [call for call in resolved_method_calls(source)
|
||||
if _contains_cursor(call.command, lines, cursor)]
|
||||
if not candidates:
|
||||
return None
|
||||
call = max(candidates, key=lambda candidate: candidate.command.pos)
|
||||
|
||||
def nested_active(node):
|
||||
return any(
|
||||
isinstance(child, Command) and _contains_cursor(child, lines, cursor)
|
||||
or nested_active(child)
|
||||
for child in node.children
|
||||
)
|
||||
|
||||
# Let the inner command's own signature provider handle its arguments.
|
||||
if nested_active(call.command):
|
||||
return None
|
||||
argument = _active_argument(call.command, cursor) - call.argument_offset
|
||||
if argument < 0:
|
||||
return None
|
||||
parameters = method_parameters(call.parameters)
|
||||
label = call.label
|
||||
infos = []
|
||||
for parameter in parameters:
|
||||
label += " "
|
||||
start = len(label.encode("utf-16-le")) // 2
|
||||
label += parameter.label
|
||||
infos.append(lsp.ParameterInformation(label=(start, len(label.encode("utf-16-le")) // 2)))
|
||||
active = min(argument, len(parameters) - 1) if parameters else None
|
||||
return lsp.SignatureHelp(
|
||||
signatures=[lsp.SignatureInformation(label=label, parameters=infos, active_parameter=active)],
|
||||
active_signature=0, active_parameter=active,
|
||||
)
|
||||
Reference in New Issue
Block a user