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