from dataclasses import dataclass, field from enum import Enum, auto from lsprotocol.types import DocumentSymbol, Range, SymbolKind URI = str class NodeKind(Enum): LET = "let" PROCEDURE = "procedure" PROC = "proc" FOREACH = "foreach" @dataclass class Node: node: str kind: NodeKind location: Range children: list["Node"] = field(default_factory=list) symbols: dict[str, DocumentSymbol] = field(default_factory=dict) @property def all_symbols(self) -> list[DocumentSymbol]: return [ *self.symbols.values(), *(sym for child in self.children for sym in child.all_symbols), ] def should_contain(self, other: "Node") -> bool: """range based overlap check""" start_after = (other.location.start.line > self.location.start.line) or ( (other.location.start.line == self.location.start.line) and (other.location.start.character > self.location.start.character) ) ends_before = (other.location.end.line < self.location.end.line) or ( (other.location.end.line == self.location.end.line) and (other.location.end.character < self.location.start.character) ) return start_after and ends_before def add_child(self, new_child: "Node") -> None: for existing_child in self.children: if existing_child.should_contain(new_child): existing_child.add_child(new_child) break else: self.children.append(new_child) def as_doc_symbol(self) -> DocumentSymbol: return DocumentSymbol( name=self.node, kind=SymbolKind.Namespace, range=self.location, selection_range=self.location, children=list(self.symbols.values()) + [child.as_doc_symbol() for child in self.children], ) @dataclass class DocumentSymbols: uri: str tree: Node