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