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