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