mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
completions: Add types in typeof<> and as<>
This commit is contained in:
parent
d5b2ee3589
commit
e9206809d6
10 changed files with 124 additions and 39 deletions
|
@ -196,6 +196,13 @@ class AstNode:
|
|||
|
||||
return None
|
||||
|
||||
def get_child_at(self, idx: int) -> "AstNode":
|
||||
for child in self.children:
|
||||
if idx in child.range:
|
||||
return child.get_child_at(idx)
|
||||
|
||||
return self
|
||||
|
||||
def get_semantic_tokens(self) -> T.Iterator[SemanticToken]:
|
||||
for child in self.children:
|
||||
yield from child.get_semantic_tokens()
|
||||
|
|
|
@ -21,7 +21,16 @@ import typing as T
|
|||
|
||||
from . import annotations, gir, language
|
||||
from .ast_utils import AstNode
|
||||
from .completions_utils import *
|
||||
from .completions_utils import (
|
||||
CompletionContext,
|
||||
completers,
|
||||
completer,
|
||||
get_sort_key,
|
||||
new_statement_patterns,
|
||||
get_property_completion,
|
||||
CompletionItemKind,
|
||||
CompletionPriority,
|
||||
)
|
||||
from .language.types import ClassName
|
||||
from .lsp_utils import Completion, CompletionItemKind, TextEdit, get_docs_section
|
||||
from .parser import SKIP_TOKENS
|
||||
|
@ -38,13 +47,6 @@ def _complete(
|
|||
token_idx: int,
|
||||
next_token: Token,
|
||||
) -> T.Iterator[Completion]:
|
||||
for child in ast_node.children:
|
||||
if child.group.start <= idx and (
|
||||
idx < child.group.end or (idx == child.group.end and child.incomplete)
|
||||
):
|
||||
yield from _complete(lsp, child, tokens, idx, token_idx, next_token)
|
||||
return
|
||||
|
||||
prev_tokens: T.List[Token] = []
|
||||
|
||||
# collect the 5 previous non-skipped tokens
|
||||
|
@ -54,7 +56,7 @@ def _complete(
|
|||
prev_tokens.insert(0, token)
|
||||
token_idx -= 1
|
||||
|
||||
for completer in ast_node.completers:
|
||||
for completer in completers:
|
||||
yield from completer(prev_tokens, next_token, ast_node, lsp, idx)
|
||||
|
||||
|
||||
|
@ -84,7 +86,17 @@ def complete(
|
|||
idx = tokens[token_idx].start
|
||||
token_idx -= 1
|
||||
|
||||
yield from _complete(lsp, ast_node, tokens, idx, token_idx, next_token)
|
||||
child_node = ast_node.get_child_at(idx)
|
||||
# If the cursor is at the end of a node, completions should be for the next child of the parent, unless the node
|
||||
# is incomplete.
|
||||
while (
|
||||
child_node.range.end == idx
|
||||
and not child_node.incomplete
|
||||
and child_node.parent is not None
|
||||
):
|
||||
child_node = child_node.parent
|
||||
|
||||
yield from _complete(lsp, child_node, tokens, idx, token_idx, next_token)
|
||||
|
||||
|
||||
@completer([language.GtkDirective])
|
||||
|
@ -163,7 +175,13 @@ def _available_namespace_completions(ctx: CompletionContext):
|
|||
|
||||
|
||||
@completer(
|
||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||
applies_in=[
|
||||
language.UI,
|
||||
language.ObjectContent,
|
||||
language.Template,
|
||||
language.TypeName,
|
||||
language.BracketedTypeName,
|
||||
],
|
||||
matches=new_statement_patterns,
|
||||
)
|
||||
def namespace(ctx: CompletionContext):
|
||||
|
@ -184,7 +202,13 @@ def namespace(ctx: CompletionContext):
|
|||
|
||||
|
||||
@completer(
|
||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||
applies_in=[
|
||||
language.UI,
|
||||
language.ObjectContent,
|
||||
language.Template,
|
||||
language.TypeName,
|
||||
language.BracketedTypeName,
|
||||
],
|
||||
matches=[
|
||||
[(TokenType.IDENT, None), (TokenType.OP, "."), (TokenType.IDENT, None)],
|
||||
[(TokenType.IDENT, None), (TokenType.OP, ".")],
|
||||
|
@ -195,7 +219,11 @@ def object_completer(ctx: CompletionContext):
|
|||
if ns is not None:
|
||||
for c in ns.classes.values():
|
||||
snippet = c.name
|
||||
if str(ctx.next_token) != "{":
|
||||
if (
|
||||
str(ctx.next_token) != "{"
|
||||
and not isinstance(ctx.ast_node, language.TypeName)
|
||||
and not isinstance(ctx.ast_node, language.BracketedTypeName)
|
||||
):
|
||||
snippet += " {\n $0\n}"
|
||||
|
||||
yield Completion(
|
||||
|
@ -209,7 +237,13 @@ def object_completer(ctx: CompletionContext):
|
|||
|
||||
|
||||
@completer(
|
||||
applies_in=[language.UI, language.ObjectContent, language.Template],
|
||||
applies_in=[
|
||||
language.UI,
|
||||
language.ObjectContent,
|
||||
language.Template,
|
||||
language.TypeName,
|
||||
language.BracketedTypeName,
|
||||
],
|
||||
matches=new_statement_patterns,
|
||||
)
|
||||
def gtk_object_completer(ctx: CompletionContext):
|
||||
|
@ -217,7 +251,11 @@ def gtk_object_completer(ctx: CompletionContext):
|
|||
if ns is not None:
|
||||
for c in ns.classes.values():
|
||||
snippet = c.name
|
||||
if str(ctx.next_token) != "{":
|
||||
if (
|
||||
str(ctx.next_token) != "{"
|
||||
and not isinstance(ctx.ast_node, language.TypeName)
|
||||
and not isinstance(ctx.ast_node, language.BracketedTypeName)
|
||||
):
|
||||
snippet += " {\n $0\n}"
|
||||
|
||||
yield Completion(
|
||||
|
@ -229,6 +267,17 @@ def gtk_object_completer(ctx: CompletionContext):
|
|||
detail=c.detail,
|
||||
)
|
||||
|
||||
if isinstance(ctx.ast_node, language.BracketedTypeName) or (
|
||||
isinstance(ctx.ast_node, language.TypeName)
|
||||
and not isinstance(ctx.ast_node, language.ClassName)
|
||||
):
|
||||
for basic_type in gir.BASIC_TYPES:
|
||||
yield Completion(
|
||||
basic_type,
|
||||
CompletionItemKind.Class,
|
||||
sort_text=get_sort_key(CompletionPriority.CLASS, basic_type),
|
||||
)
|
||||
|
||||
|
||||
@completer(
|
||||
applies_in=[language.ObjectContent],
|
||||
|
|
|
@ -57,14 +57,21 @@ new_statement_patterns = [
|
|||
[(TokenType.PUNCTUATION, "}")],
|
||||
[(TokenType.PUNCTUATION, "]")],
|
||||
[(TokenType.PUNCTUATION, ";")],
|
||||
[(TokenType.OP, "<")],
|
||||
]
|
||||
|
||||
|
||||
completers = []
|
||||
|
||||
|
||||
def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None):
|
||||
def decorator(func: T.Callable[[CompletionContext], T.Generator[Completion]]):
|
||||
def inner(
|
||||
prev_tokens: T.List[Token], next_token: Token, ast_node, lsp, idx: int
|
||||
):
|
||||
if not any(isinstance(ast_node, rule) for rule in applies_in):
|
||||
return
|
||||
|
||||
# For completers that apply in ObjectContent nodes, we can further
|
||||
# check that the object is the right class
|
||||
if applies_in_subclass is not None:
|
||||
|
@ -118,8 +125,7 @@ def completer(applies_in: T.List, matches: T.List = [], applies_in_subclass=None
|
|||
)
|
||||
yield from func(context)
|
||||
|
||||
for c in applies_in:
|
||||
c.completers.append(inner)
|
||||
completers.append(inner)
|
||||
return inner
|
||||
|
||||
return decorator
|
||||
|
|
|
@ -289,7 +289,7 @@ class TypeType(BasicType):
|
|||
return isinstance(other, TypeType)
|
||||
|
||||
|
||||
_BASIC_TYPES = {
|
||||
BASIC_TYPES = {
|
||||
"bool": BoolType,
|
||||
"string": StringType,
|
||||
"int": IntType,
|
||||
|
@ -914,7 +914,7 @@ class Namespace(GirNode):
|
|||
|
||||
def get_type_by_cname(self, cname: str) -> T.Optional[GirType]:
|
||||
"""Gets a type from this namespace by its C name."""
|
||||
for basic in _BASIC_TYPES.values():
|
||||
for basic in BASIC_TYPES.values():
|
||||
if basic.glib_type_name == cname:
|
||||
return basic()
|
||||
|
||||
|
@ -1036,8 +1036,8 @@ class GirContext:
|
|||
return None
|
||||
|
||||
def get_type(self, name: str, ns: str) -> T.Optional[GirType]:
|
||||
if ns is None and name in _BASIC_TYPES:
|
||||
return _BASIC_TYPES[name]()
|
||||
if ns is None and name in BASIC_TYPES:
|
||||
return BASIC_TYPES[name]()
|
||||
|
||||
ns = ns or "Gtk"
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ from .gtkbuilder_child import (
|
|||
from .gtkbuilder_template import Template
|
||||
from .imports import GtkDirective, Import
|
||||
from .response_id import ExtResponse
|
||||
from .types import ClassName
|
||||
from .types import BracketedTypeName, ClassName, TypeName
|
||||
from .ui import UI
|
||||
from .values import (
|
||||
ArrayValue,
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
from ..decompiler import decompile_element
|
||||
from .common import *
|
||||
from .contexts import ScopeCtx, ValueTypeCtx
|
||||
from .types import TypeName
|
||||
from .types import BracketedTypeName, TypeName
|
||||
|
||||
expr = Sequence()
|
||||
|
||||
|
@ -196,7 +196,7 @@ class CastExpr(InfixExpr):
|
|||
grammar = [
|
||||
Keyword("as"),
|
||||
AnyOf(
|
||||
["<", TypeName, Match(">").expected()],
|
||||
BracketedTypeName,
|
||||
[
|
||||
UseExact("lparen", "("),
|
||||
TypeName,
|
||||
|
@ -211,7 +211,13 @@ class CastExpr(InfixExpr):
|
|||
|
||||
@property
|
||||
def type(self) -> T.Optional[GirType]:
|
||||
if len(self.children[BracketedTypeName]) == 1:
|
||||
type_name = self.children[BracketedTypeName][0].type_name
|
||||
return None if type_name is None else type_name.gir_type
|
||||
elif len(self.children[TypeName]) == 1:
|
||||
return self.children[TypeName][0].gir_type
|
||||
else:
|
||||
return None
|
||||
|
||||
@validate()
|
||||
def cast_makes_sense(self):
|
||||
|
|
|
@ -27,11 +27,11 @@ class TypeName(AstNode):
|
|||
[
|
||||
UseIdent("namespace"),
|
||||
".",
|
||||
UseIdent("class_name"),
|
||||
UseIdent("class_name").expected("class name"),
|
||||
],
|
||||
[
|
||||
AnyOf("$", [".", UseLiteral("old_extern", True)]),
|
||||
UseIdent("class_name"),
|
||||
UseIdent("class_name").expected("class name"),
|
||||
UseLiteral("extern", True),
|
||||
],
|
||||
UseIdent("class_name"),
|
||||
|
@ -47,7 +47,11 @@ class TypeName(AstNode):
|
|||
|
||||
@validate("class_name")
|
||||
def type_exists(self):
|
||||
if not self.tokens["extern"] and self.gir_ns is not None:
|
||||
if (
|
||||
not self.tokens["extern"]
|
||||
and self.gir_ns is not None
|
||||
and self.tokens["class_name"] is not None
|
||||
):
|
||||
self.root.gir.validate_type(
|
||||
self.tokens["class_name"], self.tokens["namespace"]
|
||||
)
|
||||
|
@ -182,3 +186,14 @@ class TemplateClassName(ClassName):
|
|||
self.root.gir.validate_type(
|
||||
self.tokens["class_name"], self.tokens["namespace"]
|
||||
)
|
||||
|
||||
|
||||
class BracketedTypeName(AstNode):
|
||||
grammar = Statement("<", to_parse_node(TypeName).expected("type name"), end=">")
|
||||
|
||||
@property
|
||||
def type_name(self) -> T.Optional[TypeName]:
|
||||
if len(self.children[TypeName]) == 0:
|
||||
return None
|
||||
|
||||
return self.children[TypeName][0]
|
||||
|
|
|
@ -26,7 +26,7 @@ from .common import *
|
|||
from .contexts import ExprValueCtx, ScopeCtx, ValueTypeCtx
|
||||
from .expression import Expression
|
||||
from .gobject_object import Object
|
||||
from .types import TypeName
|
||||
from .types import BracketedTypeName, TypeName
|
||||
|
||||
|
||||
class Translated(AstNode):
|
||||
|
@ -80,11 +80,7 @@ class TypeLiteral(AstNode):
|
|||
grammar = [
|
||||
"typeof",
|
||||
AnyOf(
|
||||
[
|
||||
"<",
|
||||
to_parse_node(TypeName).expected("type name"),
|
||||
Match(">").expected(),
|
||||
],
|
||||
BracketedTypeName,
|
||||
[
|
||||
UseExact("lparen", "("),
|
||||
to_parse_node(TypeName).expected("type name"),
|
||||
|
@ -98,8 +94,13 @@ class TypeLiteral(AstNode):
|
|||
return gir.TypeType()
|
||||
|
||||
@property
|
||||
def type_name(self) -> TypeName:
|
||||
def type_name(self) -> T.Optional[TypeName]:
|
||||
if len(self.children[BracketedTypeName]) == 1:
|
||||
return self.children[BracketedTypeName][0].type_name
|
||||
elif len(self.children[TypeName]) == 1:
|
||||
return self.children[TypeName][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@validate()
|
||||
def validate_for_type(self) -> None:
|
||||
|
|
|
@ -209,6 +209,7 @@ class XmlOutput(OutputFormat):
|
|||
else:
|
||||
xml.put_text(self._object_id(value, value.ident))
|
||||
elif isinstance(value, TypeLiteral):
|
||||
assert value.type_name is not None
|
||||
xml.put_text(value.type_name.glib_type_name)
|
||||
else:
|
||||
if isinstance(value.value, float) and value.value == int(value.value):
|
||||
|
|
|
@ -280,9 +280,9 @@ class Err(ParseNode):
|
|||
start_idx -= 1
|
||||
start_token = ctx.tokens[start_idx]
|
||||
|
||||
raise CompileError(
|
||||
self.message, Range(start_token.end, start_token.end, ctx.text)
|
||||
)
|
||||
position = start_token.start if ctx.start == start_idx else start_token.end
|
||||
|
||||
raise CompileError(self.message, Range(position, position, ctx.text))
|
||||
return True
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue