Introduce several changes to reduce full-document reparses, lock contention and redundant work when handling TclOO analysis and navigation: - Add a cheap may_contain_classes pre-check and several cursor/receiver heuristics so completions, signature help and tcloo definitions skip the expensive marker reparse when the document cannot contain useful OO info. - Allow passing an existing parsed tree into tcloo completion/signature/definition helpers; update callers to use the server's cached tree when available. - Use a thread-local parser for request-time parse_source to avoid blocking the shared parser during background indexing, and add navigation_state() which returns cached definition identities (invalidated on index generation changes). - Add a cheap name pre-filter (_may_resolve_to) for symbol matching and only run class highlighting when classes may exist. These changes reduce contention and repeated parsing, improve responsiveness for requests during background indexing, and cache navigation definition identities. Tests were added/updated to assert caching and non-blocking behavior.
39 lines
1.7 KiB
Python
39 lines
1.7 KiB
Python
"""Definition targets for literal TclOO classes and resolved method calls."""
|
|
|
|
from tools.tcloo_completion import _analyze, may_contain_classes, name_location, parse_completion_source
|
|
from tools.tcloo_symbols import class_symbols
|
|
|
|
|
|
def tcloo_definition(source, uri, position, external_classes=None, tree=None):
|
|
if not may_contain_classes(source, external_classes):
|
|
return None
|
|
if tree is 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
|