freya_core/
runner.rs

1use std::{
2    any::TypeId,
3    cell::RefCell,
4    cmp::Ordering,
5    collections::{
6        HashMap,
7        HashSet,
8        VecDeque,
9    },
10    fmt::Debug,
11    rc::Rc,
12    sync::atomic::AtomicU64,
13};
14
15use futures_lite::{
16    FutureExt,
17    StreamExt,
18};
19use itertools::Itertools;
20use pathgraph::PathGraph;
21use rustc_hash::{
22    FxHashMap,
23    FxHashSet,
24};
25
26use crate::{
27    current_context::CurrentContext,
28    diff_key::DiffKey,
29    element::{
30        Element,
31        ElementExt,
32        EventHandlerType,
33    },
34    events::{
35        data::{
36            Event,
37            EventType,
38        },
39        name::EventName,
40    },
41    node_id::NodeId,
42    path_element::PathElement,
43    prelude::{
44        Task,
45        TaskId,
46    },
47    reactive_context::ReactiveContext,
48    scope::{
49        PathNode,
50        Scope,
51        ScopeStorage,
52    },
53    scope_id::ScopeId,
54    tree::DiffModifies,
55};
56
57#[derive(Debug, PartialEq)]
58pub enum MutationRemove {
59    /// Because elements always have a different parent we can easily get their position relatively to their parent
60    Element { id: NodeId, index: u32 },
61    /// In the other hand, roots of Scopes are manually connected to their parent scopes, so getting their index is not worth the effort.
62    Scope { id: NodeId },
63}
64
65impl MutationRemove {
66    pub fn node_id(&self) -> NodeId {
67        match self {
68            Self::Element { id, .. } => *id,
69            Self::Scope { id } => *id,
70        }
71    }
72}
73
74#[derive(Default)]
75pub struct Mutations {
76    pub added: Vec<(NodeId, NodeId, u32, Rc<dyn ElementExt>)>,
77    pub modified: Vec<(NodeId, Rc<dyn ElementExt>, DiffModifies)>,
78    pub removed: Vec<MutationRemove>,
79    pub moved: HashMap<NodeId, Vec<(u32, NodeId)>>,
80}
81
82impl Debug for Mutations {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        f.write_fmt(format_args!(
85            "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
86            self.added
87                .iter()
88                .map(|(a, b, c, _)| (*a, *b, *c))
89                .collect::<Vec<_>>(),
90            self.modified.iter().map(|(a, _, _)| *a).collect::<Vec<_>>(),
91            self.removed,
92            self.moved
93        ))
94    }
95}
96
97pub enum Message {
98    MarkScopeAsDirty(ScopeId),
99    PollTask(TaskId),
100}
101
102pub struct Runner {
103    pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
104    pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
105
106    pub(crate) dirty_scopes: FxHashSet<ScopeId>,
107    pub(crate) dirty_tasks: VecDeque<TaskId>,
108
109    pub node_to_scope: FxHashMap<NodeId, ScopeId>,
110
111    pub(crate) node_id_counter: NodeId,
112    pub(crate) scope_id_counter: ScopeId,
113    pub(crate) task_id_counter: Rc<AtomicU64>,
114
115    pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
116
117    pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
118    pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
119}
120
121impl Debug for Runner {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        f.debug_struct("Runner")
124            .field("dirty_scopes", &self.dirty_scopes.len())
125            .field("dirty_tasks", &self.dirty_tasks.len())
126            .field("node_to_scope", &self.node_to_scope.len())
127            .field("scopes", &self.scopes.len())
128            .field("scopes_storages", &self.scopes_storages.borrow().len())
129            .field("tasks", &self.tasks.borrow().len())
130            .finish()
131    }
132}
133
134impl Drop for Runner {
135    fn drop(&mut self) {
136        // Graceful shutdown of scopes based on their height, starting from the deepest
137        for (scope_id, _scope) in self
138            .scopes
139            .drain()
140            .sorted_by_key(|s| s.1.borrow().height)
141            .rev()
142        {
143            CurrentContext::run_with_reactive(
144                CurrentContext {
145                    scope_id,
146                    scopes_storages: self.scopes_storages.clone(),
147                    tasks: self.tasks.clone(),
148                    task_id_counter: self.task_id_counter.clone(),
149                    sender: self.sender.clone(),
150                },
151                || {
152                    let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
153                },
154            );
155        }
156    }
157}
158
159impl Runner {
160    pub fn new(root: impl Fn() -> Element + 'static) -> Self {
161        let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
162        Self {
163            scopes: FxHashMap::from_iter([(
164                ScopeId::ROOT,
165                Rc::new(RefCell::new(Scope {
166                    parent_node_id_in_parent: NodeId::ROOT,
167                    path_in_parent: Box::from([]),
168                    height: 0,
169                    parent_id: None,
170                    id: ScopeId::ROOT,
171                    key: DiffKey::Root,
172                    comp: Rc::new(move |_| root()),
173                    props: Rc::new(()),
174                    element: None,
175                    nodes: {
176                        let mut map = PathGraph::new();
177                        map.insert(
178                            &[],
179                            PathNode {
180                                node_id: NodeId::ROOT,
181                                scope_id: None,
182                            },
183                        );
184                        map
185                    },
186                })),
187            )]),
188            scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
189                ScopeId::ROOT,
190                ScopeStorage::new(None, |writer| {
191                    ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
192                }),
193            )]))),
194
195            node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
196
197            node_id_counter: NodeId::ROOT,
198            scope_id_counter: ScopeId::ROOT,
199            task_id_counter: Rc::default(),
200
201            dirty_tasks: VecDeque::default(),
202            dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
203
204            tasks: Rc::default(),
205
206            sender,
207            receiver,
208        }
209    }
210
211    #[cfg(all(debug_assertions, feature = "debug-integrity"))]
212    #[cfg_attr(feature = "hotpath", hotpath::measure)]
213    pub fn verify_scopes_integrity(&self) {
214        let mut visited = FxHashSet::default();
215        let size = self.scopes.len();
216        let mut buffer = vec![ScopeId::ROOT];
217        while let Some(scope_id) = buffer.pop() {
218            if visited.contains(&scope_id) {
219                continue;
220            }
221            visited.insert(scope_id);
222            let scope = self.scopes.get(&scope_id).unwrap();
223            let scope = scope.borrow();
224            if let Some(parent) = scope.parent_id {
225                buffer.push(parent);
226            }
227            scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
228                if let Some(scope_id) = scope_id {
229                    buffer.push(scope_id);
230                }
231            });
232        }
233        assert_eq!(size, visited.len())
234    }
235
236    pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
237        CurrentContext::run(
238            CurrentContext {
239                scope_id: ScopeId::ROOT,
240                scopes_storages: self.scopes_storages.clone(),
241                tasks: self.tasks.clone(),
242                task_id_counter: self.task_id_counter.clone(),
243                sender: self.sender.clone(),
244            },
245            move || {
246                let context = context();
247                let mut scopes_storages = self.scopes_storages.borrow_mut();
248                let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
249                root_scope_storage
250                    .contexts
251                    .insert(TypeId::of::<T>(), Rc::new(context.clone()));
252
253                context
254            },
255        )
256    }
257
258    pub fn handle_event(
259        &mut self,
260        node_id: impl Into<NodeId>,
261        event_name: EventName,
262        event_type: EventType,
263        bubbles: bool,
264    ) -> bool {
265        let node_id = node_id.into();
266        #[cfg(debug_assertions)]
267        tracing::info!("Handling event {event_name:?} for {node_id:?}");
268        let propagate = Rc::new(RefCell::new(bubbles));
269        let default = Rc::new(RefCell::new(true));
270
271        let Some(scope_id) = self.node_to_scope.get(&node_id) else {
272            return false;
273        };
274        let Some(path) = self
275            .scopes
276            .get(scope_id)
277            .unwrap()
278            .borrow()
279            .nodes
280            .find_path(|value| {
281                value
282                    == Some(&PathNode {
283                        node_id,
284                        scope_id: None,
285                    })
286            })
287        else {
288            return false;
289        };
290
291        let mut current_target = Some((path, *scope_id));
292        while let Some((path, scope_id)) = current_target.take() {
293            let scope = self.scopes.get(&scope_id).cloned().unwrap();
294            scope.borrow().with_element(&path, |element| {
295                match element {
296                    PathElement::Component { .. } => {
297                        unreachable!()
298                    }
299                    PathElement::Element { element, .. } => {
300                        CurrentContext::run(
301                            CurrentContext {
302                                scope_id,
303                                scopes_storages: self.scopes_storages.clone(),
304                                tasks: self.tasks.clone(),
305                                task_id_counter: self.task_id_counter.clone(),
306                                sender: self.sender.clone(),
307                            },
308                            || {
309                                match &event_type {
310                                    EventType::Mouse(data) => {
311                                        let event_handlers = element.events_handlers();
312                                        if let Some(event_handlers) = event_handlers {
313                                            match event_handlers.get(&event_name) {
314                                                Some(EventHandlerType::Mouse(handler)) => {
315                                                    handler.call(Event {
316                                                        data: data.clone(),
317                                                        propagate: propagate.clone(),
318                                                        default: default.clone(),
319                                                    });
320                                                }
321                                                Some(_) => unreachable!(),
322                                                _ => {}
323                                            }
324                                        }
325                                    }
326                                    EventType::Keyboard(data) => {
327                                        let event_handlers = element.events_handlers();
328                                        if let Some(event_handlers) = event_handlers {
329                                            match event_handlers.get(&event_name) {
330                                                Some(EventHandlerType::Keyboard(handler)) => {
331                                                    handler.call(Event {
332                                                        data: data.clone(),
333                                                        propagate: propagate.clone(),
334                                                        default: default.clone(),
335                                                    });
336                                                }
337                                                Some(_) => unreachable!(),
338                                                _ => {}
339                                            }
340                                        }
341                                    }
342                                    EventType::Sized(data) => {
343                                        let event_handlers = element.events_handlers();
344                                        if let Some(event_handlers) = event_handlers {
345                                            match event_handlers.get(&event_name) {
346                                                Some(EventHandlerType::Sized(handler)) => {
347                                                    handler.call(Event {
348                                                        data: data.clone(),
349                                                        propagate: propagate.clone(),
350                                                        default: default.clone(),
351                                                    });
352                                                }
353                                                Some(_) => unreachable!(),
354                                                _ => {}
355                                            }
356                                        }
357                                    }
358                                    EventType::Wheel(data) => {
359                                        let event_handlers = element.events_handlers();
360                                        if let Some(event_handlers) = event_handlers {
361                                            match event_handlers.get(&event_name) {
362                                                Some(EventHandlerType::Wheel(handler)) => {
363                                                    handler.call(Event {
364                                                        data: data.clone(),
365                                                        propagate: propagate.clone(),
366                                                        default: default.clone(),
367                                                    });
368                                                }
369                                                Some(_) => unreachable!(),
370                                                _ => {}
371                                            }
372                                        }
373                                    }
374                                    EventType::Touch(data) => {
375                                        let event_handlers = element.events_handlers();
376                                        if let Some(event_handlers) = event_handlers {
377                                            match event_handlers.get(&event_name) {
378                                                Some(EventHandlerType::Touch(handler)) => {
379                                                    handler.call(Event {
380                                                        data: data.clone(),
381                                                        propagate: propagate.clone(),
382                                                        default: default.clone(),
383                                                    });
384                                                }
385                                                Some(_) => unreachable!(),
386                                                _ => {}
387                                            }
388                                        }
389                                    }
390                                    EventType::Pointer(data) => {
391                                        let event_handlers = element.events_handlers();
392                                        if let Some(event_handlers) = event_handlers {
393                                            match event_handlers.get(&event_name) {
394                                                Some(EventHandlerType::Pointer(handler)) => {
395                                                    handler.call(Event {
396                                                        data: data.clone(),
397                                                        propagate: propagate.clone(),
398                                                        default: default.clone(),
399                                                    });
400                                                }
401                                                Some(_) => unreachable!(),
402                                                _ => {}
403                                            }
404                                        }
405                                    }
406                                    EventType::File(data) => {
407                                        let event_handlers = element.events_handlers();
408                                        if let Some(event_handlers) = event_handlers {
409                                            match event_handlers.get(&event_name) {
410                                                Some(EventHandlerType::File(handler)) => {
411                                                    handler.call(Event {
412                                                        data: data.clone(),
413                                                        propagate: propagate.clone(),
414                                                        default: default.clone(),
415                                                    });
416                                                }
417                                                Some(_) => unreachable!(),
418                                                _ => {}
419                                            }
420                                        }
421                                    }
422                                    EventType::ImePreedit(data) => {
423                                        let event_handlers = element.events_handlers();
424                                        if let Some(event_handlers) = event_handlers {
425                                            match event_handlers.get(&event_name) {
426                                                Some(EventHandlerType::ImePreedit(handler)) => {
427                                                    handler.call(Event {
428                                                        data: data.clone(),
429                                                        propagate: propagate.clone(),
430                                                        default: default.clone(),
431                                                    });
432                                                }
433                                                Some(_) => unreachable!(),
434                                                _ => {}
435                                            }
436                                        }
437                                    }
438                                }
439
440                                // Bubble up if desired
441                                if *propagate.borrow() {
442                                    if path.len() > 1 {
443                                        // Change the target to this element parent (still in the same Scope)
444                                        current_target
445                                            .replace((path[..path.len() - 1].to_vec(), scope_id));
446                                    } else {
447                                        let mut parent_scope_id = scope.borrow().parent_id;
448                                        // Otherwise change the target to this element parent in the parent Scope
449                                        loop {
450                                            if let Some(parent_id) = parent_scope_id.take() {
451                                                let parent_scope =
452                                                    self.scopes.get(&parent_id).unwrap();
453                                                let path = parent_scope.borrow().nodes.find_path(
454                                                    |value| {
455                                                        value
456                                                            == Some(&PathNode {
457                                                                node_id: scope
458                                                                    .borrow()
459                                                                    .parent_node_id_in_parent,
460                                                                scope_id: None,
461                                                            })
462                                                    },
463                                                );
464                                                if let Some(path) = path {
465                                                    current_target.replace((path, parent_id));
466                                                    break;
467                                                } else {
468                                                    parent_scope_id =
469                                                        parent_scope.borrow().parent_id;
470                                                }
471                                            } else {
472                                                return;
473                                            }
474                                        }
475                                    }
476                                }
477                            },
478                        )
479                    }
480                }
481            });
482        }
483        *default.borrow()
484    }
485
486    #[cfg_attr(feature = "hotpath", hotpath::measure)]
487    pub async fn handle_events(&mut self) {
488        loop {
489            while let Ok(Some(msg)) = self.receiver.try_next() {
490                match msg {
491                    Message::MarkScopeAsDirty(scope_id) => {
492                        self.dirty_scopes.insert(scope_id);
493                    }
494                    Message::PollTask(task_id) => {
495                        self.dirty_tasks.push_back(task_id);
496                    }
497                }
498            }
499
500            if !self.dirty_scopes.is_empty() {
501                return;
502            }
503
504            while let Some(task_id) = self.dirty_tasks.pop_front() {
505                let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
506                    continue;
507                };
508                let mut task = task.borrow_mut();
509                let waker = task.waker.clone();
510
511                let mut cx = std::task::Context::from_waker(&waker);
512
513                CurrentContext::run(
514                    {
515                        let Some(scope) = self.scopes.get(&task.scope_id) else {
516                            continue;
517                        };
518                        CurrentContext {
519                            scope_id: scope.borrow().id,
520                            scopes_storages: self.scopes_storages.clone(),
521                            tasks: self.tasks.clone(),
522                            task_id_counter: self.task_id_counter.clone(),
523                            sender: self.sender.clone(),
524                        }
525                    },
526                    || {
527                        let poll_result = task.future.poll(&mut cx);
528                        if poll_result.is_ready() {
529                            self.tasks.borrow_mut().remove(&task_id);
530                        }
531                    },
532                );
533            }
534
535            if !self.dirty_scopes.is_empty() {
536                return;
537            }
538
539            while let Some(msg) = self.receiver.next().await {
540                match msg {
541                    Message::MarkScopeAsDirty(scope_id) => {
542                        self.dirty_scopes.insert(scope_id);
543                    }
544                    Message::PollTask(task_id) => {
545                        self.dirty_tasks.push_back(task_id);
546                    }
547                }
548            }
549        }
550    }
551
552    /// Useful for freya-testing
553    #[cfg_attr(feature = "hotpath", hotpath::measure)]
554    pub fn handle_events_immediately(&mut self) {
555        while let Ok(Some(msg)) = self.receiver.try_next() {
556            match msg {
557                Message::MarkScopeAsDirty(scope_id) => {
558                    self.dirty_scopes.insert(scope_id);
559                }
560                Message::PollTask(task_id) => {
561                    self.dirty_tasks.push_back(task_id);
562                }
563            }
564        }
565
566        if !self.dirty_scopes.is_empty() {
567            return;
568        }
569
570        // Poll here
571        while let Some(task_id) = self.dirty_tasks.pop_front() {
572            let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
573                continue;
574            };
575            let mut task = task.borrow_mut();
576            let waker = task.waker.clone();
577
578            let mut cx = std::task::Context::from_waker(&waker);
579
580            CurrentContext::run(
581                {
582                    let Some(scope) = self.scopes.get(&task.scope_id) else {
583                        continue;
584                    };
585                    CurrentContext {
586                        scope_id: scope.borrow().id,
587                        scopes_storages: self.scopes_storages.clone(),
588                        tasks: self.tasks.clone(),
589                        task_id_counter: self.task_id_counter.clone(),
590                        sender: self.sender.clone(),
591                    }
592                },
593                || {
594                    let poll_result = task.future.poll(&mut cx);
595                    if poll_result.is_ready() {
596                        self.tasks.borrow_mut().remove(&task_id);
597                    }
598                },
599            );
600        }
601    }
602
603    #[cfg_attr(feature = "hotpath", hotpath::measure)]
604    pub fn sync_and_update(&mut self) -> Mutations {
605        self.handle_events_immediately();
606        use itertools::Itertools;
607
608        #[cfg(all(debug_assertions, feature = "debug-integrity"))]
609        self.verify_scopes_integrity();
610
611        let mut mutations = Mutations::default();
612
613        let dirty_scopes = self
614            .dirty_scopes
615            .drain()
616            .filter_map(|id| self.scopes.get(&id).cloned())
617            .sorted_by_key(|s| s.borrow().height)
618            .map(|s| s.borrow().id)
619            .collect::<Box<[_]>>();
620
621        let mut visited_scopes = FxHashSet::default();
622
623        for scope_id in dirty_scopes {
624            // No need to run scopes more than once
625            if visited_scopes.contains(&scope_id) {
626                continue;
627            }
628
629            let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
630                continue;
631            };
632
633            let scope_id = scope_rc.borrow().id;
634
635            let element = CurrentContext::run_with_reactive(
636                CurrentContext {
637                    scope_id,
638                    scopes_storages: self.scopes_storages.clone(),
639                    tasks: self.tasks.clone(),
640                    task_id_counter: self.task_id_counter.clone(),
641                    sender: self.sender.clone(),
642                },
643                || {
644                    let scope = scope_rc.borrow();
645                    (scope.comp)(scope.props.clone())
646                },
647            );
648
649            let path_element = PathElement::from_element(vec![0], element);
650            let mut diff = Diff::default();
651            path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
652
653            self.apply_diff(&scope_rc, diff, &mut mutations, &path_element);
654
655            self.run_scope(
656                &scope_rc,
657                &path_element,
658                &mut mutations,
659                &mut visited_scopes,
660            );
661
662            let mut scopes_storages = self.scopes_storages.borrow_mut();
663            let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
664            scope_storage.current_value = 0;
665            scope_storage.current_run += 1;
666
667            scope_rc.borrow_mut().element = Some(path_element);
668        }
669
670        mutations
671    }
672
673    #[cfg_attr(feature = "hotpath", hotpath::measure)]
674    fn run_scope(
675        &mut self,
676        scope: &Rc<RefCell<Scope>>,
677        element: &PathElement,
678        mutations: &mut Mutations,
679        visited_scopes: &mut FxHashSet<ScopeId>,
680    ) {
681        visited_scopes.insert(scope.borrow().id);
682        match element {
683            PathElement::Component {
684                comp,
685                props,
686                key,
687                path,
688            } => {
689                // Safe to unwrap because this is a component
690                let assigned_scope_id = scope
691                    .borrow()
692                    .nodes
693                    .get(path)
694                    .and_then(|path_node| path_node.scope_id)
695                    .unwrap();
696
697                let parent_node_id = if path.as_ref() == [0] {
698                    scope.borrow().parent_node_id_in_parent
699                } else {
700                    scope
701                        .borrow()
702                        .nodes
703                        .get(&path[..path.len() - 1])
704                        .unwrap()
705                        .node_id
706                };
707
708                if let Some(Ok(mut existing_scope)) = self
709                    .scopes
710                    .get(&assigned_scope_id)
711                    .map(|s| s.try_borrow_mut())
712                {
713                    let key_changed = existing_scope.key != *key;
714                    if key_changed || existing_scope.props.changed(props.as_ref()) {
715                        self.dirty_scopes.insert(assigned_scope_id);
716                        existing_scope.props = props.clone();
717
718                        if key_changed {
719                            self.scopes_storages
720                                .borrow_mut()
721                                .get_mut(&assigned_scope_id)
722                                .unwrap()
723                                .reset();
724                        }
725                    }
726                } else {
727                    self.scopes.insert(
728                        assigned_scope_id,
729                        Rc::new(RefCell::new(Scope {
730                            parent_node_id_in_parent: parent_node_id,
731                            path_in_parent: path.clone(),
732                            height: scope.borrow().height + 1,
733                            parent_id: Some(scope.borrow().id),
734                            id: assigned_scope_id,
735                            key: key.clone(),
736                            comp: comp.clone(),
737                            props: props.clone(),
738                            element: None,
739                            nodes: PathGraph::default(),
740                        })),
741                    );
742                    self.scopes_storages.borrow_mut().insert(
743                        assigned_scope_id,
744                        ScopeStorage::new(Some(scope.borrow().id), |writer| {
745                            ReactiveContext::new_for_scope(
746                                self.sender.clone(),
747                                assigned_scope_id,
748                                writer,
749                            )
750                        }),
751                    );
752                    self.dirty_scopes.insert(assigned_scope_id);
753                }
754
755                let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
756
757                if !was_dirty {
758                    // No need to rerun scope if it is not dirty
759                    return;
760                }
761
762                let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
763
764                let element = hotpath::measure_block!("Scope Rendering", {
765                    CurrentContext::run_with_reactive(
766                        CurrentContext {
767                            scope_id: assigned_scope_id,
768                            scopes_storages: self.scopes_storages.clone(),
769                            tasks: self.tasks.clone(),
770                            task_id_counter: self.task_id_counter.clone(),
771                            sender: self.sender.clone(),
772                        },
773                        || {
774                            let scope = scope_rc.borrow();
775                            (scope.comp)(scope.props.clone())
776                        },
777                    )
778                });
779
780                let path_element = PathElement::from_element(vec![0], element);
781                let mut diff = Diff::default();
782                path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
783
784                self.apply_diff(&scope_rc, diff, mutations, &path_element);
785
786                self.run_scope(&scope_rc, &path_element, mutations, visited_scopes);
787                let mut scopes_storages = self.scopes_storages.borrow_mut();
788                let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
789                scope_storage.current_value = 0;
790                scope_storage.current_run += 1;
791
792                scope_rc.borrow_mut().element = Some(path_element);
793            }
794            PathElement::Element { elements, .. } => {
795                for element in elements.iter() {
796                    self.run_scope(scope, element, mutations, visited_scopes);
797                }
798            }
799        }
800    }
801
802    #[cfg_attr(feature = "hotpath", hotpath::measure)]
803    fn apply_diff(
804        &mut self,
805        scope: &Rc<RefCell<Scope>>,
806        diff: Diff,
807        mutations: &mut Mutations,
808        path_element: &PathElement,
809    ) {
810        let mut moved_nodes =
811            FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
812        let mut parents_to_resync_scopes = FxHashSet::default();
813
814        // Store the moved nodes so that they can
815        // later be rarranged once the removals and additions have been done
816        for (parent, movements) in &diff.moved {
817            parents_to_resync_scopes.insert(parent.clone());
818            let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
819                let parent_node_id = scope.borrow().nodes.get(parent).unwrap().node_id;
820                (parent_node_id, FxHashMap::default())
821            });
822
823            for (from, _to) in movements.iter() {
824                let mut path = parent.to_vec();
825                path.push(*from);
826
827                let path_node = scope.borrow().nodes.get(&path).cloned().unwrap();
828
829                paths.1.insert(*from, path_node);
830            }
831        }
832
833        // Collect a set of branches to remove in cascade
834        let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
835        let mut scope_removal_buffer = vec![];
836
837        // Given some removals like:
838        // [
839        //     [0,2],
840        //     [0,1,0,1],
841        //     [0,1,0,2],
842        //     [0,3],
843        //     [0,1,5,8],
844        // ]
845        //
846        // We want them ordered like:
847        // [
848        //     [0,3],
849        //     [0,2],
850        //     [0,1,5,8],
851        //     [0,1,0,2],
852        //     [0,1,0,1],
853        // ]
854        //
855        // This way any removal does not move the next removals
856        'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
857            for (x, y) in a.iter().zip(b.iter()) {
858                match x.cmp(y) {
859                    Ordering::Equal => continue,
860                    non_eq => return non_eq.reverse(),
861                }
862            }
863            b.len().cmp(&a.len())
864        }) {
865            parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
866
867            let path_node = scope.borrow().nodes.get(removed).cloned();
868            if let Some(PathNode { node_id, scope_id }) = path_node {
869                if let Some(scope_id) = scope_id {
870                    // component -> queue scope removal and remove node
871                    scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
872                    scope.borrow_mut().nodes.remove(removed);
873                } else {
874                    // plain element removal
875                    mutations.removed.push(MutationRemove::Element {
876                        id: node_id,
877                        index: *removed.last().unwrap(),
878                    });
879
880                    // Skip if this removed path is already covered by a previously selected root
881                    for (root, inner) in &mut selected_roots {
882                        if is_descendant(removed, root) {
883                            inner.insert(removed);
884                            continue 'remove;
885                        }
886                    }
887
888                    // Remove any previously selected roots that are descendants of this new (higher) removed path
889                    selected_roots.retain(|root, _| !is_descendant(root, removed));
890
891                    selected_roots
892                        .entry(&removed[..removed.len() - 1])
893                        .or_default()
894                        .insert(removed);
895                }
896            } else {
897                unreachable!()
898            }
899        }
900
901        // Traverse each chosen branch root and queue nested scopes
902        for (root, removed) in selected_roots {
903            scope.borrow_mut().nodes.retain(
904                root,
905                |p, _| !removed.contains(p),
906                |_, &PathNode { scope_id, node_id }| {
907                    if let Some(scope_id) = scope_id {
908                        // Queue scope to be removed
909                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
910                    } else {
911                        self.node_to_scope.remove(&node_id).unwrap();
912                    }
913                },
914            );
915        }
916
917        let mut scope_removal_queue = VecDeque::new();
918
919        while let Some(scope_rc) = scope_removal_buffer.pop() {
920            // Push the scopes to a queue that will remove
921            // them starting from the deepest to the highest ones
922            scope_removal_queue.push_front(scope_rc.clone());
923
924            let scope = scope_rc.borrow_mut();
925
926            let mut scope_root_node_id = None;
927
928            // Queue nested scopes to be removed
929            scope
930                .nodes
931                .traverse(&[], |path, &PathNode { scope_id, node_id }| {
932                    if let Some(scope_id) = scope_id {
933                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
934                    } else {
935                        self.node_to_scope.remove(&node_id).unwrap();
936                    }
937                    if path == [0] {
938                        scope_root_node_id = Some(node_id);
939                    }
940                });
941
942            // Nodes that have a scope id are components, so no need to mark those as removed in the tree
943            // Instead we get their root node id and remove it
944            mutations.removed.push(MutationRemove::Scope {
945                id: scope_root_node_id.unwrap(),
946            });
947        }
948
949        // Finally drops the scopes and their storage
950        for scope_rc in scope_removal_queue {
951            let scope = scope_rc.borrow_mut();
952
953            self.scopes.remove(&scope.id);
954
955            // Dropped hooks might e.g spawn forever tasks, so they need access to the context
956            CurrentContext::run_with_reactive(
957                CurrentContext {
958                    scope_id: scope.id,
959                    scopes_storages: self.scopes_storages.clone(),
960                    tasks: self.tasks.clone(),
961                    task_id_counter: self.task_id_counter.clone(),
962                    sender: self.sender.clone(),
963                },
964                || {
965                    // This is very important, the scope storage must be dropped after the borrow in `scopes_storages` has been released
966                    let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
967                },
968            );
969
970            // TODO: Scopes could also maintain its own registry of assigned tasks
971            self.tasks
972                .borrow_mut()
973                .retain(|_task_id, task| task.borrow().scope_id != scope.id);
974        }
975
976        // Given some additions like:
977        // [
978        //     [0,2],
979        //     [0,1,0,1],
980        //     [0,1,0,2],
981        //     [0,3],
982        //     [0,1,5,8],
983        // ]
984        //
985        // We want them ordered like:
986        // [
987        //     [0,1,0,1],
988        //     [0,1,0,2],
989        //     [0,1,5,8],
990        //     [0,2],
991        //     [0,3],
992        // ]
993        //
994        // This way, no addition offsets the next additions in line.
995        for added in diff
996            .added
997            .iter()
998            .sorted_by(|a, b| {
999                for (x, y) in a.iter().zip(b.iter()) {
1000                    match x.cmp(y) {
1001                        Ordering::Equal => continue,
1002                        non_eq => return non_eq.reverse(),
1003                    }
1004                }
1005                b.len().cmp(&a.len())
1006            })
1007            .rev()
1008        {
1009            let (parent_node_id, index_inside_parent) = if added.as_ref() == [0] {
1010                // For the [0] elements of scopes (their roots) we traverse recursively to ancestor
1011                // scopes until we find an element that does not a ScopeId in its path node,
1012                // or in other words its not a Component but an Element, this way we can figure out a suitable tree index
1013
1014                let mut index_inside_parent = 0;
1015
1016                let parent_id = scope.borrow().parent_id;
1017                let scope_id = scope.borrow().id;
1018                let parent_node_id = scope.borrow().parent_node_id_in_parent;
1019
1020                if let Some(parent_id) = parent_id {
1021                    let mut buffer = Some((parent_id, parent_node_id, scope_id));
1022                    while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
1023                        let parent_scope = self.scopes.get(&parent_id).unwrap();
1024                        let parent_scope = parent_scope.borrow();
1025
1026                        let scope = self.scopes.get(&scope_id).unwrap();
1027                        let scope = scope.borrow();
1028
1029                        let path_node_parent = parent_scope.nodes.find(|v| {
1030                            if let Some(v) = v {
1031                                v.node_id == parent_node_id
1032                            } else {
1033                                false
1034                            }
1035                        });
1036
1037                        if let Some(path_node_parent) = path_node_parent {
1038                            if let Some(scope_id) = path_node_parent.scope_id {
1039                                if let Some(parent_id) = parent_scope.parent_id {
1040                                    // The found element turns out to be a component so go to it to continue looking
1041                                    buffer.replace((
1042                                        parent_id,
1043                                        parent_scope.parent_node_id_in_parent,
1044                                        scope_id,
1045                                    ));
1046                                } else {
1047                                    assert_eq!(scope_id, ScopeId::ROOT);
1048                                }
1049                            } else {
1050                                // Found an Element parent so we get the index from the path
1051                                index_inside_parent = *scope.path_in_parent.last().unwrap();
1052                            }
1053                        } else if let Some(new_parent_id) = parent_scope.parent_id {
1054                            // If no element was found we go to the parent scope
1055                            buffer.replace((
1056                                new_parent_id,
1057                                parent_scope.parent_node_id_in_parent,
1058                                parent_id,
1059                            ));
1060                        }
1061                    }
1062                } else {
1063                    assert_eq!(scope_id, ScopeId::ROOT);
1064                }
1065
1066                (parent_node_id, index_inside_parent)
1067            } else {
1068                // Only do it for non-scope-roots because the root is is always in the same postion therefore it doesnt make sense to resync from its parent
1069                parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
1070                (
1071                    scope
1072                        .borrow()
1073                        .nodes
1074                        .get(&added[..added.len() - 1])
1075                        .unwrap()
1076                        .node_id,
1077                    added[added.len() - 1],
1078                )
1079            };
1080
1081            self.node_id_counter += 1;
1082
1083            path_element.with_element(added, |element| match element {
1084                PathElement::Component { .. } => {
1085                    self.scope_id_counter += 1;
1086                    let scope_id = self.scope_id_counter;
1087
1088                    scope.borrow_mut().nodes.insert(
1089                        added,
1090                        PathNode {
1091                            node_id: self.node_id_counter,
1092                            scope_id: Some(scope_id),
1093                        },
1094                    );
1095                }
1096                PathElement::Element { element, .. } => {
1097                    mutations.added.push((
1098                        self.node_id_counter,
1099                        parent_node_id,
1100                        index_inside_parent,
1101                        element.clone(),
1102                    ));
1103
1104                    self.node_to_scope
1105                        .insert(self.node_id_counter, scope.borrow().id);
1106                    scope.borrow_mut().nodes.insert(
1107                        added,
1108                        PathNode {
1109                            node_id: self.node_id_counter,
1110                            scope_id: None,
1111                        },
1112                    );
1113                }
1114            });
1115        }
1116
1117        for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1118            for (x, y) in a.iter().zip(b.iter()) {
1119                match x.cmp(y) {
1120                    Ordering::Equal => continue,
1121                    non_eq => return non_eq.reverse(),
1122                }
1123            }
1124            b.len().cmp(&a.len())
1125        }) {
1126            parents_to_resync_scopes.insert(parent.clone());
1127
1128            let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1129
1130            for (from, to) in movements.into_iter().sorted_by_key(|e| e.0).rev() {
1131                let path_node = paths.remove(&from).unwrap();
1132
1133                let PathNode { node_id, scope_id } = path_node;
1134
1135                // Search for this moved node current position
1136                let from_path = scope
1137                    .borrow()
1138                    .nodes
1139                    .find_child_path(&parent, |v| v == Some(&path_node))
1140                    .unwrap();
1141
1142                let mut to_path = parent.to_vec();
1143                to_path.push(to);
1144
1145                if from_path == to_path {
1146                    continue;
1147                }
1148
1149                // Remove the node from the old position and add it to the new one
1150                let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1151                scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1152
1153                if let Some(scope_id) = scope_id {
1154                    let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1155
1156                    let scope = scope_rc.borrow();
1157
1158                    let scope_root_node_id = scope.nodes.get(&[0]).map(|node| node.node_id);
1159
1160                    // Mark the scope root node id as moved
1161                    mutations
1162                        .moved
1163                        .entry(scope.parent_node_id_in_parent)
1164                        .or_default()
1165                        .push((to, scope_root_node_id.unwrap()));
1166                } else {
1167                    // Mark the element as moved
1168                    mutations
1169                        .moved
1170                        .entry(*parent_node_id)
1171                        .or_default()
1172                        .push((to, node_id));
1173                }
1174            }
1175        }
1176
1177        for (modified, flags) in diff.modified {
1178            path_element.with_element(&modified, |element| match element {
1179                PathElement::Component { .. } => {
1180                    // Components never change when being diffed
1181                    unreachable!()
1182                }
1183                PathElement::Element { element, .. } => {
1184                    let node_id = scope
1185                        .borrow()
1186                        .nodes
1187                        .get(&modified)
1188                        .map(|path_node| path_node.node_id)
1189                        .unwrap();
1190                    mutations.modified.push((node_id, element.clone(), flags));
1191                }
1192            });
1193        }
1194
1195        // When a parent gets a new child, or a child is removed or moved we
1196        // resync its 1 level children scopes with their new path
1197        for parent in parents_to_resync_scopes {
1198            // But only if the parent already existed before otherwise its pointless
1199            // as Scopes will be created with the latest path already
1200            if diff.added.contains(&parent) {
1201                // TODO: Maybe do this check before inserting
1202                continue;
1203            }
1204
1205            // Update all the nested scopes in this Scope with their up to date paths
1206            scope
1207                .borrow_mut()
1208                .nodes
1209                .traverse_1_level(&parent, |p, path_node| {
1210                    if let Some(scope_id) = path_node.scope_id
1211                        && let Some(scope_rc) = self.scopes.get(&scope_id)
1212                    {
1213                        let mut scope = scope_rc.borrow_mut();
1214                        scope.path_in_parent = Box::from(p);
1215                    }
1216                });
1217        }
1218    }
1219}
1220
1221#[derive(Default, Debug)]
1222pub struct Diff {
1223    pub added: Vec<Box<[u32]>>,
1224
1225    pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1226
1227    pub removed: Vec<Box<[u32]>>,
1228
1229    pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1230}
1231
1232fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1233    if ancestor.len() > candidate.len() {
1234        return false;
1235    }
1236    candidate[..ancestor.len()] == *ancestor
1237}