lsp: Add document outline

This commit is contained in:
James Westman 2023-07-23 21:11:00 -05:00
parent 950b141d26
commit e087aeb44f
24 changed files with 469 additions and 28 deletions

View file

@ -35,6 +35,16 @@ class AdwBreakpointCondition(AstNode):
def condition(self) -> str:
return self.tokens["condition"]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"condition",
SymbolKind.Property,
self.range,
self.group.tokens["kw"].range,
self.condition,
)
@docs("kw")
def keyword_docs(self):
klass = self.root.gir.get_type("Breakpoint", "Adw")
@ -93,6 +103,16 @@ class AdwBreakpointSetter(AstNode):
else:
return None
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
f"{self.object_id}.{self.property_name}",
SymbolKind.Property,
self.range,
self.group.tokens["object"].range,
self.value.range.text,
)
@context(ValueTypeCtx)
def value_type(self) -> ValueTypeCtx:
if self.gir_property is not None:
@ -147,12 +167,25 @@ class AdwBreakpointSetter(AstNode):
class AdwBreakpointSetters(AstNode):
grammar = ["setters", Match("{").expected(), Until(AdwBreakpointSetter, "}")]
grammar = [
Keyword("setters"),
Match("{").expected(),
Until(AdwBreakpointSetter, "}"),
]
@property
def setters(self) -> T.List[AdwBreakpointSetter]:
return self.children[AdwBreakpointSetter]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"setters",
SymbolKind.Struct,
self.range,
self.group.tokens["setters"].range,
)
@validate()
def container_is_breakpoint(self):
validate_parent_type(self, "Adw", "Breakpoint", "breakpoint setters")

View file

@ -84,6 +84,16 @@ class ExtAdwMessageDialogResponse(AstNode):
def value(self) -> StringValue:
return self.children[0]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.id,
SymbolKind.Field,
self.range,
self.group.tokens["id"].range,
self.value.range.text,
)
@context(ValueTypeCtx)
def value_type(self) -> ValueTypeCtx:
return ValueTypeCtx(StringType())
@ -108,6 +118,15 @@ class ExtAdwMessageDialog(AstNode):
def responses(self) -> T.List[ExtAdwMessageDialogResponse]:
return self.children
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"responses",
SymbolKind.Array,
self.range,
self.group.tokens["responses"].range,
)
@validate("responses")
def container_is_message_dialog(self):
validate_parent_type(self, "Adw", "MessageDialog", "responses")

View file

@ -46,7 +46,14 @@ from ..gir import (
IntType,
StringType,
)
from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType
from ..lsp_utils import (
Completion,
CompletionItemKind,
DocumentSymbol,
SemanticToken,
SemanticTokenType,
SymbolKind,
)
from ..parse_tree import *
OBJECT_CONTENT_HOOKS = AnyOf()

View file

@ -21,6 +21,9 @@
import typing as T
from functools import cached_property
from blueprintcompiler.errors import T
from blueprintcompiler.lsp_utils import DocumentSymbol
from .common import *
from .response_id import ExtResponse
from .types import ClassName, ConcreteClassName
@ -59,8 +62,20 @@ class Object(AstNode):
def signature(self) -> str:
if self.id:
return f"{self.class_name.gir_type.full_name} {self.id}"
elif t := self.class_name.gir_type:
return f"{t.full_name}"
else:
return f"{self.class_name.gir_type.full_name}"
return f"{self.class_name.as_string}"
@property
def document_symbol(self) -> T.Optional[DocumentSymbol]:
return DocumentSymbol(
self.class_name.as_string,
SymbolKind.Object,
self.range,
self.children[ClassName][0].range,
self.id,
)
@property
def gir_class(self) -> GirType:

View file

@ -47,6 +47,21 @@ class Property(AstNode):
else:
return None
@property
def document_symbol(self) -> DocumentSymbol:
if isinstance(self.value, ObjectValue):
detail = None
else:
detail = self.value.range.text
return DocumentSymbol(
self.name,
SymbolKind.Property,
self.range,
self.group.tokens["name"].range,
detail,
)
@validate()
def binding_valid(self):
if (

View file

@ -51,12 +51,14 @@ class Signal(AstNode):
]
),
"=>",
Mark("detail_start"),
Optional(["$", UseLiteral("extern", True)]),
UseIdent("handler").expected("the name of a function to handle the signal"),
Match("(").expected("argument list"),
Optional(UseIdent("object")).expected("object identifier"),
Match(")").expected(),
ZeroOrMore(SignalFlag),
Mark("detail_end"),
)
@property
@ -105,6 +107,16 @@ class Signal(AstNode):
def gir_class(self):
return self.parent.parent.gir_class
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.full_name,
SymbolKind.Event,
self.range,
self.group.tokens["name"].range,
self.ranges["detail_start", "detail_end"].text,
)
@validate("handler")
def old_extern(self):
if not self.tokens["extern"]:

