usse/scrape/venv/lib/python3.10/site-packages/esbonio/lsp/symbols.py
2023-12-22 15:26:01 +01:00

132 lines
3.7 KiB
Python

from typing import Optional
from docutils import nodes
from docutils.nodes import NodeVisitor
from lsprotocol.types import DocumentSymbol
from lsprotocol.types import Position
from lsprotocol.types import Range
from lsprotocol.types import SymbolKind
class SymbolVisitor(NodeVisitor):
"""A visitor used to build the hierarchy we return from a
``textDocument/documentSymbol`` request.
"""
def __init__(self, rst, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger = rst.logger
self.symbols = []
self.symbol_stack = []
@property
def current_symbol(self) -> Optional[DocumentSymbol]:
if len(self.symbol_stack) == 0:
return None
return self.symbol_stack[-1]
def push_symbol(self):
symbol = DocumentSymbol(
name="",
kind=SymbolKind.String,
range=Range(
start=Position(line=1, character=0),
end=Position(line=1, character=10),
),
selection_range=Range(
start=Position(line=1, character=0),
end=Position(line=1, character=10),
),
children=[],
)
current_symbol = self.current_symbol
if not current_symbol:
self.symbols.append(symbol)
else:
if current_symbol.children is None:
current_symbol.children = [symbol]
else:
current_symbol.children.append(symbol)
self.symbol_stack.append(symbol)
return symbol
def pop_symbol(self):
self.symbol_stack.pop()
def visit_section(self, node: nodes.Node) -> None:
self.push_symbol()
def depart_section(self, node: nodes.Node) -> None:
self.pop_symbol()
def visit_title(self, node: nodes.Node) -> None:
symbol = self.current_symbol
has_parent = True
if symbol is None:
has_parent = False
symbol = self.push_symbol()
name = node.astext()
line = (node.line or 1) - 1
symbol.name = name
symbol.range.start.line = line
symbol.range.end.line = line
symbol.range.end.character = len(name) - 1
symbol.selection_range.start.line = line
symbol.selection_range.end.line = line
symbol.selection_range.end.character = len(name) - 1
if not has_parent:
self.pop_symbol()
def depart_title(self, node: nodes.Node) -> None:
pass
def visit_a_directive(self, node: nodes.Element):
symbol = self.push_symbol()
name = node["text"] # type: ignore
line = (node.line or 1) - 1
symbol.name = name
symbol.kind = SymbolKind.Class
symbol.range.start.line = line
symbol.range.end.line = line
symbol.range.end.character = len(name) - 1
symbol.selection_range.start.line = line
symbol.selection_range.end.line = line
symbol.selection_range.end.character = len(name) - 1
def depart_a_directive(self, node: nodes.Node):
self.pop_symbol()
# TODO: Enable symbols for roles
# However the reported line numbers can be inaccurate...
def visit_a_role(self, node: nodes.Node) -> None:
...
def depart_a_role(self, node: nodes.Node) -> None:
...
# TODO: Enable symbols for definition list items
# However the reported line numbers appear to be inaccurate...
def visit_Text(self, node: nodes.Node) -> None:
pass
def depart_Text(self, node: nodes.Node) -> None:
pass
def unknown_visit(self, node: nodes.Node) -> None:
pass
def unknown_departure(self, node: nodes.Node) -> None:
pass