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