View file

@ -139,6 +139,16 @@ class A11yProperty(BaseAttribute):
def value_type(self) -> ValueTypeCtx:
return ValueTypeCtx(get_types(self.root.gir).get(self.tokens["name"]))
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.name,
SymbolKind.Field,
self.range,
self.group.tokens["name"].range,
self.value.range.text,
)
@validate("name")
def is_valid_property(self):
types = get_types(self.root.gir)
@ -172,6 +182,15 @@ class ExtAccessibility(AstNode):
def properties(self) -> T.List[A11yProperty]:
return self.children[A11yProperty]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"accessibility",
SymbolKind.Struct,
self.range,
self.group.tokens["accessibility"].range,
)
@validate("accessibility")
def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "accessibility properties")

View file

@ -31,13 +31,23 @@ class Item(AstNode):
]
@property
def name(self) -> str:
def name(self) -> T.Optional[str]:
return self.tokens["name"]
@property
def value(self) -> StringValue:
return self.children[StringValue][0]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.value.range.text,
SymbolKind.String,
self.range,
self.value.range,
self.name,
)
@validate("name")
def unique_in_parent(self):
if self.name is not None:
@ -54,6 +64,15 @@ class ExtComboBoxItems(AstNode):
"]",
]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"items",
SymbolKind.Array,
self.range,
self.group.tokens["items"].range,
)
@validate("items")
def container_is_combo_box_text(self):
validate_parent_type(self, "Gtk", "ComboBoxText", "combo box items")

View file

@ -23,6 +23,15 @@ from .gobject_object import ObjectContent, validate_parent_type
class Filters(AstNode):
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.tokens["tag_name"],
SymbolKind.Array,
self.range,
self.group.tokens[self.tokens["tag_name"]].range,
)
@validate()
def container_is_file_filter(self):
validate_parent_type(self, "Gtk", "FileFilter", "file filter properties")
@ -46,6 +55,15 @@ class FilterString(AstNode):
def item(self) -> str:
return self.tokens["name"]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.item,
SymbolKind.String,
self.range,
self.group.tokens["name"].range,
)
@validate()
def unique_in_parent(self):
self.validate_unique_in_parent(

View file

@ -36,6 +36,16 @@ class LayoutProperty(AstNode):
def value(self) -> Value:
return self.children[Value][0]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.name,
SymbolKind.Field,
self.range,
self.group.tokens["name"].range,
self.value.range.text,
)
@context(ValueTypeCtx)
def value_type(self) -> ValueTypeCtx:
# there isn't really a way to validate these
@ -56,6 +66,15 @@ class ExtLayout(AstNode):
Until(LayoutProperty, "}"),
)
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"layout",
SymbolKind.Struct,
self.range,
self.group.tokens["layout"].range,
)
@validate("layout")
def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "layout properties")

View file

@ -1,5 +1,9 @@
import typing as T
from blueprintcompiler.errors import T
from blueprintcompiler.lsp_utils import DocumentSymbol
from ..ast_utils import AstNode, validate
from ..parse_tree import Keyword
from .common import *
from .contexts import ScopeCtx
from .gobject_object import ObjectContent, validate_parent_type
@ -17,6 +21,15 @@ class ExtListItemFactory(AstNode):
def signature(self) -> str:
return f"template {self.gir_class.full_name}"
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.signature,
SymbolKind.Object,
self.range,
self.group.tokens["id"].range,
)
@property
def type_name(self) -> T.Optional[TypeName]:
if len(self.children[TypeName]) == 1:

View file

