| 1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
| 3 | |
| 4 | use crate::diagnostics::BuildDiagnostics; |
| 5 | use crate::langtype::ElementType; |
| 6 | use crate::object_tree::*; |
| 7 | use smol_str::{format_smolstr, SmolStr, ToSmolStr}; |
| 8 | use std::collections::HashMap; |
| 9 | use std::rc::Rc; |
| 10 | |
| 11 | /// This pass make sure that the id of the elements are unique |
| 12 | /// |
| 13 | /// It currently does so by adding a number to the existing id |
| 14 | pub fn assign_unique_id(doc: &Document) { |
| 15 | let mut count: u32 = 0; |
| 16 | doc.visit_all_used_components(|component: &Rc| { |
| 17 | if !component.is_global() { |
| 18 | assign_unique_id_in_component(component, &mut count) |
| 19 | } |
| 20 | }); |
| 21 | rename_globals(doc, count); |
| 22 | } |
| 23 | |
| 24 | fn assign_unique_id_in_component(component: &Rc<Component>, count: &mut u32) { |
| 25 | recurse_elem_including_sub_components(component, &(), &mut |elem: &Rc>, _| { |
| 26 | *count += 1; |
| 27 | let mut elem_mut: RefMut<'_, Element> = elem.borrow_mut(); |
| 28 | let old_id: SmolStr = if !elem_mut.id.is_empty() { |
| 29 | elem_mut.id.clone() |
| 30 | } else { |
| 31 | elem_mut.base_type.to_smolstr().to_ascii_lowercase().into() |
| 32 | }; |
| 33 | elem_mut.id = format_smolstr!(" {}- {}" , old_id, count); |
| 34 | |
| 35 | let enclosing: Rc = elem_mut.enclosing_component.upgrade().unwrap(); |
| 36 | if Rc::ptr_eq(this:elem, &enclosing.root_element) { |
| 37 | for o: &Rc> in enclosing.optimized_elements.borrow().iter() { |
| 38 | *count += 1; |
| 39 | let mut elem_mut: RefMut<'_, Element> = o.borrow_mut(); |
| 40 | elem_mut.id = format_smolstr!("optimized- {}- {}" , elem_mut.id, count); |
| 41 | } |
| 42 | } |
| 43 | }); |
| 44 | } |
| 45 | |
| 46 | /// Give globals unique name |
| 47 | fn rename_globals(doc: &Document, mut count: u32) { |
| 48 | for g: &Rc in &doc.used_types.borrow().globals { |
| 49 | count += 1; |
| 50 | let mut root: RefMut<'_, Element> = g.root_element.borrow_mut(); |
| 51 | if matches!(&root.base_type, ElementType::Builtin(_)) { |
| 52 | // builtin global keeps its name |
| 53 | root.id.clone_from(&g.id); |
| 54 | } else if let Some(s: &ExportedName) = g.exported_global_names.borrow().first() { |
| 55 | root.id = s.to_smolstr(); |
| 56 | } else { |
| 57 | root.id = format_smolstr!(" {}- {}" , g.id, count); |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | /// Checks that all ids in the Component are unique |
| 63 | pub fn check_unique_id(doc: &Document, diag: &mut BuildDiagnostics) { |
| 64 | for component: &Rc in &doc.inner_components { |
| 65 | check_unique_id_in_component(component, diag); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | fn check_unique_id_in_component(component: &Rc<Component>, diag: &mut BuildDiagnostics) { |
| 70 | struct SeenId { |
| 71 | element: ElementRc, |
| 72 | error_reported: bool, |
| 73 | } |
| 74 | let mut seen_ids: HashMap<SmolStr, SeenId> = HashMap::new(); |
| 75 | |
| 76 | recurse_elem(&component.root_element, &(), &mut |elem, _| { |
| 77 | let elem_bor = elem.borrow(); |
| 78 | let id = &elem_bor.id; |
| 79 | if !id.is_empty() { |
| 80 | if let Some(other_loc) = seen_ids.get_mut(id) { |
| 81 | debug_assert!(!Rc::ptr_eq(&other_loc.element, elem)); |
| 82 | let message = format!("duplicated element id ' {id}'" ); |
| 83 | if !other_loc.error_reported { |
| 84 | diag.push_error(message.clone(), &*other_loc.element.borrow()); |
| 85 | other_loc.error_reported = true; |
| 86 | } |
| 87 | diag.push_error(message, &*elem_bor); |
| 88 | } else { |
| 89 | seen_ids |
| 90 | .insert(id.clone(), SeenId { element: elem.clone(), error_reported: false }); |
| 91 | } |
| 92 | } |
| 93 | }) |
| 94 | } |
| 95 | |