Fix template types

This commit is contained in:
James Westman 2023-03-28 10:41:42 -05:00
parent 64879491a1
commit 88f5b4f1c7
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
14 changed files with 120 additions and 21 deletions

View file

@ -120,7 +120,7 @@ def gtk_object_completer(ast_node, match_variables):
matches=new_statement_patterns, matches=new_statement_patterns,
) )
def property_completer(ast_node, match_variables): def property_completer(ast_node, match_variables):
if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.UncheckedType): if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.ExternType):
for prop in ast_node.gir_class.properties: for prop in ast_node.gir_class.properties:
yield Completion(prop, CompletionItemKind.Property, snippet=f"{prop}: $0;") yield Completion(prop, CompletionItemKind.Property, snippet=f"{prop}: $0;")
@ -144,7 +144,7 @@ def prop_value_completer(ast_node, match_variables):
matches=new_statement_patterns, matches=new_statement_patterns,
) )
def signal_completer(ast_node, match_variables): def signal_completer(ast_node, match_variables):
if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.UncheckedType): if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.ExternType):
for signal in ast_node.gir_class.signals: for signal in ast_node.gir_class.signals:
if not isinstance(ast_node.parent, language.Object): if not isinstance(ast_node.parent, language.Object):
name = "on" name = "on"

View file

@ -114,8 +114,12 @@ class GirType:
"""The name of the type in the GObject type system, suitable to pass to `g_type_from_name()`.""" """The name of the type in the GObject type system, suitable to pass to `g_type_from_name()`."""
raise NotImplementedError() raise NotImplementedError()
@property
def incomplete(self) -> bool:
return False
class UncheckedType(GirType):
class ExternType(GirType):
def __init__(self, name: str) -> None: def __init__(self, name: str) -> None:
super().__init__() super().__init__()
self._name = name self._name = name
@ -131,6 +135,10 @@ class UncheckedType(GirType):
def glib_type_name(self) -> str: def glib_type_name(self) -> str:
return self._name return self._name
@property
def incomplete(self) -> bool:
return True
class ArrayType(GirType): class ArrayType(GirType):
def __init__(self, inner: GirType) -> None: def __init__(self, inner: GirType) -> None:
@ -507,6 +515,60 @@ class Class(GirNode, GirType):
yield from impl.signals.values() yield from impl.signals.values()
class TemplateType(GirType):
def __init__(self, name: str, parent: T.Optional[Class]):
self._name = name
self.parent = parent
@property
def name(self) -> str:
return self._name
@property
def full_name(self) -> str:
return self._name
@property
def glib_type_name(self) -> str:
return self._name
@cached_property
def properties(self) -> T.Mapping[str, Property]:
if self.parent is None or isinstance(self.parent, ExternType):
return {}
else:
return self.parent.properties
@cached_property
def signals(self) -> T.Mapping[str, Signal]:
if self.parent is None or isinstance(self.parent, ExternType):
return {}
else:
return self.parent.signals
def assignable_to(self, other: "GirType") -> bool:
if self == other:
return True
elif isinstance(other, Interface):
# we don't know the template type's interfaces, assume yes
return True
elif self.parent is None or isinstance(self.parent, ExternType):
return isinstance(other, Class)
else:
return self.parent.assignable_to(other)
@cached_property
def signature(self) -> str:
if self.parent is None:
return f"template {self.name}"
else:
return f"template {self.name} : {self.parent.full_name}"
@property
def incomplete(self) -> bool:
return True
class EnumMember(GirNode): class EnumMember(GirNode):
def __init__(self, enum: "Enumeration", tl: typelib.Typelib) -> None: def __init__(self, enum: "Enumeration", tl: typelib.Typelib) -> None:
super().__init__(enum, tl) super().__init__(enum, tl)

View file

@ -37,7 +37,7 @@ from ..gir import (
FloatType, FloatType,
GirType, GirType,
Enumeration, Enumeration,
UncheckedType, ExternType,
) )
from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType
from ..parse_tree import * from ..parse_tree import *

View file

