diff --git a/blueprintcompiler/completions.py b/blueprintcompiler/completions.py index 085baee..9940055 100644 --- a/blueprintcompiler/completions.py +++ b/blueprintcompiler/completions.py @@ -120,7 +120,7 @@ def gtk_object_completer(ast_node, match_variables): matches=new_statement_patterns, ) def property_completer(ast_node, match_variables): - if ast_node.gir_class: + if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.UncheckedType): for prop in ast_node.gir_class.properties: 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, ) def signal_completer(ast_node, match_variables): - if ast_node.gir_class: + if ast_node.gir_class and not isinstance(ast_node.gir_class, gir.UncheckedType): for signal in ast_node.gir_class.signals: if not isinstance(ast_node.parent, language.Object): name = "on" diff --git a/blueprintcompiler/gir.py b/blueprintcompiler/gir.py index 12c9772..ab8af75 100644 --- a/blueprintcompiler/gir.py +++ b/blueprintcompiler/gir.py @@ -93,13 +93,36 @@ class GirType: def doc(self): return None - def assignable_to(self, other) -> bool: + def assignable_to(self, other: "GirType") -> bool: raise NotImplementedError() @property def full_name(self) -> str: + """The GIR name of the type to use in diagnostics""" raise NotImplementedError() + @property + def glib_type_name(self) -> str: + """The name of the type in the GObject type system, suitable to pass to `g_type_from_name()`.""" + raise NotImplementedError() + + +class UncheckedType(GirType): + def __init__(self, name) -> None: + super().__init__() + self._name = name + + def assignable_to(self, other: GirType) -> bool: + return True + + @property + def full_name(self) -> str: + return self._name + + @property + def glib_type_name(self) -> str: + return self._name + class BasicType(GirType): name: str = "unknown type" @@ -111,6 +134,7 @@ class BasicType(GirType): class BoolType(BasicType): name = "bool" + glib_type_name: str = "gboolean" def assignable_to(self, other) -> bool: return isinstance(other, BoolType) @@ -118,6 +142,7 @@ class BoolType(BasicType): class IntType(BasicType): name = "int" + glib_type_name: str = "gint" def assignable_to(self, other) -> bool: return ( @@ -129,6 +154,7 @@ class IntType(BasicType): class UIntType(BasicType): name = "uint" + glib_type_name: str = "guint" def assignable_to(self, other) -> bool: return ( @@ -140,6 +166,7 @@ class UIntType(BasicType): class FloatType(BasicType): name = "float" + glib_type_name: str = "gfloat" def assignable_to(self, other) -> bool: return isinstance(other, FloatType) @@ -147,6 +174,7 @@ class FloatType(BasicType): class StringType(BasicType): name = "string" + glib_type_name: str = "gchararray" def assignable_to(self, other) -> bool: return isinstance(other, StringType) @@ -154,6 +182,7 @@ class StringType(BasicType): class TypeType(BasicType): name = "GType" + glib_type_name: str = "GType" def assignable_to(self, other) -> bool: return isinstance(other, TypeType) diff --git a/blueprintcompiler/language/common.py b/blueprintcompiler/language/common.py index f6a8f8e..ec15c7a 100644 --- a/blueprintcompiler/language/common.py +++ b/blueprintcompiler/language/common.py @@ -24,7 +24,15 @@ from ..errors import CompileError, MultipleErrors from ..completions_utils import * from .. import decompiler as decompile from ..decompiler import DecompileCtx, decompiler -from ..gir import StringType, BoolType, IntType, FloatType, GirType, Enumeration +from ..gir import ( + StringType, + BoolType, + IntType, + FloatType, + GirType, + Enumeration, + UncheckedType, +) from ..lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType from ..parse_tree import * diff --git a/blueprintcompiler/language/gobject_property.py b/blueprintcompiler/language/gobject_property.py index 2c1e2ae..c6db999 100644 --- a/blueprintcompiler/language/gobject_property.py +++ b/blueprintcompiler/language/gobject_property.py @@ -69,7 +69,7 @@ class Property(AstNode): @property def gir_property(self): - if self.gir_class is not None: + if self.gir_class is not None and not isinstance(self.gir_class, UncheckedType): return self.gir_class.properties.get(self.tokens["name"]) @property @@ -79,7 +79,7 @@ class Property(AstNode): @validate("name") def property_exists(self): - if self.gir_class is None: + if self.gir_class is None or isinstance(self.gir_class, UncheckedType): # Objects that we have no gir data on should not be validated # This happens for classes defined by the app itself return diff --git a/blueprintcompiler/language/gobject_signal.py b/blueprintcompiler/language/gobject_signal.py index d50792e..11a69e0 100644 --- a/blueprintcompiler/language/gobject_signal.py +++ b/blueprintcompiler/language/gobject_signal.py @@ -71,7 +71,7 @@ class Signal(AstNode): @property def gir_signal(self): - if self.gir_class is not None: + if self.gir_class is not None and not isinstance(self.gir_class, UncheckedType): return self.gir_class.signals.get(self.tokens["name"]) @property @@ -80,7 +80,7 @@ class Signal(AstNode): @validate("name") def signal_exists(self): - if self.gir_class is None: + if self.gir_class is None or isinstance(self.gir_class, UncheckedType): # Objects that we have no gir data on should not be validated # This happens for classes defined by the app itself return diff --git a/blueprintcompiler/language/gtkbuilder_template.py b/blueprintcompiler/language/gtkbuilder_template.py index a215cdf..40ac7f7 100644 --- a/blueprintcompiler/language/gtkbuilder_template.py +++ b/blueprintcompiler/language/gtkbuilder_template.py @@ -53,6 +53,8 @@ class Template(Object): # Templates might not have a parent class defined if class_name := self.class_name: return class_name.gir_type + else: + return gir.UncheckedType(self.id) @validate("id") def unique_in_parent(self): diff --git a/blueprintcompiler/language/types.py b/blueprintcompiler/language/types.py index 0987262..3b454bc 100644 --- a/blueprintcompiler/language/types.py +++ b/blueprintcompiler/language/types.py @@ -56,12 +56,13 @@ class TypeName(AstNode): return self.root.gir.namespaces.get(self.tokens["namespace"] or "Gtk") @property - def gir_type(self) -> T.Optional[gir.Class]: + def gir_type(self) -> gir.GirType: if self.tokens["class_name"] and not self.tokens["ignore_gir"]: return self.root.gir.get_type( self.tokens["class_name"], self.tokens["namespace"] ) - return None + + return gir.UncheckedType(self.tokens["class_name"]) @property def glib_type_name(self) -> str: @@ -84,7 +85,11 @@ class TypeName(AstNode): class ClassName(TypeName): @validate("namespace", "class_name") def gir_class_exists(self): - if self.gir_type is not None and not isinstance(self.gir_type, Class): + if ( + self.gir_type + and not isinstance(self.gir_type, UncheckedType) + and not isinstance(self.gir_type, Class) + ): if isinstance(self.gir_type, Interface): raise CompileError( f"{self.gir_type.full_name} is an interface, not a class"