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