@ -141,7 +141,7 @@ class LookupOp(InfixExpr):
], ],
) )
if isinstance(self.lhs.type, UncheckedType) or not self.lhs.type_complete: if self.lhs.type.incomplete:
return return
elif not isinstance(self.lhs.type, gir.Class) and not isinstance( elif not isinstance(self.lhs.type, gir.Class) and not isinstance(

View file

@ -46,7 +46,7 @@ class Property(AstNode):
@property @property
def gir_property(self): def gir_property(self):
if self.gir_class is not None and not isinstance(self.gir_class, UncheckedType): if self.gir_class is not None and not isinstance(self.gir_class, ExternType):
return self.gir_class.properties.get(self.tokens["name"]) return self.gir_class.properties.get(self.tokens["name"])
@context(ValueTypeCtx) @context(ValueTypeCtx)
@ -75,7 +75,7 @@ class Property(AstNode):
@validate("name") @validate("name")
def property_exists(self): def property_exists(self):
if self.gir_class is None or isinstance(self.gir_class, UncheckedType): if self.gir_class is None or self.gir_class.incomplete:
# Objects that we have no gir data on should not be validated # Objects that we have no gir data on should not be validated
# This happens for classes defined by the app itself # This happens for classes defined by the app itself
return return

View file

@ -72,7 +72,7 @@ class Signal(AstNode):
@property @property
def gir_signal(self): def gir_signal(self):
if self.gir_class is not None and not isinstance(self.gir_class, UncheckedType): if self.gir_class is not None and not isinstance(self.gir_class, ExternType):
return self.gir_class.signals.get(self.tokens["name"]) return self.gir_class.signals.get(self.tokens["name"])
@property @property
@ -90,7 +90,7 @@ class Signal(AstNode):
@validate("name") @validate("name")
def signal_exists(self): def signal_exists(self):
if self.gir_class is None or isinstance(self.gir_class, UncheckedType): if self.gir_class is None or self.gir_class.incomplete:
# Objects that we have no gir data on should not be validated # Objects that we have no gir data on should not be validated
# This happens for classes defined by the app itself # This happens for classes defined by the app itself
return return

View file

@ -50,11 +50,10 @@ class Template(Object):
@property @property
def gir_class(self): def gir_class(self):
# Templates might not have a parent class defined if self.class_name is None:
if class_name := self.class_name: return gir.TemplateType(self.id, None)
return class_name.gir_type
else: else:
return gir.UncheckedType(self.id) return gir.TemplateType(self.id, self.class_name.gir_type)
@validate("id") @validate("id")
def unique_in_parent(self): def unique_in_parent(self):

View file

@ -108,11 +108,7 @@ class PropertyBinding(AstNode):
gir_class = self.source_obj.gir_class gir_class = self.source_obj.gir_class
if ( if gir_class is None or gir_class.incomplete:
isinstance(self.source_obj, Template)
or gir_class is None
or isinstance(gir_class, UncheckedType)
):
# Objects that we have no gir data on should not be validated # Objects that we have no gir data on should not be validated
# This happens for classes defined by the app itself # This happens for classes defined by the app itself
return return

View file

@ -20,7 +20,7 @@
import typing as T import typing as T
from .common import * from .common import *
from ..gir import Class, Interface from ..gir import Class, ExternType, Interface
class TypeName(AstNode): class TypeName(AstNode):
@ -70,7 +70,7 @@ class TypeName(AstNode):
self.tokens["class_name"], self.tokens["namespace"] self.tokens["class_name"], self.tokens["namespace"]
) )
return gir.UncheckedType(self.tokens["class_name"]) return gir.ExternType(self.tokens["class_name"])
@property @property
def glib_type_name(self) -> str: def glib_type_name(self) -> str:
@ -95,7 +95,7 @@ class ClassName(TypeName):
def gir_class_exists(self): def gir_class_exists(self):
if ( if (
self.gir_type is not None self.gir_type is not None
and not isinstance(self.gir_type, UncheckedType) and not isinstance(self.gir_type, ExternType)
and not isinstance(self.gir_type, Class) and not isinstance(self.gir_type, Class)
): ):
if isinstance(self.gir_type, Interface): if isinstance(self.gir_type, Interface):

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
template MyTemplate : Box {
prop1: bind MyTemplate.prop2 as ($MyObject).prop3;
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="MyTemplate" parent="GtkBox">
<binding name="prop1">
<lookup name="prop3" type="MyObject">
<lookup name="prop2" type="MyTemplate">
<constant>MyTemplate</constant>
</lookup>
</lookup>
</binding>
</template>
</interface>

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
template MyTemplate : $MyParentClass {
prop1: bind MyTemplate.prop2 as ($MyObject).prop3;
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="MyTemplate" parent="MyParentClass">
<binding name="prop1">
<lookup name="prop3" type="MyObject">
<lookup name="prop2" type="MyTemplate">
<constant>MyTemplate</constant>
</lookup>
</lookup>
</binding>
</template>
</interface>

View file

@ -187,6 +187,12 @@ class TestSamples(unittest.TestCase):
self.assert_sample( self.assert_sample(
"template", skip_run=True "template", skip_run=True
) # The template class doesn't exist ) # The template class doesn't exist
self.assert_sample(
"template_binding", skip_run=True
) # The template class doesn't exist
self.assert_sample(
"template_binding_extern", skip_run=True
) # The template class doesn't exist
self.assert_sample( self.assert_sample(
"template_no_parent", skip_run=True "template_no_parent", skip_run=True
) # The template class doesn't exist ) # The template class doesn't exist