freya_core/
runner.rs

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