67 lines
1.9 KiB
Python
67 lines
1.9 KiB
Python
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
|