@ -42,6 +42,16 @@ class Menu(AstNode):
else:
return "Gio.Menu"
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.tokens["tag"],
SymbolKind.Object,
self.range,
self.group.tokens[self.tokens["tag"]].range,
self.id,
)
@property
def tag(self) -> str:
return self.tokens["tag"]
@ -72,6 +82,18 @@ class MenuAttribute(AstNode):
def value(self) -> StringValue:
return self.children[StringValue][0]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.name,
SymbolKind.Field,
self.range,
self.group.tokens["name"].range
if self.group.tokens["name"]
else self.range,
self.value.range.text,
)
@context(ValueTypeCtx)
def value_type(self) -> ValueTypeCtx:
return ValueTypeCtx(None)
@ -98,7 +120,7 @@ menu_attribute = Group(
menu_section = Group(
Menu,
[
"section",
Keyword("section"),
UseLiteral("tag", "section"),
Optional(UseIdent("id")),
Match("{").expected(),
@ -109,7 +131,7 @@ menu_section = Group(
menu_submenu = Group(
Menu,
[
"submenu",
Keyword("submenu"),
UseLiteral("tag", "submenu"),
Optional(UseIdent("id")),
Match("{").expected(),
@ -120,7 +142,7 @@ menu_submenu = Group(
menu_item = Group(
Menu,
[
"item",
Keyword("item"),
UseLiteral("tag", "item"),
Match("{").expected(),
Until(menu_attribute, "}"),
@ -130,7 +152,7 @@ menu_item = Group(
menu_item_shorthand = Group(
Menu,
[
"item",
Keyword("item"),
UseLiteral("tag", "item"),
"(",
Group(

View file

@ -58,6 +58,16 @@ class ExtScaleMark(AstNode):
else:
return None
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
str(self.value),
SymbolKind.Field,
self.range,
self.group.tokens["mark"].range,
self.label.string if self.label else None,
)
@docs("position")
def position_docs(self) -> T.Optional[str]:
if member := self.root.gir.get_type("PositionType", "Gtk").members.get(
@ -88,6 +98,15 @@ class ExtScaleMarks(AstNode):
def marks(self) -> T.List[ExtScaleMark]:
return self.children
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"marks",
SymbolKind.Array,
self.range,
self.group.tokens["marks"].range,
)
@validate("marks")
def container_is_size_group(self):
validate_parent_type(self, "Gtk", "Scale", "scale marks")

View file

@ -30,6 +30,15 @@ class Widget(AstNode):
def name(self) -> str:
return self.tokens["name"]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.name,
SymbolKind.Field,
self.range,
self.group.tokens["name"].range,
)
@validate("name")
def obj_widget(self):
object = self.context[ScopeCtx].objects.get(self.tokens["name"])
@ -62,6 +71,15 @@ class ExtSizeGroupWidgets(AstNode):
"]",
]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"widgets",
SymbolKind.Array,
self.range,
self.group.tokens["widgets"].range,
)
@validate("widgets")
def container_is_size_group(self):
validate_parent_type(self, "Gtk", "SizeGroup", "size group properties")

View file

@ -30,6 +30,15 @@ class Item(AstNode):
def child(self) -> StringValue:
return self.children[StringValue][0]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.child.range.text,
SymbolKind.String,
self.range,
self.range,
)
class ExtStringListStrings(AstNode):
grammar = [
@ -39,6 +48,15 @@ class ExtStringListStrings(AstNode):
"]",
]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"strings",
SymbolKind.Array,
self.range,
self.group.tokens["strings"].range,
)
@validate("items")
def container_is_string_list(self):
validate_parent_type(self, "Gtk", "StringList", "StringList items")

View file

@ -29,6 +29,15 @@ class StyleClass(AstNode):
def name(self) -> str:
return self.tokens["name"]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.name,
SymbolKind.String,
self.range,
self.range,
)
@validate("name")
def unique_in_parent(self):
self.validate_unique_in_parent(
@ -44,6 +53,15 @@ class ExtStyles(AstNode):
"]",
]
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
"styles",
SymbolKind.Array,
self.range,
self.group.tokens["styles"].range,
)
@validate("styles")
def container_is_widget(self):
validate_parent_type(self, "Gtk", "Widget", "style classes")

View file

@ -46,10 +46,19 @@ class Template(Object):
@property
def signature(self) -> str:
if self.parent_type:
return f"template {self.gir_class.full_name} : {self.parent_type.gir_type.full_name}"
if self.parent_type and self.parent_type.gir_type:
return f"template {self.class_name.as_string} : {self.parent_type.gir_type.full_name}"
else:
return f"template {self.gir_class.full_name}"
return f"template {self.class_name.as_string}"
@property
def document_symbol(self) -> DocumentSymbol:
return DocumentSymbol(
self.signature,
SymbolKind.Object,
self.range,
self.group.tokens["id"].range,
)
@property
def gir_class(self) -> GirType: