1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{self, BindingExpression, Callable, Expression, Unit};
12use crate::langtype::{
13 BuiltinElement, BuiltinPropertyDefault, Enumeration, EnumerationValue, Function, NativeClass,
14 Struct, Type,
15};
16use crate::langtype::{ElementType, PropertyLookupResult};
17use crate::layout::{LayoutConstraints, Orientation};
18use crate::namedreference::NamedReference;
19use crate::parser;
20use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNode};
21use crate::typeloader::{ImportKind, ImportedTypes, LibraryInfo};
22use crate::typeregister::TypeRegister;
23use itertools::Either;
24use smol_str::{format_smolstr, SmolStr, ToSmolStr};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::btree_map::Entry;
27use std::collections::{BTreeMap, HashMap, HashSet};
28use std::fmt::Display;
29use std::path::PathBuf;
30use std::rc::{Rc, Weak};
31
32macro_rules! unwrap_or_continue {
33 ($e:expr ; $diag:expr) => {
34 match $e {
35 Some(x) => x,
36 None => {
37 debug_assert!($diag.has_errors()); continue;
39 }
40 }
41 };
42}
43
44#[derive(Default)]
46pub struct Document {
47 pub node: Option<syntax_nodes::Document>,
48 pub inner_components: Vec<Rc<Component>>,
49 pub inner_types: Vec<Type>,
50 pub local_registry: TypeRegister,
51 pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
54 pub exports: Exports,
55 pub imports: Vec<ImportedTypes>,
56 pub library_exports: HashMap<String, LibraryInfo>,
57
58 pub embedded_file_resources:
61 RefCell<BTreeMap<SmolStr, crate::embedded_resources::EmbeddedResources>>,
62
63 #[cfg(feature = "bundle-translations")]
64 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
65
66 pub used_types: RefCell<UsedSubTypes>,
68
69 pub popup_menu_impl: Option<Rc<Component>>,
71}
72
73impl Document {
74 pub fn from_node(
75 node: syntax_nodes::Document,
76 imports: Vec<ImportedTypes>,
77 reexports: Exports,
78 diag: &mut BuildDiagnostics,
79 parent_registry: &Rc<RefCell<TypeRegister>>,
80 ) -> Self {
81 debug_assert_eq!(node.kind(), SyntaxKind::Document);
82
83 let mut local_registry = TypeRegister::new(parent_registry);
84 let mut inner_components = vec![];
85 let mut inner_types = vec![];
86
87 let mut process_component =
88 |n: syntax_nodes::Component,
89 diag: &mut BuildDiagnostics,
90 local_registry: &mut TypeRegister| {
91 let compo = Component::from_node(n, diag, local_registry);
92 if !local_registry.add(compo.clone()) {
93 diag.push_warning(format!("Component '{}' is replacing a previously defined component with the same name", compo.id), &syntax_nodes::Component::from(compo.node.clone().unwrap()).DeclaredIdentifier());
94 }
95 inner_components.push(compo);
96 };
97 let process_struct = |n: syntax_nodes::StructDeclaration,
98 diag: &mut BuildDiagnostics,
99 local_registry: &mut TypeRegister,
100 inner_types: &mut Vec<Type>| {
101 let rust_attributes = n.AtRustAttr().map(|child| vec![child.text().to_smolstr()]);
102 let ty = type_struct_from_node(
103 n.ObjectType(),
104 diag,
105 local_registry,
106 rust_attributes,
107 parser::identifier_text(&n.DeclaredIdentifier()),
108 );
109 assert!(matches!(ty, Type::Struct(_)));
110 if !local_registry.insert_type(ty.clone()) {
111 diag.push_warning(
112 format!(
113 "Struct '{ty}' is replacing a previously defined type with the same name"
114 ),
115 &n.DeclaredIdentifier(),
116 );
117 }
118 inner_types.push(ty);
119 };
120 let process_enum = |n: syntax_nodes::EnumDeclaration,
121 diag: &mut BuildDiagnostics,
122 local_registry: &mut TypeRegister,
123 inner_types: &mut Vec<Type>| {
124 let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else {
125 assert!(diag.has_errors());
126 return;
127 };
128 let mut existing_names = HashSet::new();
129 let values = n
130 .EnumValue()
131 .filter_map(|v| {
132 let value = parser::identifier_text(&v)?;
133 if value == name {
134 diag.push_error(
135 format!("Enum '{value}' can't have a value with the same name"),
136 &v,
137 );
138 None
139 } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) {
140 diag.push_error(format!("Duplicated enum value '{value}'"), &v);
141 None
142 } else {
143 Some(value)
144 }
145 })
146 .collect();
147 let en =
148 Enumeration { name: name.clone(), values, default_value: 0, node: Some(n.clone()) };
149 if en.values.is_empty() {
150 diag.push_error("Enums must have at least one value".into(), &n);
151 }
152
153 let ty = Type::Enumeration(Rc::new(en));
154 if !local_registry.insert_type_with_name(ty.clone(), name.clone()) {
155 diag.push_warning(
156 format!(
157 "Enum '{name}' is replacing a previously defined type with the same name"
158 ),
159 &n.DeclaredIdentifier(),
160 );
161 }
162 inner_types.push(ty);
163 };
164
165 for n in node.children() {
166 match n.kind() {
167 SyntaxKind::Component => process_component(n.into(), diag, &mut local_registry),
168 SyntaxKind::StructDeclaration => {
169 process_struct(n.into(), diag, &mut local_registry, &mut inner_types)
170 }
171 SyntaxKind::EnumDeclaration => {
172 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
173 }
174 SyntaxKind::ExportsList => {
175 for n in n.children() {
176 match n.kind() {
177 SyntaxKind::Component => {
178 process_component(n.into(), diag, &mut local_registry)
179 }
180 SyntaxKind::StructDeclaration => process_struct(
181 n.into(),
182 diag,
183 &mut local_registry,
184 &mut inner_types,
185 ),
186 SyntaxKind::EnumDeclaration => {
187 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
188 }
189 _ => {}
190 }
191 }
192 }
193 _ => {}
194 };
195 }
196 let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
197 exports.add_reexports(reexports, diag);
198
199 let custom_fonts = imports
200 .iter()
201 .filter(|import| matches!(import.import_kind, ImportKind::FileImport))
202 .filter_map(|import| {
203 if import.file.ends_with(".ttc")
204 || import.file.ends_with(".ttf")
205 || import.file.ends_with(".otf")
206 {
207 let token_path = import.import_uri_token.source_file.path();
208 let import_file_path = PathBuf::from(import.file.clone());
209 let import_file_path = crate::pathutils::join(token_path, &import_file_path)
210 .unwrap_or(import_file_path);
211
212 if crate::pathutils::is_url(&import_file_path)
215 || crate::fileaccess::load_file(std::path::Path::new(&import_file_path))
216 .is_some()
217 {
218 Some((import_file_path.to_string_lossy().into(), import.import_uri_token.clone()))
219 } else {
220 diag.push_error(
221 format!("File \"{}\" not found", import.file),
222 &import.import_uri_token,
223 );
224 None
225 }
226 } else if import.file.ends_with(".slint") {
227 diag.push_error("Import names are missing. Please specify which types you would like to import".into(), &import.import_uri_token.parent());
228 None
229 } else {
230 diag.push_error(
231 format!("Unsupported foreign import \"{}\"", import.file),
232 &import.import_uri_token,
233 );
234 None
235 }
236 })
237 .collect();
238
239 for local_compo in &inner_components {
240 if exports
241 .components_or_types
242 .iter()
243 .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left())
244 .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo))
245 {
246 continue;
247 }
248 if local_compo.is_global() {
251 continue;
252 }
253 if !local_compo.used.get() {
254 diag.push_warning(
255 "Component is neither used nor exported".into(),
256 &local_compo.node.as_ref().map(|n| n.to_source_location()),
257 )
258 }
259 }
260
261 Document {
262 node: Some(node),
263 inner_components,
264 inner_types,
265 local_registry,
266 custom_fonts,
267 imports,
268 exports,
269 library_exports: Default::default(),
270 embedded_file_resources: Default::default(),
271 #[cfg(feature = "bundle-translations")]
272 translation_builder: None,
273 used_types: Default::default(),
274 popup_menu_impl: None,
275 }
276 }
277
278 pub fn exported_roots(&self) -> impl DoubleEndedIterator<Item = Rc<Component>> + '_ {
279 self.exports.iter().filter_map(|e| e.1.as_ref().left()).filter(|c| !c.is_global()).cloned()
280 }
281
282 pub fn last_exported_component(&self) -> Option<Rc<Component>> {
284 self.exports
285 .iter()
286 .filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
287 .filter(|(_, c)| !c.is_global())
288 .max_by_key(|(n, _)| n.text_range().end())
289 .map(|(_, c)| c.clone())
290 }
291
292 pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
294 let used_types = self.used_types.borrow();
295 for c in &used_types.sub_components {
296 v(c);
297 }
298 for c in self.exported_roots() {
299 v(&c);
300 }
301 for c in &used_types.globals {
302 v(c);
303 }
304 if let Some(c) = &self.popup_menu_impl {
305 v(c);
306 }
307 }
308}
309
310#[derive(Debug, Clone)]
311pub struct PopupWindow {
312 pub component: Rc<Component>,
313 pub x: NamedReference,
314 pub y: NamedReference,
315 pub close_policy: EnumerationValue,
316 pub parent_element: ElementRc,
317}
318
319#[derive(Debug, Clone)]
320pub struct Timer {
321 pub interval: NamedReference,
322 pub triggered: NamedReference,
323 pub running: NamedReference,
324 pub element: ElementWeak,
325}
326
327#[derive(Clone, Debug)]
328pub struct ChildrenInsertionPoint {
329 pub parent: ElementRc,
330 pub insertion_index: usize,
331 pub node: syntax_nodes::ChildrenPlaceholder,
332}
333
334#[derive(Debug, Default)]
336pub struct UsedSubTypes {
337 pub globals: Vec<Rc<Component>>,
339 pub structs_and_enums: Vec<Type>,
341 pub sub_components: Vec<Rc<Component>>,
344 pub library_types_imports: Vec<(SmolStr, LibraryInfo)>,
347 pub library_global_imports: Vec<(SmolStr, LibraryInfo)>,
350}
351
352#[derive(Debug, Default, Clone)]
353pub struct InitCode {
354 pub constructor_code: Vec<Expression>,
356 pub focus_setting_code: Vec<Expression>,
358 pub font_registration_code: Vec<Expression>,
360
361 pub inlined_init_code: BTreeMap<usize, Expression>,
364}
365
366impl InitCode {
367 pub fn iter(&self) -> impl Iterator<Item = &Expression> {
368 self.font_registration_code
369 .iter()
370 .chain(self.focus_setting_code.iter())
371 .chain(self.constructor_code.iter())
372 .chain(self.inlined_init_code.values())
373 }
374 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
375 self.font_registration_code
376 .iter_mut()
377 .chain(self.focus_setting_code.iter_mut())
378 .chain(self.constructor_code.iter_mut())
379 .chain(self.inlined_init_code.values_mut())
380 }
381}
382
383#[derive(Default, Debug)]
386pub struct Component {
387 pub node: Option<syntax_nodes::Component>,
388 pub id: SmolStr,
389 pub root_element: ElementRc,
390
391 pub parent_element: Weak<RefCell<Element>>,
393
394 pub optimized_elements: RefCell<Vec<ElementRc>>,
397
398 pub root_constraints: RefCell<LayoutConstraints>,
400
401 pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>,
404
405 pub init_code: RefCell<InitCode>,
406
407 pub popup_windows: RefCell<Vec<PopupWindow>>,
408 pub timers: RefCell<Vec<Timer>>,
409 pub menu_item_tree: RefCell<Vec<Rc<Component>>>,
410
411 pub inherits_popup_window: Cell<bool>,
413
414 pub exported_global_names: RefCell<Vec<ExportedName>>,
417
418 pub used: Cell<bool>,
420
421 pub private_properties: RefCell<Vec<(SmolStr, Type)>>,
424
425 pub from_library: Cell<bool>,
427}
428
429impl Component {
430 pub fn from_node(
431 node: syntax_nodes::Component,
432 diag: &mut BuildDiagnostics,
433 tr: &TypeRegister,
434 ) -> Rc<Self> {
435 let mut child_insertion_point = None;
436 let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some();
437 let c = Component {
438 node: Some(node.clone()),
439 id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
440 root_element: Element::from_node(
441 node.Element(),
442 "root".into(),
443 if node.child_text(SyntaxKind::Identifier).is_some_and(|t| t == "global") {
444 ElementType::Global
445 } else {
446 ElementType::Error
447 },
448 &mut child_insertion_point,
449 is_legacy_syntax,
450 diag,
451 tr,
452 ),
453 child_insertion_point: RefCell::new(child_insertion_point),
454 ..Default::default()
455 };
456 let c = Rc::new(c);
457 let weak = Rc::downgrade(&c);
458 recurse_elem(&c.root_element, &(), &mut |e, _| {
459 e.borrow_mut().enclosing_component = weak.clone();
460 if let Some(qualified_id) =
461 e.borrow_mut().debug.first_mut().and_then(|x| x.qualified_id.as_mut())
462 {
463 *qualified_id = format_smolstr!("{}::{}", c.id, qualified_id);
464 }
465 });
466 c
467 }
468
469 pub fn is_global(&self) -> bool {
471 match &self.root_element.borrow().base_type {
472 ElementType::Global => true,
473 ElementType::Builtin(c) => c.is_global,
474 _ => false,
475 }
476 }
477
478 pub fn global_aliases(&self) -> Vec<SmolStr> {
481 self.exported_global_names
482 .borrow()
483 .iter()
484 .filter(|name| name.as_str() != self.root_element.borrow().id)
485 .map(|name| name.original_name())
486 .collect()
487 }
488
489 pub fn repeater_count(&self) -> u32 {
491 let mut count = 0;
492 recurse_elem(&self.root_element, &(), &mut |element, _| {
493 let element = element.borrow();
494 if let Some(sub_component) = element.sub_component() {
495 count += sub_component.repeater_count();
496 } else if element.repeated.is_some() {
497 count += 1;
498 }
499 });
500 count
501 }
502}
503
504#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
505pub enum PropertyVisibility {
506 #[default]
507 Private,
508 Input,
509 Output,
510 InOut,
511 Constexpr,
513 Fake,
516 Public,
518 Protected,
519}
520
521impl Display for PropertyVisibility {
522 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523 match self {
524 PropertyVisibility::Private => f.write_str("private"),
525 PropertyVisibility::Input => f.write_str("input"),
526 PropertyVisibility::Output => f.write_str("output"),
527 PropertyVisibility::InOut => f.write_str("input output"),
528 PropertyVisibility::Constexpr => f.write_str("constexpr"),
529 PropertyVisibility::Public => f.write_str("public"),
530 PropertyVisibility::Protected => f.write_str("protected"),
531 PropertyVisibility::Fake => f.write_str("fake"),
532 }
533 }
534}
535
536#[derive(Clone, Debug, Default)]
537pub struct PropertyDeclaration {
538 pub property_type: Type,
539 pub node: Option<SyntaxNode>,
540 pub expose_in_public_api: bool,
542 pub is_alias: Option<NamedReference>,
544 pub visibility: PropertyVisibility,
545 pub pure: Option<bool>,
547}
548
549impl PropertyDeclaration {
550 pub fn type_node(&self) -> Option<SyntaxNode> {
552 let node = self.node.as_ref()?;
553 if let Some(x) = syntax_nodes::PropertyDeclaration::new(node.clone()) {
554 Some(x.Type().map_or_else(|| x.into(), |x| x.into()))
555 } else {
556 node.clone().into()
557 }
558 }
559}
560
561impl From<Type> for PropertyDeclaration {
562 fn from(ty: Type) -> Self {
563 PropertyDeclaration { property_type: ty, ..Self::default() }
564 }
565}
566
567#[derive(Debug, Clone, Copy, PartialEq)]
568pub enum TransitionDirection {
569 In,
570 Out,
571 InOut,
572}
573
574#[derive(Debug, Clone)]
575pub struct TransitionPropertyAnimation {
576 pub state_id: i32,
578 pub direction: TransitionDirection,
580 pub animation: ElementRc,
582}
583
584impl TransitionPropertyAnimation {
585 pub fn condition(&self, state: Expression) -> Expression {
588 match self.direction {
589 TransitionDirection::In => Expression::BinaryExpression {
590 lhs: Box::new(Expression::StructFieldAccess {
591 base: Box::new(state),
592 name: "current-state".into(),
593 }),
594 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
595 op: '=',
596 },
597 TransitionDirection::Out => Expression::BinaryExpression {
598 lhs: Box::new(Expression::StructFieldAccess {
599 base: Box::new(state),
600 name: "previous-state".into(),
601 }),
602 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
603 op: '=',
604 },
605 TransitionDirection::InOut => Expression::BinaryExpression {
606 lhs: Box::new(Expression::BinaryExpression {
607 lhs: Box::new(Expression::StructFieldAccess {
608 base: Box::new(state.clone()),
609 name: "current-state".into(),
610 }),
611 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
612 op: '=',
613 }),
614 rhs: Box::new(Expression::BinaryExpression {
615 lhs: Box::new(Expression::StructFieldAccess {
616 base: Box::new(state),
617 name: "previous-state".into(),
618 }),
619 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
620 op: '=',
621 }),
622 op: '|',
623 },
624 }
625 }
626}
627
628#[derive(Debug)]
629pub enum PropertyAnimation {
630 Static(ElementRc),
631 Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
632}
633
634impl Clone for PropertyAnimation {
635 fn clone(&self) -> Self {
636 fn deep_clone(e: &ElementRc) -> ElementRc {
637 let e = e.borrow();
638 debug_assert!(e.children.is_empty());
639 debug_assert!(e.property_declarations.is_empty());
640 debug_assert!(e.states.is_empty() && e.transitions.is_empty());
641 Rc::new(RefCell::new(Element {
642 id: e.id.clone(),
643 base_type: e.base_type.clone(),
644 bindings: e.bindings.clone(),
645 property_analysis: e.property_analysis.clone(),
646 enclosing_component: e.enclosing_component.clone(),
647 repeated: None,
648 debug: e.debug.clone(),
649 ..Default::default()
650 }))
651 }
652 match self {
653 PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
654 PropertyAnimation::Transition { state_ref, animations } => {
655 PropertyAnimation::Transition {
656 state_ref: state_ref.clone(),
657 animations: animations
658 .iter()
659 .map(|t| TransitionPropertyAnimation {
660 state_id: t.state_id,
661 direction: t.direction,
662 animation: deep_clone(&t.animation),
663 })
664 .collect(),
665 }
666 }
667 }
668 }
669}
670
671#[derive(Default, Clone)]
673pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>);
674
675#[derive(Clone, Debug)]
676pub struct GeometryProps {
677 pub x: NamedReference,
678 pub y: NamedReference,
679 pub width: NamedReference,
680 pub height: NamedReference,
681}
682
683impl GeometryProps {
684 pub fn new(element: &ElementRc) -> Self {
685 Self {
686 x: NamedReference::new(element, SmolStr::new_static("x")),
687 y: NamedReference::new(element, SmolStr::new_static("y")),
688 width: NamedReference::new(element, SmolStr::new_static("width")),
689 height: NamedReference::new(element, SmolStr::new_static("height")),
690 }
691 }
692}
693
694pub type BindingsMap = BTreeMap<SmolStr, RefCell<BindingExpression>>;
695
696#[derive(Clone)]
697pub struct ElementDebugInfo {
698 pub qualified_id: Option<SmolStr>,
700 pub type_name: String,
701 pub element_hash: u64,
706 pub node: syntax_nodes::Element,
707 pub layout: Option<crate::layout::Layout>,
710 pub element_boundary: bool,
714}
715
716impl ElementDebugInfo {
717 fn encoded_element_info(&self) -> String {
720 let mut info = self.type_name.clone();
721 info.push(',');
722 if let Some(id) = self.qualified_id.as_ref() {
723 info.push_str(id);
724 }
725 info
726 }
727}
728
729#[derive(Default)]
731pub struct Element {
732 pub id: SmolStr,
738 pub base_type: ElementType,
740 pub bindings: BindingsMap,
742 pub change_callbacks: BTreeMap<SmolStr, RefCell<Vec<Expression>>>,
743 pub property_analysis: RefCell<HashMap<SmolStr, PropertyAnalysis>>,
744
745 pub children: Vec<ElementRc>,
746 pub enclosing_component: Weak<Component>,
748
749 pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
750
751 pub named_references: crate::namedreference::NamedReferenceContainer,
753
754 pub repeated: Option<RepeatedElementInfo>,
756 pub is_component_placeholder: bool,
758
759 pub states: Vec<State>,
760 pub transitions: Vec<Transition>,
761
762 pub child_of_layout: bool,
764 pub layout_info_prop: Option<(NamedReference, NamedReference)>,
766 pub default_fill_parent: (bool, bool),
768
769 pub accessibility_props: AccessibilityProps,
770
771 pub geometry_props: Option<GeometryProps>,
774
775 pub is_flickable_viewport: bool,
777
778 pub has_popup_child: bool,
781
782 pub item_index: OnceCell<u32>,
785 pub item_index_of_first_children: OnceCell<u32>,
787
788 pub is_legacy_syntax: bool,
790
791 pub inline_depth: i32,
793
794 pub debug: Vec<ElementDebugInfo>,
800}
801
802impl Spanned for Element {
803 fn span(&self) -> crate::diagnostics::Span {
804 self.debug.first().map(|n| n.node.span()).unwrap_or_default()
805 }
806
807 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
808 self.debug.first().map(|n| &n.node.source_file)
809 }
810}
811
812impl core::fmt::Debug for Element {
813 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
814 pretty_print(f, self, 0)
815 }
816}
817
818pub fn pretty_print(
819 f: &mut impl std::fmt::Write,
820 e: &Element,
821 indentation: usize,
822) -> std::fmt::Result {
823 if let Some(repeated) = &e.repeated {
824 write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
825 expression_tree::pretty_print(f, &repeated.model)?;
826 write!(f, ":")?;
827 if let ElementType::Component(base) = &e.base_type {
828 write!(f, "(base) ")?;
829 if base.parent_element.upgrade().is_some() {
830 pretty_print(f, &base.root_element.borrow(), indentation)?;
831 return Ok(());
832 }
833 }
834 }
835 if e.is_component_placeholder {
836 write!(f, "/* Component Placeholder */ ")?;
837 }
838 writeln!(f, "{} := {} {{ /* {} */", e.id, e.base_type, e.element_infos())?;
839 let mut indentation = indentation + 1;
840 macro_rules! indent {
841 () => {
842 for _ in 0..indentation {
843 write!(f, " ")?
844 }
845 };
846 }
847 for (name, ty) in &e.property_declarations {
848 indent!();
849 if let Some(alias) = &ty.is_alias {
850 writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
851 } else {
852 writeln!(f, "property<{}> {};", ty.property_type, name)?
853 }
854 }
855 for (name, expr) in &e.bindings {
856 let expr = expr.borrow();
857 indent!();
858 write!(f, "{name}: ")?;
859 expression_tree::pretty_print(f, &expr.expression)?;
860 if expr.analysis.as_ref().is_some_and(|a| a.is_const) {
861 write!(f, "/*const*/")?;
862 }
863 writeln!(f, ";")?;
864 if let Some(anim) = &expr.animation {
866 indent!();
867 writeln!(f, "animate {name} {anim:?}")?;
868 }
869 for nr in &expr.two_way_bindings {
870 indent!();
871 writeln!(f, "{name} <=> {nr:?};")?;
872 }
873 }
874 for (name, ch) in &e.change_callbacks {
875 for ex in &*ch.borrow() {
876 indent!();
877 write!(f, "changed {name} => ")?;
878 expression_tree::pretty_print(f, ex)?;
879 writeln!(f)?;
880 }
881 }
882 if !e.states.is_empty() {
883 indent!();
884 writeln!(f, "states {:?}", e.states)?;
885 }
886 if !e.transitions.is_empty() {
887 indent!();
888 writeln!(f, "transitions {:?} ", e.transitions)?;
889 }
890 for c in &e.children {
891 indent!();
892 pretty_print(f, &c.borrow(), indentation)?
893 }
894 if let Some(g) = &e.geometry_props {
895 indent!();
896 writeln!(f, "geometry {g:?} ")?;
897 }
898
899 indentation -= 1;
903 indent!();
904 writeln!(f, "}}")
905}
906
907#[derive(Clone, Default, Debug)]
908pub struct PropertyAnalysis {
909 pub is_set: bool,
911
912 pub is_set_externally: bool,
914
915 pub is_read: bool,
918
919 pub is_read_externally: bool,
921
922 pub is_linked_to_read_only: bool,
924
925 pub is_linked: bool,
927}
928
929impl PropertyAnalysis {
930 pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
935 self.is_set |= other.is_set;
936 self.is_read |= other.is_read;
937 }
938
939 pub fn merge(&mut self, other: &PropertyAnalysis) {
941 self.is_set |= other.is_set;
942 self.is_read |= other.is_read;
943 self.is_read_externally |= other.is_read_externally;
944 self.is_set_externally |= other.is_set_externally;
945 }
946
947 pub fn is_used(&self) -> bool {
949 self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
950 }
951}
952
953#[derive(Debug, Clone)]
954pub struct ListViewInfo {
955 pub viewport_y: NamedReference,
956 pub viewport_height: NamedReference,
957 pub viewport_width: NamedReference,
958 pub listview_height: NamedReference,
960 pub listview_width: NamedReference,
962}
963
964#[derive(Debug, Clone)]
965pub struct RepeatedElementInfo {
967 pub model: Expression,
968 pub model_data_id: SmolStr,
969 pub index_id: SmolStr,
970 pub is_conditional_element: bool,
974 pub is_listview: Option<ListViewInfo>,
976}
977
978pub type ElementRc = Rc<RefCell<Element>>;
979pub type ElementWeak = Weak<RefCell<Element>>;
980
981impl Element {
982 pub fn make_rc(self) -> ElementRc {
983 let r = ElementRc::new(RefCell::new(self));
984 let g = GeometryProps::new(&r);
985 r.borrow_mut().geometry_props = Some(g);
986 r
987 }
988
989 pub fn from_node(
990 node: syntax_nodes::Element,
991 id: SmolStr,
992 parent_type: ElementType,
993 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
994 is_legacy_syntax: bool,
995 diag: &mut BuildDiagnostics,
996 tr: &TypeRegister,
997 ) -> ElementRc {
998 let base_type = if let Some(base_node) = node.QualifiedName() {
999 let base = QualifiedTypeName::from_node(base_node.clone());
1000 let base_string = base.to_smolstr();
1001 match parent_type.lookup_type_for_child_element(&base_string, tr) {
1002 Ok(ElementType::Component(c)) if c.is_global() => {
1003 diag.push_error(
1004 "Cannot create an instance of a global component".into(),
1005 &base_node,
1006 );
1007 ElementType::Error
1008 }
1009 Ok(ty) => ty,
1010 Err(err) => {
1011 diag.push_error(err, &base_node);
1012 ElementType::Error
1013 }
1014 }
1015 } else if parent_type == ElementType::Global {
1016 let mut error_on = |node: &dyn Spanned, what: &str| {
1018 diag.push_error(format!("A global component cannot have {what}"), node);
1019 };
1020 node.SubElement().for_each(|n| error_on(&n, "sub elements"));
1021 node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
1022 if let Some(n) = node.ChildrenPlaceholder() {
1023 error_on(&n, "sub elements");
1024 }
1025 node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
1026 node.States().for_each(|n| error_on(&n, "states"));
1027 node.Transitions().for_each(|n| error_on(&n, "transitions"));
1028 node.CallbackDeclaration().for_each(|cb| {
1029 if parser::identifier_text(&cb.DeclaredIdentifier()).is_some_and(|s| s == "init") {
1030 error_on(&cb, "an 'init' callback")
1031 }
1032 });
1033 node.CallbackConnection().for_each(|cb| {
1034 if parser::identifier_text(&cb).is_some_and(|s| s == "init") {
1035 error_on(&cb, "an 'init' callback")
1036 }
1037 });
1038
1039 ElementType::Global
1040 } else if parent_type != ElementType::Error {
1041 assert!(diag.has_errors());
1043 return ElementRc::default();
1044 } else {
1045 tr.empty_type()
1046 };
1047 let qualified_id = (!id.is_empty()).then(|| id.clone());
1049 if let ElementType::Component(c) = &base_type {
1050 c.used.set(true);
1051 }
1052 let type_name = base_type
1053 .type_name()
1054 .filter(|_| base_type != tr.empty_type())
1055 .unwrap_or_default()
1056 .to_string();
1057 let mut r = Element {
1058 id,
1059 base_type,
1060 debug: vec![ElementDebugInfo {
1061 qualified_id,
1062 element_hash: 0,
1063 type_name,
1064 node: node.clone(),
1065 layout: None,
1066 element_boundary: false,
1067 }],
1068 is_legacy_syntax,
1069 ..Default::default()
1070 };
1071
1072 for prop_decl in node.PropertyDeclaration() {
1073 let prop_type = prop_decl
1074 .Type()
1075 .map(|type_node| type_from_node(type_node, diag, tr))
1076 .unwrap_or(Type::InferredProperty);
1078
1079 let unresolved_prop_name =
1080 unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
1081 let PropertyLookupResult {
1082 resolved_name: prop_name,
1083 property_type: maybe_existing_prop_type,
1084 ..
1085 } = r.lookup_property(&unresolved_prop_name);
1086 match maybe_existing_prop_type {
1087 Type::Callback { .. } => {
1088 diag.push_error(
1089 format!("Cannot declare property '{prop_name}' when a callback with the same name exists"),
1090 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1091 );
1092 continue;
1093 }
1094 Type::Function { .. } => {
1095 diag.push_error(
1096 format!("Cannot declare property '{prop_name}' when a function with the same name exists"),
1097 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1098 );
1099 continue;
1100 }
1101 Type::Invalid => {} _ => {
1103 diag.push_error(
1104 format!("Cannot override property '{unresolved_prop_name}'"),
1105 &prop_decl
1106 .DeclaredIdentifier()
1107 .child_token(SyntaxKind::Identifier)
1108 .unwrap(),
1109 );
1110 continue;
1111 }
1112 }
1113
1114 let mut visibility = None;
1115 for token in prop_decl.children_with_tokens() {
1116 if token.kind() != SyntaxKind::Identifier {
1117 continue;
1118 }
1119 match (token.as_token().unwrap().text(), visibility) {
1120 ("in", None) => visibility = Some(PropertyVisibility::Input),
1121 ("in", Some(_)) => diag.push_error("Extra 'in' keyword".into(), &token),
1122 ("out", None) => visibility = Some(PropertyVisibility::Output),
1123 ("out", Some(_)) => diag.push_error("Extra 'out' keyword".into(), &token),
1124 ("in-out" | "in_out", None) => visibility = Some(PropertyVisibility::InOut),
1125 ("in-out" | "in_out", Some(_)) => {
1126 diag.push_error("Extra 'in-out' keyword".into(), &token)
1127 }
1128 ("private", None) => visibility = Some(PropertyVisibility::Private),
1129 ("private", Some(_)) => {
1130 diag.push_error("Extra 'private' keyword".into(), &token)
1131 }
1132 _ => (),
1133 }
1134 }
1135 let visibility = visibility.unwrap_or({
1136 if is_legacy_syntax {
1137 PropertyVisibility::InOut
1138 } else {
1139 PropertyVisibility::Private
1140 }
1141 });
1142
1143 r.property_declarations.insert(
1144 prop_name.clone().into(),
1145 PropertyDeclaration {
1146 property_type: prop_type,
1147 node: Some(prop_decl.clone().into()),
1148 visibility,
1149 ..Default::default()
1150 },
1151 );
1152
1153 if let Some(csn) = prop_decl.BindingExpression() {
1154 match r.bindings.entry(prop_name.clone().into()) {
1155 Entry::Vacant(e) => {
1156 e.insert(BindingExpression::new_uncompiled(csn.into()).into());
1157 }
1158 Entry::Occupied(_) => {
1159 diag.push_error(
1160 "Duplicated property binding".into(),
1161 &prop_decl.DeclaredIdentifier(),
1162 );
1163 }
1164 }
1165 }
1166 if let Some(csn) = prop_decl.TwoWayBinding() {
1167 if r.bindings
1168 .insert(prop_name.into(), BindingExpression::new_uncompiled(csn.into()).into())
1169 .is_some()
1170 {
1171 diag.push_error(
1172 "Duplicated property binding".into(),
1173 &prop_decl.DeclaredIdentifier(),
1174 );
1175 }
1176 }
1177 }
1178
1179 r.parse_bindings(
1180 node.Binding().filter_map(|b| {
1181 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1182 }),
1183 is_legacy_syntax,
1184 diag,
1185 );
1186 r.parse_bindings(
1187 node.TwoWayBinding()
1188 .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
1189 is_legacy_syntax,
1190 diag,
1191 );
1192
1193 apply_default_type_properties(&mut r);
1194
1195 for sig_decl in node.CallbackDeclaration() {
1196 let name =
1197 unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
1198
1199 let pure = Some(
1200 sig_decl.child_token(SyntaxKind::Identifier).is_some_and(|t| t.text() == "pure"),
1201 );
1202
1203 let PropertyLookupResult {
1204 resolved_name: existing_name,
1205 property_type: maybe_existing_prop_type,
1206 ..
1207 } = r.lookup_property(&name);
1208 if !matches!(maybe_existing_prop_type, Type::Invalid) {
1209 if matches!(maybe_existing_prop_type, Type::Callback { .. }) {
1210 if r.property_declarations.contains_key(&name) {
1211 diag.push_error(
1212 "Duplicated callback declaration".into(),
1213 &sig_decl.DeclaredIdentifier(),
1214 );
1215 } else {
1216 diag.push_error(
1217 format!("Cannot override callback '{existing_name}'"),
1218 &sig_decl.DeclaredIdentifier(),
1219 )
1220 }
1221 } else {
1222 diag.push_error(
1223 format!(
1224 "Cannot declare callback '{existing_name}' when a {} with the same name exists",
1225 if matches!(maybe_existing_prop_type, Type::Function { .. }) { "function" } else { "property" }
1226 ),
1227 &sig_decl.DeclaredIdentifier(),
1228 );
1229 }
1230 continue;
1231 }
1232
1233 if let Some(csn) = sig_decl.TwoWayBinding() {
1234 r.bindings
1235 .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
1236 r.property_declarations.insert(
1237 name,
1238 PropertyDeclaration {
1239 property_type: Type::InferredCallback,
1240 node: Some(sig_decl.into()),
1241 visibility: PropertyVisibility::InOut,
1242 pure,
1243 ..Default::default()
1244 },
1245 );
1246 continue;
1247 }
1248
1249 let args = sig_decl
1250 .CallbackDeclarationParameter()
1251 .map(|p| type_from_node(p.Type(), diag, tr))
1252 .collect();
1253 let return_type = sig_decl
1254 .ReturnType()
1255 .map(|ret_ty| type_from_node(ret_ty.Type(), diag, tr))
1256 .unwrap_or(Type::Void);
1257 let arg_names = sig_decl
1258 .CallbackDeclarationParameter()
1259 .map(|a| {
1260 a.DeclaredIdentifier()
1261 .and_then(|x| parser::identifier_text(&x))
1262 .unwrap_or_default()
1263 })
1264 .collect();
1265 r.property_declarations.insert(
1266 name,
1267 PropertyDeclaration {
1268 property_type: Type::Callback(Rc::new(Function {
1269 return_type,
1270 args,
1271 arg_names,
1272 })),
1273 node: Some(sig_decl.into()),
1274 visibility: PropertyVisibility::InOut,
1275 pure,
1276 ..Default::default()
1277 },
1278 );
1279 }
1280
1281 for func in node.Function() {
1282 let name =
1283 unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag);
1284
1285 let PropertyLookupResult {
1286 resolved_name: existing_name,
1287 property_type: maybe_existing_prop_type,
1288 ..
1289 } = r.lookup_property(&name);
1290 if !matches!(maybe_existing_prop_type, Type::Invalid) {
1291 if matches!(maybe_existing_prop_type, Type::Callback { .. } | Type::Function { .. })
1292 {
1293 diag.push_error(
1294 format!("Cannot override '{existing_name}'"),
1295 &func.DeclaredIdentifier(),
1296 )
1297 } else {
1298 diag.push_error(
1299 format!("Cannot declare function '{existing_name}' when a property with the same name exists"),
1300 &func.DeclaredIdentifier(),
1301 );
1302 }
1303 continue;
1304 }
1305
1306 let mut args = vec![];
1307 let mut arg_names = vec![];
1308 for a in func.ArgumentDeclaration() {
1309 args.push(type_from_node(a.Type(), diag, tr));
1310 let name =
1311 unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag);
1312 if arg_names.contains(&name) {
1313 diag.push_error(
1314 format!("Duplicated argument name '{name}'"),
1315 &a.DeclaredIdentifier(),
1316 );
1317 }
1318 arg_names.push(name);
1319 }
1320 let return_type = func
1321 .ReturnType()
1322 .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr));
1323 if r.bindings
1324 .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into())
1325 .is_some()
1326 {
1327 assert!(diag.has_errors());
1328 }
1329
1330 let mut visibility = PropertyVisibility::Private;
1331 let mut pure = None;
1332 for token in func.children_with_tokens() {
1333 if token.kind() != SyntaxKind::Identifier {
1334 continue;
1335 }
1336 match token.as_token().unwrap().text() {
1337 "pure" => pure = Some(true),
1338 "public" => {
1339 visibility = PropertyVisibility::Public;
1340 pure = pure.or(Some(false));
1341 }
1342 "protected" => {
1343 visibility = PropertyVisibility::Protected;
1344 pure = pure.or(Some(false));
1345 }
1346 _ => (),
1347 }
1348 }
1349
1350 r.property_declarations.insert(
1351 name,
1352 PropertyDeclaration {
1353 property_type: Type::Function(Rc::new(Function {
1354 return_type,
1355 args,
1356 arg_names,
1357 })),
1358 node: Some(func.into()),
1359 visibility,
1360 pure,
1361 ..Default::default()
1362 },
1363 );
1364 }
1365
1366 for con_node in node.CallbackConnection() {
1367 let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
1368 let PropertyLookupResult { resolved_name, property_type, .. } =
1369 r.lookup_property(&unresolved_name);
1370 if let Type::Callback(callback) = &property_type {
1371 let num_arg = con_node.DeclaredIdentifier().count();
1372 if num_arg > callback.args.len() {
1373 diag.push_error(
1374 format!(
1375 "'{}' only has {} arguments, but {} were provided",
1376 unresolved_name,
1377 callback.args.len(),
1378 num_arg
1379 ),
1380 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1381 );
1382 }
1383 } else if property_type == Type::InferredCallback {
1384 } else {
1386 if r.base_type != ElementType::Error {
1387 diag.push_error(
1388 format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
1389 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1390 );
1391 }
1392 continue;
1393 }
1394 match r.bindings.entry(resolved_name.into()) {
1395 Entry::Vacant(e) => {
1396 e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into());
1397 }
1398 Entry::Occupied(_) => diag.push_error(
1399 "Duplicated callback".into(),
1400 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1401 ),
1402 }
1403 }
1404
1405 for anim in node.PropertyAnimation() {
1406 if let Some(star) = anim.child_token(SyntaxKind::Star) {
1407 diag.push_error(
1408 "catch-all property is only allowed within transitions".into(),
1409 &star,
1410 )
1411 };
1412 for prop_name_token in anim.QualifiedName() {
1413 match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
1414 [unresolved_prop_name] => {
1415 if r.base_type == ElementType::Error {
1416 continue;
1417 };
1418 let lookup_result = r.lookup_property(unresolved_prop_name);
1419 let valid_assign = lookup_result.is_valid_for_assignment();
1420 if let Some(anim_element) = animation_element_from_node(
1421 &anim,
1422 &prop_name_token,
1423 lookup_result.property_type,
1424 diag,
1425 tr,
1426 ) {
1427 if !valid_assign {
1428 diag.push_error(
1429 format!(
1430 "Cannot animate {} property '{}'",
1431 lookup_result.property_visibility, unresolved_prop_name
1432 ),
1433 &prop_name_token,
1434 );
1435 }
1436
1437 if unresolved_prop_name != lookup_result.resolved_name.as_ref() {
1438 diag.push_property_deprecation_warning(
1439 unresolved_prop_name,
1440 &lookup_result.resolved_name,
1441 &prop_name_token,
1442 );
1443 }
1444
1445 let expr_binding = r
1446 .bindings
1447 .entry(lookup_result.resolved_name.into())
1448 .or_insert_with(|| {
1449 let mut r = BindingExpression::from(Expression::Invalid);
1450 r.priority = 1;
1451 r.span = Some(prop_name_token.to_source_location());
1452 r.into()
1453 });
1454 if expr_binding
1455 .get_mut()
1456 .animation
1457 .replace(PropertyAnimation::Static(anim_element))
1458 .is_some()
1459 {
1460 diag.push_error("Duplicated animation".into(), &prop_name_token)
1461 }
1462 }
1463 }
1464 _ => diag.push_error(
1465 "Can only refer to property in the current element".into(),
1466 &prop_name_token,
1467 ),
1468 }
1469 }
1470 }
1471
1472 for ch in node.PropertyChangedCallback() {
1473 let Some(prop) = parser::identifier_text(&ch.DeclaredIdentifier()) else { continue };
1474 let lookup_result = r.lookup_property(&prop);
1475 if !lookup_result.is_valid() {
1476 if r.base_type != ElementType::Error {
1477 diag.push_error(
1478 format!("Property '{prop}' does not exist"),
1479 &ch.DeclaredIdentifier(),
1480 );
1481 }
1482 } else if !lookup_result.property_type.is_property_type() {
1483 let what = match lookup_result.property_type {
1484 Type::Function { .. } => "a function",
1485 Type::Callback { .. } => "a callback",
1486 _ => "not a property",
1487 };
1488 diag.push_error(
1489 format!(
1490 "Change callback can only be set on properties, and '{prop}' is {what}"
1491 ),
1492 &ch.DeclaredIdentifier(),
1493 );
1494 } else if lookup_result.property_visibility == PropertyVisibility::Private
1495 && !lookup_result.is_local_to_component
1496 {
1497 diag.push_error(
1498 format!("Change callback on a private property '{prop}'"),
1499 &ch.DeclaredIdentifier(),
1500 );
1501 }
1502 let handler = Expression::Uncompiled(ch.clone().into());
1503 match r.change_callbacks.entry(prop) {
1504 Entry::Vacant(e) => {
1505 e.insert(vec![handler].into());
1506 }
1507 Entry::Occupied(mut e) => {
1508 diag.push_error(
1509 format!("Duplicated change callback on '{}'", e.key()),
1510 &ch.DeclaredIdentifier(),
1511 );
1512 e.get_mut().get_mut().push(handler);
1513 }
1514 }
1515 }
1516
1517 let mut children_placeholder = None;
1518 let r = r.make_rc();
1519
1520 for se in node.children() {
1521 if se.kind() == SyntaxKind::SubElement {
1522 let parent_type = r.borrow().base_type.clone();
1523 r.borrow_mut().children.push(Element::from_sub_element_node(
1524 se.into(),
1525 parent_type,
1526 component_child_insertion_point,
1527 is_legacy_syntax,
1528 diag,
1529 tr,
1530 ));
1531 } else if se.kind() == SyntaxKind::RepeatedElement {
1532 let mut sub_child_insertion_point = None;
1533 let rep = Element::from_repeated_node(
1534 se.into(),
1535 &r,
1536 &mut sub_child_insertion_point,
1537 is_legacy_syntax,
1538 diag,
1539 tr,
1540 );
1541 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1542 diag.push_error(
1543 "The @children placeholder cannot appear in a repeated element".into(),
1544 &se,
1545 )
1546 }
1547 r.borrow_mut().children.push(rep);
1548 } else if se.kind() == SyntaxKind::ConditionalElement {
1549 let mut sub_child_insertion_point = None;
1550 let rep = Element::from_conditional_node(
1551 se.into(),
1552 r.borrow().base_type.clone(),
1553 &mut sub_child_insertion_point,
1554 is_legacy_syntax,
1555 diag,
1556 tr,
1557 );
1558 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1559 diag.push_error(
1560 "The @children placeholder cannot appear in a conditional element".into(),
1561 &se,
1562 )
1563 }
1564 r.borrow_mut().children.push(rep);
1565 } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
1566 if children_placeholder.is_some() {
1567 diag.push_error(
1568 "The @children placeholder can only appear once in an element".into(),
1569 &se,
1570 )
1571 } else {
1572 children_placeholder = Some((se.clone().into(), r.borrow().children.len()));
1573 }
1574 }
1575 }
1576
1577 if let Some((children_placeholder, index)) = children_placeholder {
1578 if component_child_insertion_point.is_some() {
1579 diag.push_error(
1580 "The @children placeholder can only appear once in an element hierarchy".into(),
1581 &children_placeholder,
1582 )
1583 } else {
1584 *component_child_insertion_point = Some(ChildrenInsertionPoint {
1585 parent: r.clone(),
1586 insertion_index: index,
1587 node: children_placeholder,
1588 });
1589 }
1590 }
1591
1592 for state in node.States().flat_map(|s| s.State()) {
1593 let s = State {
1594 id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
1595 condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
1596 property_changes: state
1597 .StatePropertyChange()
1598 .filter_map(|s| {
1599 lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag)
1600 .map(|(ne, ty)| {
1601 if !ty.is_property_type() && !matches!(ty, Type::Invalid) {
1602 diag.push_error(
1603 format!("'{}' is not a property", **s.QualifiedName()),
1604 &s,
1605 );
1606 }
1607 (ne, Expression::Uncompiled(s.BindingExpression().into()), s)
1608 })
1609 })
1610 .collect(),
1611 };
1612 for trs in state.Transition() {
1613 let mut t = Transition::from_node(trs, &r, tr, diag);
1614 t.state_id.clone_from(&s.id);
1615 r.borrow_mut().transitions.push(t);
1616 }
1617 r.borrow_mut().states.push(s);
1618 }
1619
1620 for ts in node.Transitions() {
1621 if !is_legacy_syntax {
1622 diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition".into(), &ts);
1623 }
1624 for trs in ts.Transition() {
1625 let trans = Transition::from_node(trs, &r, tr, diag);
1626 r.borrow_mut().transitions.push(trans);
1627 }
1628 }
1629
1630 if r.borrow().base_type.to_smolstr() == "ListView" {
1631 let mut seen_for = false;
1632 for se in node.children() {
1633 if se.kind() == SyntaxKind::RepeatedElement && !seen_for {
1634 seen_for = true;
1635 } else if matches!(
1636 se.kind(),
1637 SyntaxKind::SubElement
1638 | SyntaxKind::ConditionalElement
1639 | SyntaxKind::RepeatedElement
1640 | SyntaxKind::ChildrenPlaceholder
1641 ) {
1642 diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported".into(), &se)
1643 }
1644 }
1645 }
1646
1647 r
1648 }
1649
1650 fn from_sub_element_node(
1651 node: syntax_nodes::SubElement,
1652 parent_type: ElementType,
1653 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1654 is_in_legacy_component: bool,
1655 diag: &mut BuildDiagnostics,
1656 tr: &TypeRegister,
1657 ) -> ElementRc {
1658 let mut id = parser::identifier_text(&node).unwrap_or_default();
1659 if matches!(id.as_ref(), "parent" | "self" | "root") {
1660 diag.push_error(
1661 format!("'{id}' is a reserved id"),
1662 &node.child_token(SyntaxKind::Identifier).unwrap(),
1663 );
1664 id = SmolStr::default();
1665 }
1666 Element::from_node(
1667 node.Element(),
1668 id,
1669 parent_type,
1670 component_child_insertion_point,
1671 is_in_legacy_component,
1672 diag,
1673 tr,
1674 )
1675 }
1676
1677 fn from_repeated_node(
1678 node: syntax_nodes::RepeatedElement,
1679 parent: &ElementRc,
1680 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1681 is_in_legacy_component: bool,
1682 diag: &mut BuildDiagnostics,
1683 tr: &TypeRegister,
1684 ) -> ElementRc {
1685 let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
1686 Some(ListViewInfo {
1687 viewport_y: NamedReference::new(parent, SmolStr::new_static("viewport-y")),
1688 viewport_height: NamedReference::new(
1689 parent,
1690 SmolStr::new_static("viewport-height"),
1691 ),
1692 viewport_width: NamedReference::new(parent, SmolStr::new_static("viewport-width")),
1693 listview_height: NamedReference::new(parent, SmolStr::new_static("visible-height")),
1694 listview_width: NamedReference::new(parent, SmolStr::new_static("visible-width")),
1695 })
1696 } else {
1697 None
1698 };
1699 let rei = RepeatedElementInfo {
1700 model: Expression::Uncompiled(node.Expression().into()),
1701 model_data_id: node
1702 .DeclaredIdentifier()
1703 .and_then(|n| parser::identifier_text(&n))
1704 .unwrap_or_default(),
1705 index_id: node
1706 .RepeatedIndex()
1707 .and_then(|r| parser::identifier_text(&r))
1708 .unwrap_or_default(),
1709 is_conditional_element: false,
1710 is_listview,
1711 };
1712 let e = Element::from_sub_element_node(
1713 node.SubElement(),
1714 parent.borrow().base_type.clone(),
1715 component_child_insertion_point,
1716 is_in_legacy_component,
1717 diag,
1718 tr,
1719 );
1720 e.borrow_mut().repeated = Some(rei);
1721 e
1722 }
1723
1724 fn from_conditional_node(
1725 node: syntax_nodes::ConditionalElement,
1726 parent_type: ElementType,
1727 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1728 is_in_legacy_component: bool,
1729 diag: &mut BuildDiagnostics,
1730 tr: &TypeRegister,
1731 ) -> ElementRc {
1732 let rei = RepeatedElementInfo {
1733 model: Expression::Uncompiled(node.Expression().into()),
1734 model_data_id: SmolStr::default(),
1735 index_id: SmolStr::default(),
1736 is_conditional_element: true,
1737 is_listview: None,
1738 };
1739 let e = Element::from_sub_element_node(
1740 node.SubElement(),
1741 parent_type,
1742 component_child_insertion_point,
1743 is_in_legacy_component,
1744 diag,
1745 tr,
1746 );
1747 e.borrow_mut().repeated = Some(rei);
1748 e
1749 }
1750
1751 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
1755 self.property_declarations.get(name).map_or_else(
1756 || {
1757 let mut r = self.base_type.lookup_property(name);
1758 r.is_in_direct_base = r.is_local_to_component;
1759 r.is_local_to_component = false;
1760 r
1761 },
1762 |p| PropertyLookupResult {
1763 resolved_name: name.into(),
1764 property_type: p.property_type.clone(),
1765 property_visibility: p.visibility,
1766 declared_pure: p.pure,
1767 is_local_to_component: true,
1768 is_in_direct_base: false,
1769 builtin_function: None,
1770 },
1771 )
1772 }
1773
1774 fn parse_bindings(
1775 &mut self,
1776 bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
1777 is_in_legacy_component: bool,
1778 diag: &mut BuildDiagnostics,
1779 ) {
1780 for (name_token, b) in bindings {
1781 let unresolved_name = crate::parser::normalize_identifier(name_token.text());
1782 let lookup_result = self.lookup_property(&unresolved_name);
1783 if !lookup_result.property_type.is_property_type() {
1784 match lookup_result.property_type {
1785 Type::Invalid => {
1786 if self.base_type != ElementType::Error {
1787 diag.push_error(if self.base_type.to_smolstr() == "Empty" {
1788 format!( "Unknown property {unresolved_name}")
1789 } else {
1790 format!( "Unknown property {unresolved_name} in {}", self.base_type)
1791 },
1792 &name_token);
1793 }
1794 }
1795 Type::Callback { .. } => {
1796 diag.push_error(format!("'{unresolved_name}' is a callback. Use `=>` to connect"),
1797 &name_token)
1798 }
1799 _ => diag.push_error(format!(
1800 "Cannot assign to {} in {} because it does not have a valid property type",
1801 unresolved_name, self.base_type,
1802 ),
1803 &name_token),
1804 }
1805 } else if !lookup_result.is_local_to_component
1806 && (lookup_result.property_visibility == PropertyVisibility::Private
1807 || lookup_result.property_visibility == PropertyVisibility::Output)
1808 {
1809 if is_in_legacy_component
1810 && lookup_result.property_visibility == PropertyVisibility::Output
1811 {
1812 diag.push_warning(
1813 format!("Assigning to output property '{unresolved_name}' is deprecated"),
1814 &name_token,
1815 );
1816 } else {
1817 diag.push_error(
1818 format!(
1819 "Cannot assign to {} property '{}'",
1820 lookup_result.property_visibility, unresolved_name
1821 ),
1822 &name_token,
1823 );
1824 }
1825 }
1826
1827 if *lookup_result.resolved_name != *unresolved_name {
1828 diag.push_property_deprecation_warning(
1829 &unresolved_name,
1830 &lookup_result.resolved_name,
1831 &name_token,
1832 );
1833 }
1834
1835 match self.bindings.entry(lookup_result.resolved_name.into()) {
1836 Entry::Occupied(_) => {
1837 diag.push_error("Duplicated property binding".into(), &name_token);
1838 }
1839 Entry::Vacant(entry) => {
1840 entry.insert(BindingExpression::new_uncompiled(b).into());
1841 }
1842 };
1843 }
1844 }
1845
1846 pub fn native_class(&self) -> Option<Rc<NativeClass>> {
1847 let mut base_type = self.base_type.clone();
1848 loop {
1849 match &base_type {
1850 ElementType::Component(component) => {
1851 base_type = component.root_element.clone().borrow().base_type.clone();
1852 }
1853 ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()),
1854 ElementType::Native(native) => break Some(native.clone()),
1855 _ => break None,
1856 }
1857 }
1858 }
1859
1860 pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
1861 let mut base_type = self.base_type.clone();
1862 loop {
1863 match &base_type {
1864 ElementType::Component(component) => {
1865 base_type = component.root_element.clone().borrow().base_type.clone();
1866 }
1867 ElementType::Builtin(builtin) => break Some(builtin.clone()),
1868 _ => break None,
1869 }
1870 }
1871 }
1872
1873 pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> {
1874 self.layout_info_prop.as_ref().map(|prop| match orientation {
1875 Orientation::Horizontal => &prop.0,
1876 Orientation::Vertical => &prop.1,
1877 })
1878 }
1879
1880 pub fn original_name(&self) -> SmolStr {
1882 self.debug
1883 .first()
1884 .and_then(|n| n.node.child_token(parser::SyntaxKind::Identifier))
1885 .map(|n| n.to_smolstr())
1886 .unwrap_or_else(|| self.id.clone())
1887 }
1888
1889 pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
1894 if self.bindings.get(property_name).is_some_and(|b| {
1895 b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0)
1896 }) {
1897 true
1898 } else if let ElementType::Component(base) = &self.base_type {
1899 base.root_element.borrow().is_binding_set(property_name, need_explicit)
1900 } else {
1901 false
1902 }
1903 }
1904
1905 pub fn set_binding_if_not_set(
1910 &mut self,
1911 property_name: SmolStr,
1912 expression_fn: impl FnOnce() -> Expression,
1913 ) -> bool {
1914 if self.is_binding_set(&property_name, false) {
1915 return false;
1916 }
1917
1918 match self.bindings.entry(property_name) {
1919 Entry::Vacant(vacant_entry) => {
1920 let mut binding: BindingExpression = expression_fn().into();
1921 binding.priority = i32::MAX;
1922 vacant_entry.insert(binding.into());
1923 }
1924 Entry::Occupied(mut existing_entry) => {
1925 let mut binding: BindingExpression = expression_fn().into();
1926 binding.priority = i32::MAX;
1927 existing_entry.get_mut().get_mut().merge_with(&binding);
1928 }
1929 };
1930 true
1931 }
1932
1933 pub fn sub_component(&self) -> Option<&Rc<Component>> {
1934 if self.repeated.is_some() {
1935 None
1936 } else if let ElementType::Component(sub_component) = &self.base_type {
1937 Some(sub_component)
1938 } else {
1939 None
1940 }
1941 }
1942
1943 pub fn element_infos(&self) -> String {
1944 let mut debug_infos = self.debug.clone();
1945 let mut base = self.base_type.clone();
1946 while let ElementType::Component(b) = base {
1947 let elem = b.root_element.borrow();
1948 base = elem.base_type.clone();
1949 debug_infos.extend(elem.debug.iter().cloned());
1950 }
1951
1952 let (infos, _, _) = debug_infos.into_iter().fold(
1953 (String::new(), false, true),
1954 |(mut infos, elem_boundary, first), debug_info| {
1955 if elem_boundary {
1956 infos.push('/');
1957 } else if !first {
1958 infos.push(';');
1959 }
1960
1961 infos.push_str(&debug_info.encoded_element_info());
1962 (infos, debug_info.element_boundary, false)
1963 },
1964 );
1965 infos
1966 }
1967}
1968
1969fn apply_default_type_properties(element: &mut Element) {
1971 if let ElementType::Builtin(builtin_base) = &element.base_type {
1973 for (prop, info) in &builtin_base.properties {
1974 if let BuiltinPropertyDefault::Expr(expr) = &info.default_value {
1975 element.bindings.entry(prop.clone()).or_insert_with(|| {
1976 let mut binding = BindingExpression::from(expr.clone());
1977 binding.priority = i32::MAX;
1978 RefCell::new(binding)
1979 });
1980 }
1981 }
1982 }
1983}
1984
1985pub fn type_from_node(
1987 node: syntax_nodes::Type,
1988 diag: &mut BuildDiagnostics,
1989 tr: &TypeRegister,
1990) -> Type {
1991 if let Some(qualified_type_node) = node.QualifiedName() {
1992 let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
1993
1994 let prop_type = tr.lookup_qualified(&qualified_type.members);
1995
1996 if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_smolstr()).is_err() {
1997 diag.push_error(format!("Unknown type '{qualified_type}'"), &qualified_type_node);
1998 } else if !prop_type.is_property_type() {
1999 diag.push_error(
2000 format!("'{qualified_type}' is not a valid type"),
2001 &qualified_type_node,
2002 );
2003 }
2004 prop_type
2005 } else if let Some(object_node) = node.ObjectType() {
2006 type_struct_from_node(object_node, diag, tr, None, None)
2007 } else if let Some(array_node) = node.ArrayType() {
2008 Type::Array(Rc::new(type_from_node(array_node.Type(), diag, tr)))
2009 } else {
2010 assert!(diag.has_errors());
2011 Type::Invalid
2012 }
2013}
2014
2015pub fn type_struct_from_node(
2017 object_node: syntax_nodes::ObjectType,
2018 diag: &mut BuildDiagnostics,
2019 tr: &TypeRegister,
2020 rust_attributes: Option<Vec<SmolStr>>,
2021 name: Option<SmolStr>,
2022) -> Type {
2023 let fields = object_node
2024 .ObjectTypeMember()
2025 .map(|member| {
2026 (
2027 parser::identifier_text(&member).unwrap_or_default(),
2028 type_from_node(member.Type(), diag, tr),
2029 )
2030 })
2031 .collect();
2032 Type::Struct(Rc::new(Struct { fields, name, node: Some(object_node), rust_attributes }))
2033}
2034
2035fn animation_element_from_node(
2036 anim: &syntax_nodes::PropertyAnimation,
2037 prop_name: &syntax_nodes::QualifiedName,
2038 prop_type: Type,
2039 diag: &mut BuildDiagnostics,
2040 tr: &TypeRegister,
2041) -> Option<ElementRc> {
2042 let anim_type = tr.property_animation_type_for_property(prop_type);
2043 if !matches!(anim_type, ElementType::Builtin(..)) {
2044 diag.push_error(
2045 format!(
2046 "'{}' is not a property that can be animated",
2047 prop_name.text().to_string().trim()
2048 ),
2049 prop_name,
2050 );
2051 None
2052 } else {
2053 let mut anim_element =
2054 Element { id: "".into(), base_type: anim_type, ..Default::default() };
2055 anim_element.parse_bindings(
2056 anim.Binding().filter_map(|b| {
2057 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
2058 }),
2059 false,
2060 diag,
2061 );
2062
2063 apply_default_type_properties(&mut anim_element);
2064
2065 Some(Rc::new(RefCell::new(anim_element)))
2066 }
2067}
2068
2069#[derive(Default, Debug, Clone)]
2070pub struct QualifiedTypeName {
2071 pub members: Vec<SmolStr>,
2072}
2073
2074impl QualifiedTypeName {
2075 pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
2076 debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
2077 let members = node
2078 .children_with_tokens()
2079 .filter(|n| n.kind() == SyntaxKind::Identifier)
2080 .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
2081 .collect();
2082 Self { members }
2083 }
2084}
2085
2086impl Display for QualifiedTypeName {
2087 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2088 write!(f, "{}", self.members.join("."))
2089 }
2090}
2091
2092fn lookup_property_from_qualified_name_for_state(
2095 node: syntax_nodes::QualifiedName,
2096 r: &ElementRc,
2097 diag: &mut BuildDiagnostics,
2098) -> Option<(NamedReference, Type)> {
2099 let qualname = QualifiedTypeName::from_node(node.clone());
2100 match qualname.members.as_slice() {
2101 [unresolved_prop_name] => {
2102 let lookup_result = r.borrow().lookup_property(unresolved_prop_name.as_ref());
2103 if !lookup_result.property_type.is_property_type() {
2104 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2105 } else if !lookup_result.is_valid_for_assignment() {
2106 diag.push_error(
2107 format!(
2108 "'{}' cannot be set in a state because it is {}",
2109 qualname, lookup_result.property_visibility
2110 ),
2111 &node,
2112 );
2113 }
2114 Some((
2115 NamedReference::new(r, lookup_result.resolved_name.to_smolstr()),
2116 lookup_result.property_type,
2117 ))
2118 }
2119 [elem_id, unresolved_prop_name] => {
2120 if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
2121 let lookup_result = element.borrow().lookup_property(unresolved_prop_name.as_ref());
2122 if !lookup_result.is_valid() {
2123 diag.push_error(
2124 format!("'{unresolved_prop_name}' not found in '{elem_id}'"),
2125 &node,
2126 );
2127 } else if !lookup_result.is_valid_for_assignment() {
2128 diag.push_error(
2129 format!(
2130 "'{}' cannot be set in a state because it is {}",
2131 qualname, lookup_result.property_visibility
2132 ),
2133 &node,
2134 );
2135 }
2136 Some((
2137 NamedReference::new(&element, lookup_result.resolved_name.to_smolstr()),
2138 lookup_result.property_type,
2139 ))
2140 } else {
2141 diag.push_error(format!("'{elem_id}' is not a valid element id"), &node);
2142 None
2143 }
2144 }
2145 _ => {
2146 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2147 None
2148 }
2149 }
2150}
2151
2152fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
2154 if e.borrow().id == name {
2155 return Some(e.clone());
2156 }
2157 for x in &e.borrow().children {
2158 if x.borrow().repeated.is_some() {
2159 continue;
2160 }
2161 if let Some(x) = find_element_by_id(x, name) {
2162 return Some(x);
2163 }
2164 }
2165
2166 None
2167}
2168
2169pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
2172 fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
2173 for child in &base.borrow().children {
2174 if Rc::ptr_eq(child, e) {
2175 return Some(base.clone());
2176 }
2177 if let Some(x) = recurse(child, e) {
2178 return Some(x);
2179 }
2180 }
2181 None
2182 }
2183
2184 let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
2185 if Rc::ptr_eq(&root, e) {
2186 return None;
2187 }
2188 recurse(&root, e)
2189}
2190
2191pub fn recurse_elem<State>(
2195 elem: &ElementRc,
2196 state: &State,
2197 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2198) {
2199 let state = vis(elem, state);
2200 for sub in &elem.borrow().children {
2201 recurse_elem(sub, &state, vis);
2202 }
2203}
2204
2205pub fn recurse_elem_including_sub_components<State>(
2207 component: &Component,
2208 state: &State,
2209 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2210) {
2211 recurse_elem(&component.root_element, state, &mut |elem, state| {
2212 debug_assert!(std::ptr::eq(
2213 component as *const Component,
2214 (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
2215 ));
2216 if elem.borrow().repeated.is_some() {
2217 if let ElementType::Component(base) = &elem.borrow().base_type {
2218 if base.parent_element.upgrade().is_some() {
2219 recurse_elem_including_sub_components(base, state, vis);
2220 }
2221 }
2222 }
2223 vis(elem, state)
2224 });
2225 component
2226 .popup_windows
2227 .borrow()
2228 .iter()
2229 .for_each(|p| recurse_elem_including_sub_components(&p.component, state, vis));
2230 component
2231 .menu_item_tree
2232 .borrow()
2233 .iter()
2234 .for_each(|c| recurse_elem_including_sub_components(c, state, vis));
2235}
2236
2237pub fn recurse_elem_no_borrow<State>(
2239 elem: &ElementRc,
2240 state: &State,
2241 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2242) {
2243 let state = vis(elem, state);
2244 let children = elem.borrow().children.clone();
2245 for sub in &children {
2246 recurse_elem_no_borrow(sub, &state, vis);
2247 }
2248}
2249
2250pub fn recurse_elem_including_sub_components_no_borrow<State>(
2252 component: &Component,
2253 state: &State,
2254 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2255) {
2256 recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| {
2257 let base = if elem.borrow().repeated.is_some() {
2258 if let ElementType::Component(base) = &elem.borrow().base_type {
2259 if base.parent_element.upgrade().is_some() {
2260 Some(base.clone())
2261 } else {
2262 None
2264 }
2265 } else {
2266 None
2267 }
2268 } else {
2269 None
2270 };
2271 if let Some(base) = base {
2272 recurse_elem_including_sub_components_no_borrow(&base, state, vis);
2273 }
2274 vis(elem, state)
2275 });
2276 component
2277 .popup_windows
2278 .borrow()
2279 .iter()
2280 .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis));
2281 component
2282 .menu_item_tree
2283 .borrow()
2284 .iter()
2285 .for_each(|c| recurse_elem_including_sub_components_no_borrow(c, state, vis));
2286}
2287
2288pub fn visit_element_expressions(
2294 elem: &ElementRc,
2295 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2296) {
2297 fn visit_element_expressions_simple(
2298 elem: &ElementRc,
2299 vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2300 ) {
2301 for (name, expr) in &elem.borrow().bindings {
2302 vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| {
2303 elem.borrow().lookup_property(name).property_type
2304 });
2305
2306 match &mut expr.borrow_mut().animation {
2307 Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
2308 Some(PropertyAnimation::Transition { animations, state_ref }) => {
2309 vis(state_ref, None, &|| Type::Int32);
2310 for a in animations {
2311 visit_element_expressions_simple(&a.animation, vis)
2312 }
2313 }
2314 None => (),
2315 }
2316 }
2317 }
2318
2319 let repeated = elem
2320 .borrow_mut()
2321 .repeated
2322 .as_mut()
2323 .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element));
2324 if let Some((mut model, is_cond)) = repeated {
2325 vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model });
2326 elem.borrow_mut().repeated.as_mut().unwrap().model = model;
2327 }
2328 visit_element_expressions_simple(elem, &mut vis);
2329
2330 for expr in elem.borrow().change_callbacks.values() {
2331 for expr in expr.borrow_mut().iter_mut() {
2332 vis(expr, Some("$change callback$"), &|| Type::Void);
2333 }
2334 }
2335
2336 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2337 for s in &mut states {
2338 if let Some(cond) = s.condition.as_mut() {
2339 vis(cond, None, &|| Type::Bool)
2340 }
2341 for (ne, e, _) in &mut s.property_changes {
2342 vis(e, Some(ne.name()), &|| {
2343 ne.element().borrow().lookup_property(ne.name()).property_type
2344 });
2345 }
2346 }
2347 elem.borrow_mut().states = states;
2348
2349 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2350 for t in &mut transitions {
2351 for (_, _, a) in &mut t.property_animations {
2352 visit_element_expressions_simple(a, &mut vis);
2353 }
2354 }
2355 elem.borrow_mut().transitions = transitions;
2356
2357 let component = elem.borrow().enclosing_component.upgrade().unwrap();
2358 if Rc::ptr_eq(&component.root_element, elem) {
2359 for e in component.init_code.borrow_mut().iter_mut() {
2360 vis(e, None, &|| Type::Void);
2361 }
2362 }
2363}
2364
2365pub fn visit_named_references_in_expression(
2366 expr: &mut Expression,
2367 vis: &mut impl FnMut(&mut NamedReference),
2368) {
2369 expr.visit_mut(|sub| visit_named_references_in_expression(sub, vis));
2370 match expr {
2371 Expression::PropertyReference(r) => vis(r),
2372 Expression::FunctionCall {
2373 function: Callable::Callback(r) | Callable::Function(r),
2374 ..
2375 } => vis(r),
2376 Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
2377 Expression::SolveLayout(l, _) => l.visit_named_references(vis),
2378 Expression::ComputeLayoutInfo(l, _) => l.visit_named_references(vis),
2379 Expression::RepeaterModelReference { element }
2382 | Expression::RepeaterIndexReference { element } => {
2383 let mut nc =
2385 NamedReference::new(&element.upgrade().unwrap(), SmolStr::new_static("$model"));
2386 vis(&mut nc);
2387 debug_assert!(nc.element().borrow().repeated.is_some());
2388 *element = Rc::downgrade(&nc.element());
2389 }
2390 _ => {}
2391 }
2392}
2393
2394pub fn visit_all_named_references_in_element(
2397 elem: &ElementRc,
2398 mut vis: impl FnMut(&mut NamedReference),
2399) {
2400 visit_element_expressions(elem, |expr, _, _| {
2401 visit_named_references_in_expression(expr, &mut vis)
2402 });
2403 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2404 for s in &mut states {
2405 for (r, _, _) in &mut s.property_changes {
2406 vis(r);
2407 }
2408 }
2409 elem.borrow_mut().states = states;
2410 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2411 for t in &mut transitions {
2412 for (r, _, _) in &mut t.property_animations {
2413 vis(r)
2414 }
2415 }
2416 elem.borrow_mut().transitions = transitions;
2417 let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
2418 if let Some(r) = &mut repeated {
2419 if let Some(lv) = &mut r.is_listview {
2420 vis(&mut lv.viewport_y);
2421 vis(&mut lv.viewport_height);
2422 vis(&mut lv.viewport_width);
2423 vis(&mut lv.listview_height);
2424 vis(&mut lv.listview_width);
2425 }
2426 }
2427 elem.borrow_mut().repeated = repeated;
2428 let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
2429 layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
2430 elem.borrow_mut().layout_info_prop = layout_info_prop;
2431 let mut debug = std::mem::take(&mut elem.borrow_mut().debug);
2432 for d in debug.iter_mut() {
2433 if let Some(l) = d.layout.as_mut() {
2434 l.visit_named_references(&mut vis)
2435 }
2436 }
2437 elem.borrow_mut().debug = debug;
2438
2439 let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props);
2440 accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x));
2441 elem.borrow_mut().accessibility_props = accessibility_props;
2442
2443 let geometry_props = elem.borrow_mut().geometry_props.take();
2444 if let Some(mut geometry_props) = geometry_props {
2445 vis(&mut geometry_props.x);
2446 vis(&mut geometry_props.y);
2447 vis(&mut geometry_props.width);
2448 vis(&mut geometry_props.height);
2449 elem.borrow_mut().geometry_props = Some(geometry_props);
2450 }
2451
2452 for expr in elem.borrow().bindings.values() {
2454 for nr in &mut expr.borrow_mut().two_way_bindings {
2455 vis(nr);
2456 }
2457 }
2458
2459 let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
2460 for pd in property_declarations.values_mut() {
2461 pd.is_alias.as_mut().map(&mut vis);
2462 }
2463 elem.borrow_mut().property_declarations = property_declarations;
2464}
2465
2466pub fn visit_all_named_references(
2468 component: &Component,
2469 vis: &mut impl FnMut(&mut NamedReference),
2470) {
2471 recurse_elem_including_sub_components_no_borrow(
2472 component,
2473 &Weak::new(),
2474 &mut |elem, parent_compo| {
2475 visit_all_named_references_in_element(elem, |nr| vis(nr));
2476 let compo = elem.borrow().enclosing_component.clone();
2477 if !Weak::ptr_eq(parent_compo, &compo) {
2478 let compo = compo.upgrade().unwrap();
2479 compo.root_constraints.borrow_mut().visit_named_references(vis);
2480 compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
2481 vis(&mut p.x);
2482 vis(&mut p.y);
2483 });
2484 compo.timers.borrow_mut().iter_mut().for_each(|t| {
2485 vis(&mut t.interval);
2486 vis(&mut t.triggered);
2487 vis(&mut t.running);
2488 });
2489 for o in compo.optimized_elements.borrow().iter() {
2490 visit_element_expressions(o, |expr, _, _| {
2491 visit_named_references_in_expression(expr, vis)
2492 });
2493 }
2494 }
2495 compo
2496 },
2497 );
2498}
2499
2500pub fn visit_all_expressions(
2504 component: &Component,
2505 mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
2506) {
2507 recurse_elem_including_sub_components(component, &Weak::new(), &mut |elem, parent_compo| {
2508 visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
2509 let compo = elem.borrow().enclosing_component.clone();
2510 if !Weak::ptr_eq(parent_compo, &compo) {
2511 let compo = compo.upgrade().unwrap();
2512 for o in compo.optimized_elements.borrow().iter() {
2513 visit_element_expressions(o, |expr, _, ty| vis(expr, ty));
2514 }
2515 }
2516 compo
2517 })
2518}
2519
2520#[derive(Debug, Clone)]
2521pub struct State {
2522 pub id: SmolStr,
2523 pub condition: Option<Expression>,
2524 pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
2525}
2526
2527#[derive(Debug, Clone)]
2528pub struct Transition {
2529 pub direction: TransitionDirection,
2530 pub state_id: SmolStr,
2531 pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
2532 pub node: syntax_nodes::Transition,
2533}
2534
2535impl Transition {
2536 fn from_node(
2537 trs: syntax_nodes::Transition,
2538 r: &ElementRc,
2539 tr: &TypeRegister,
2540 diag: &mut BuildDiagnostics,
2541 ) -> Transition {
2542 if let Some(star) = trs.child_token(SyntaxKind::Star) {
2543 diag.push_error("catch-all not yet implemented".into(), &star);
2544 };
2545 let direction_text = trs
2546 .first_child_or_token()
2547 .and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
2548 .unwrap_or_default();
2549
2550 Transition {
2551 direction: match direction_text.as_str() {
2552 "in" => TransitionDirection::In,
2553 "out" => TransitionDirection::Out,
2554 "in-out" => TransitionDirection::InOut,
2555 "in_out" => TransitionDirection::InOut,
2556 _ => {
2557 unreachable!("Unknown transition direction: '{}'", direction_text);
2558 }
2559 },
2560 state_id: trs
2561 .DeclaredIdentifier()
2562 .and_then(|x| parser::identifier_text(&x))
2563 .unwrap_or_default(),
2564 property_animations: trs
2565 .PropertyAnimation()
2566 .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
2567 .filter_map(|(pa, qn)| {
2568 lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then(
2569 |(ne, prop_type)| {
2570 animation_element_from_node(&pa, &qn, prop_type, diag, tr)
2571 .map(|anim_element| (ne, qn.to_source_location(), anim_element))
2572 },
2573 )
2574 })
2575 .collect(),
2576 node: trs.clone(),
2577 }
2578 }
2579}
2580
2581#[derive(Clone, Debug, derive_more::Deref)]
2582pub struct ExportedName {
2583 #[deref]
2584 pub name: SmolStr, pub name_ident: SyntaxNode,
2586}
2587
2588impl ExportedName {
2589 pub fn original_name(&self) -> SmolStr {
2590 self.name_ident
2591 .child_token(parser::SyntaxKind::Identifier)
2592 .map(|n| n.to_smolstr())
2593 .unwrap_or_else(|| self.name.clone())
2594 }
2595
2596 pub fn from_export_specifier(
2597 export_specifier: &syntax_nodes::ExportSpecifier,
2598 ) -> (SmolStr, ExportedName) {
2599 let internal_name =
2600 parser::identifier_text(&export_specifier.ExportIdentifier()).unwrap_or_default();
2601
2602 let (name, name_ident): (SmolStr, SyntaxNode) = export_specifier
2603 .ExportName()
2604 .and_then(|ident| {
2605 parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
2606 })
2607 .unwrap_or_else(|| (internal_name.clone(), export_specifier.ExportIdentifier().into()));
2608 (internal_name, ExportedName { name, name_ident })
2609 }
2610}
2611
2612#[derive(Default, Debug, derive_more::Deref)]
2613pub struct Exports {
2614 #[deref]
2615 components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
2616}
2617
2618impl Exports {
2619 pub fn from_node(
2620 doc: &syntax_nodes::Document,
2621 inner_components: &[Rc<Component>],
2622 type_registry: &TypeRegister,
2623 diag: &mut BuildDiagnostics,
2624 ) -> Self {
2625 let resolve_export_to_inner_component_or_import =
2626 |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| {
2627 if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) {
2628 Some(Either::Left(c))
2629 } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) =
2630 type_registry.lookup(internal_name)
2631 {
2632 Some(Either::Right(ty))
2633 } else if type_registry.lookup_element(internal_name).is_ok()
2634 || type_registry.lookup(internal_name) != Type::Invalid
2635 {
2636 diag.push_error(
2637 format!("Cannot export '{internal_name}' because it is not a component",),
2638 internal_name_node,
2639 );
2640 None
2641 } else {
2642 diag.push_error(format!("'{internal_name}' not found",), internal_name_node);
2643 None
2644 }
2645 };
2646
2647 let mut sorted_exports_with_duplicates: Vec<(ExportedName, _)> = Vec::new();
2648
2649 let mut extend_exports =
2650 |it: &mut dyn Iterator<Item = (ExportedName, Either<Rc<Component>, Type>)>| {
2651 for (name, compo_or_type) in it {
2652 let pos = sorted_exports_with_duplicates
2653 .partition_point(|(existing_name, _)| existing_name.name <= name.name);
2654 sorted_exports_with_duplicates.insert(pos, (name, compo_or_type));
2655 }
2656 };
2657
2658 extend_exports(
2659 &mut doc
2660 .ExportsList()
2661 .filter(|exports| exports.ExportModule().is_none())
2663 .flat_map(|exports| exports.ExportSpecifier())
2664 .filter_map(|export_specifier| {
2665 let (internal_name, exported_name) =
2666 ExportedName::from_export_specifier(&export_specifier);
2667 Some((
2668 exported_name,
2669 resolve_export_to_inner_component_or_import(
2670 &internal_name,
2671 &export_specifier.ExportIdentifier(),
2672 diag,
2673 )?,
2674 ))
2675 }),
2676 );
2677
2678 extend_exports(&mut doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(
2679 |component| {
2680 let name_ident: SyntaxNode = component.DeclaredIdentifier().into();
2681 let name =
2682 parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
2683 debug_assert!(diag.has_errors());
2684 SmolStr::default()
2685 });
2686
2687 let compo_or_type =
2688 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
2689
2690 Some((ExportedName { name, name_ident }, compo_or_type))
2691 },
2692 ));
2693
2694 extend_exports(
2695 &mut doc
2696 .ExportsList()
2697 .flat_map(|exports| {
2698 exports
2699 .StructDeclaration()
2700 .map(|st| st.DeclaredIdentifier())
2701 .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier()))
2702 })
2703 .filter_map(|name_ident| {
2704 let name = parser::identifier_text(&name_ident).unwrap_or_else(|| {
2705 debug_assert!(diag.has_errors());
2706 SmolStr::default()
2707 });
2708
2709 let name_ident = name_ident.into();
2710
2711 let compo_or_type =
2712 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
2713
2714 Some((ExportedName { name, name_ident }, compo_or_type))
2715 }),
2716 );
2717
2718 let mut sorted_deduped_exports = Vec::with_capacity(sorted_exports_with_duplicates.len());
2719 let mut it = sorted_exports_with_duplicates.into_iter().peekable();
2720 while let Some((exported_name, compo_or_type)) = it.next() {
2721 let mut warning_issued_on_first_occurrence = false;
2722
2723 while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) {
2725 let message = format!("Duplicated export '{}'", exported_name.name);
2726
2727 if !warning_issued_on_first_occurrence {
2728 diag.push_error(message.clone(), &exported_name.name_ident);
2729 warning_issued_on_first_occurrence = true;
2730 }
2731
2732 let duplicate_loc = it.next().unwrap().0.name_ident;
2733 diag.push_error(message.clone(), &duplicate_loc);
2734 }
2735
2736 sorted_deduped_exports.push((exported_name, compo_or_type));
2737 }
2738
2739 if let Some(last_compo) = inner_components.last() {
2740 let name = last_compo.id.clone();
2741 if last_compo.is_global() {
2742 if sorted_deduped_exports.is_empty() {
2743 diag.push_warning("Global singleton is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
2744 sorted_deduped_exports.push((
2745 ExportedName { name, name_ident: doc.clone().into() },
2746 Either::Left(last_compo.clone()),
2747 ))
2748 }
2749 } else if !sorted_deduped_exports
2750 .iter()
2751 .any(|e| e.1.as_ref().left().is_some_and(|c| !c.is_global()))
2752 {
2753 diag.push_warning("Component is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
2754 let insert_pos = sorted_deduped_exports
2755 .partition_point(|(existing_export, _)| existing_export.name <= name);
2756 sorted_deduped_exports.insert(
2757 insert_pos,
2758 (
2759 ExportedName { name, name_ident: doc.clone().into() },
2760 Either::Left(last_compo.clone()),
2761 ),
2762 )
2763 }
2764 }
2765 Self { components_or_types: sorted_deduped_exports }
2766 }
2767
2768 pub fn add_reexports(
2769 &mut self,
2770 other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>,
2771 diag: &mut BuildDiagnostics,
2772 ) {
2773 for export in other_exports {
2774 match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) {
2775 Ok(_) => {
2776 diag.push_warning(
2777 format!(
2778 "'{}' is already exported in this file; it will not be re-exported",
2779 &*export.0
2780 ),
2781 &export.0.name_ident,
2782 );
2783 }
2784 Err(insert_pos) => {
2785 self.components_or_types.insert(insert_pos, export);
2786 }
2787 }
2788 }
2789 }
2790
2791 pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> {
2792 self.components_or_types
2793 .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name))
2794 .ok()
2795 .map(|index| self.components_or_types[index].1.clone())
2796 }
2797
2798 pub fn retain(
2799 &mut self,
2800 func: impl FnMut(&mut (ExportedName, Either<Rc<Component>, Type>)) -> bool,
2801 ) {
2802 self.components_or_types.retain_mut(func)
2803 }
2804
2805 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
2806 let components_or_types = self
2807 .components_or_types
2808 .iter()
2809 .map(|(en, either)| {
2810 let en = en.clone();
2811 let either = match either {
2812 itertools::Either::Left(l) => itertools::Either::Left({
2813 Weak::upgrade(&snapshotter.use_component(l))
2814 .expect("Component should cleanly upgrade here")
2815 }),
2816 itertools::Either::Right(r) => itertools::Either::Right(r.clone()),
2817 };
2818 (en, either)
2819 })
2820 .collect();
2821
2822 Self { components_or_types }
2823 }
2824}
2825
2826impl std::iter::IntoIterator for Exports {
2827 type Item = (ExportedName, Either<Rc<Component>, Type>);
2828
2829 type IntoIter = std::vec::IntoIter<Self::Item>;
2830
2831 fn into_iter(self) -> Self::IntoIter {
2832 self.components_or_types.into_iter()
2833 }
2834}
2835
2836pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
2840 let component = repeated_element.borrow().base_type.as_component().clone();
2841 debug_assert_eq!(Rc::strong_count(&component), 2);
2845 let old_root = &component.root_element;
2846
2847 adjust_geometry_for_injected_parent(&new_root, old_root);
2848
2849 let mut elements_with_enclosing_component_reference = Vec::new();
2851 recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
2852 if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade() {
2853 if Rc::ptr_eq(&enclosing_component, &component) {
2854 elements_with_enclosing_component_reference.push(element.clone());
2855 }
2856 }
2857 });
2858 elements_with_enclosing_component_reference
2859 .extend_from_slice(component.optimized_elements.borrow().as_slice());
2860 elements_with_enclosing_component_reference.push(new_root.clone());
2861
2862 new_root.borrow_mut().child_of_layout =
2863 std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
2864 let layout_info_prop = old_root.borrow().layout_info_prop.clone().or_else(|| {
2865 let li_v = crate::layout::create_new_prop(
2867 &new_root,
2868 SmolStr::new_static("layoutinfo-v"),
2869 crate::typeregister::layout_info_type().into(),
2870 );
2871 let li_h = crate::layout::create_new_prop(
2872 &new_root,
2873 SmolStr::new_static("layoutinfo-h"),
2874 crate::typeregister::layout_info_type().into(),
2875 );
2876 let expr_h = crate::layout::implicit_layout_info_call(old_root, Orientation::Horizontal);
2877 let expr_v = crate::layout::implicit_layout_info_call(old_root, Orientation::Vertical);
2878 let expr_v =
2879 BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location());
2880 li_v.element().borrow_mut().bindings.insert(li_v.name().clone(), expr_v.into());
2881 let expr_h =
2882 BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location());
2883 li_h.element().borrow_mut().bindings.insert(li_h.name().clone(), expr_h.into());
2884 Some((li_h.clone(), li_v.clone()))
2885 });
2886 new_root.borrow_mut().layout_info_prop = layout_info_prop;
2887
2888 drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
2891
2892 debug_assert_eq!(Rc::strong_count(&component), 1);
2893
2894 let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
2895
2896 let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
2897 new_root.borrow_mut().children.push(old_root);
2898
2899 let component = Rc::new(component);
2900 repeated_element.borrow_mut().base_type = ElementType::Component(component.clone());
2901
2902 for elem in elements_with_enclosing_component_reference {
2903 elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
2904 }
2905}
2906
2907pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) {
2910 let mut injected_parent_mut = injected_parent.borrow_mut();
2911 injected_parent_mut.bindings.insert(
2912 "z".into(),
2913 RefCell::new(BindingExpression::new_two_way(NamedReference::new(
2914 old_elem,
2915 SmolStr::new_static("z"),
2916 ))),
2917 );
2918 injected_parent_mut.property_declarations.insert(
2920 "dummy".into(),
2921 PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() },
2922 );
2923 let mut old_elem_mut = old_elem.borrow_mut();
2924 injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent);
2925 injected_parent_mut.geometry_props.clone_from(&old_elem_mut.geometry_props);
2926 drop(injected_parent_mut);
2927 old_elem_mut.geometry_props.as_mut().unwrap().x =
2928 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
2929 old_elem_mut.geometry_props.as_mut().unwrap().y =
2930 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
2931}