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    #[cfg_attr(feature = "hotpath", hotpath::measure)]
908    fn apply_diff(
909        &mut self,
910        scope: &Rc<RefCell<Scope>>,
911        diff: Diff,
912        mutations: &mut Mutations,
913        path_element: &PathElement,
914    ) {
915        let mut moved_nodes =
916            FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
917        let mut parents_to_resync_scopes = FxHashSet::default();
918
919        // Store the moved nodes so that they can
920        // later be rearranged once the removals and additions have been done
921        for (parent, movements) in &diff.moved {
922            parents_to_resync_scopes.insert(parent.clone());
923            // `parent` is a new-tree path; if the parent itself was moved, its path in the
924            // old nodes tree will differ — resolve it before any lookup.
925            let old_parent = resolve_old_path(parent, &diff.moved);
926            let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
927                let parent_node_id = scope.borrow().nodes.get(&old_parent).unwrap().node_id;
928                (parent_node_id, FxHashMap::default())
929            });
930
931            for (from, _to) in movements.iter() {
932                let mut old_child_path = old_parent.clone();
933                old_child_path.push(*from);
934
935                let path_node = scope.borrow().nodes.get(&old_child_path).cloned().unwrap();
936
937                paths.1.insert(*from, path_node);
938            }
939        }
940
941        // Collect a set of branches to remove in cascade
942        let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
943        let mut scope_removal_buffer = vec![];
944
945        // Given some removals like:
946        // [
947        //     [0,2],
948        //     [0,1,0,1],
949        //     [0,1,0,2],
950        //     [0,3],
951        //     [0,1,5,8],
952        // ]
953        //
954        // We want them ordered like:
955        // [
956        //     [0,3],
957        //     [0,2],
958        //     [0,1,5,8],
959        //     [0,1,0,2],
960        //     [0,1,0,1],
961        // ]
962        //
963        // This way any removal does not move the next removals
964        'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
965            for (x, y) in a.iter().zip(b.iter()) {
966                match x.cmp(y) {
967                    Ordering::Equal => continue,
968                    non_eq => return non_eq.reverse(),
969                }
970            }
971            b.len().cmp(&a.len())
972        }) {
973            parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
974
975            let path_node = scope.borrow().nodes.get(removed).cloned();
976            if let Some(PathNode { node_id, scope_id }) = path_node {
977                if scope_id.is_none() {
978                    let index_inside_parent = if removed.as_ref() == [0] {
979                        let parent_id = scope.borrow().parent_id;
980                        let scope_id = scope.borrow().id;
981                        let parent_node_id = scope.borrow().parent_node_id_in_parent;
982                        self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
983                            .1
984                    } else {
985                        // 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
986                        removed[removed.len() - 1]
987                    };
988
989                    // plain element removal
990                    mutations.removed.push(MutationRemove::Element {
991                        id: node_id,
992                        index: index_inside_parent,
993                    });
994                }
995
996                // Skip if this removed path is already covered by a previously selected root
997                for (root, inner) in &mut selected_roots {
998                    if is_descendant(removed, root) {
999                        inner.insert(removed);
1000                        continue 'remove;
1001                    }
1002                }
1003
1004                // Remove any previously selected roots that are descendants of this new (higher) removed path
1005                selected_roots.retain(|root, _| !is_descendant(root, removed));
1006
1007                selected_roots
1008                    .entry(&removed[..removed.len() - 1])
1009                    .or_default()
1010                    .insert(removed);
1011            } else {
1012                unreachable!()
1013            }
1014        }
1015
1016        // Traverse each chosen branch root and queue nested scopes
1017        for (root, removed) in selected_roots.iter().sorted_by(|(a, _), (b, _)| {
1018            for (x, y) in a.iter().zip(b.iter()) {
1019                match x.cmp(y) {
1020                    Ordering::Equal => continue,
1021                    non_eq => return non_eq.reverse(),
1022                }
1023            }
1024            b.len().cmp(&a.len())
1025        }) {
1026            scope.borrow_mut().nodes.retain(
1027                root,
1028                |p, _| !removed.contains(p),
1029                |_, &PathNode { scope_id, node_id }| {
1030                    if let Some(scope_id) = scope_id {
1031                        // Queue scope to be removed
1032                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1033                    } else {
1034                        self.node_to_scope.remove(&node_id).unwrap();
1035                    }
1036                },
1037            );
1038        }
1039
1040        let mut scope_removal_queue = VecDeque::new();
1041
1042        while let Some(scope_rc) = scope_removal_buffer.pop() {
1043            // Push the scopes to a queue that will remove
1044            // them starting from the deepest to the highest ones
1045            scope_removal_queue.push_front(scope_rc.clone());
1046
1047            let scope = scope_rc.borrow_mut();
1048
1049            let mut scope_root_node_id = None;
1050
1051            // Queue nested scopes to be removed
1052            scope
1053                .nodes
1054                .traverse(&[], |path, &PathNode { scope_id, node_id }| {
1055                    if let Some(scope_id) = scope_id {
1056                        scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1057                    } else {
1058                        self.node_to_scope.remove(&node_id).unwrap();
1059                    }
1060                    if path == [0] {
1061                        scope_root_node_id = Some(node_id);
1062                    }
1063                });
1064
1065            // Nodes that have a scope id are components, so no need to mark those as removed in the tree
1066            // Instead we get their root node id and remove it
1067            mutations.removed.push(MutationRemove::Scope {
1068                id: scope_root_node_id.unwrap(),
1069            });
1070        }
1071
1072        // Finally drops the scopes and their storage
1073        for scope_rc in scope_removal_queue {
1074            let scope = scope_rc.borrow_mut();
1075
1076            self.scopes.remove(&scope.id);
1077
1078            // Dropped hooks might e.g spawn forever tasks, so they need access to the context
1079            CurrentContext::run_with_reactive(
1080                CurrentContext {
1081                    scope_id: scope.id,
1082                    scopes_storages: self.scopes_storages.clone(),
1083                    tasks: self.tasks.clone(),
1084                    task_id_counter: self.task_id_counter.clone(),
1085                    sender: self.sender.clone(),
1086                },
1087                || {
1088                    // TODO: Scopes could also maintain its own registry of assigned tasks
1089                    let mut _removed_tasks = Vec::new();
1090
1091                    self.tasks.borrow_mut().retain(|task_id, task| {
1092                        if task.borrow().scope_id == scope.id {
1093                            _removed_tasks.push((*task_id, task.clone()));
1094                            false
1095                        } else {
1096                            true
1097                        }
1098                    });
1099                    drop(_removed_tasks);
1100                    // This is very important, the scope storage must be dropped after the borrow in `scopes_storages` has been released
1101                    let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
1102                },
1103            );
1104        }
1105
1106        // Given some additions like:
1107        // [
1108        //     [0,2],
1109        //     [0,1,0,1],
1110        //     [0,1,0,2],
1111        //     [0,3],
1112        //     [0,1,5,8],
1113        // ]
1114        //
1115        // We want them ordered like:
1116        // [
1117        //     [0,1,0,1],
1118        //     [0,1,0,2],
1119        //     [0,1,5,8],
1120        //     [0,2],
1121        //     [0,3],
1122        // ]
1123        //
1124        // This way, no addition offsets the next additions in line.
1125        for added in diff
1126            .added
1127            .iter()
1128            .sorted_by(|a, b| {
1129                for (x, y) in a.iter().zip(b.iter()) {
1130                    match x.cmp(y) {
1131                        Ordering::Equal => continue,
1132                        non_eq => return non_eq.reverse(),
1133                    }
1134                }
1135                b.len().cmp(&a.len())
1136            })
1137            .rev()
1138        {
1139            let (parent_node_id, index_inside_parent) = if added.as_ref() == [0] {
1140                let parent_id = scope.borrow().parent_id;
1141                let scope_id = scope.borrow().id;
1142                let parent_node_id = scope.borrow().parent_node_id_in_parent;
1143                self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
1144            } else {
1145                // 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
1146                parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
1147                (
1148                    scope
1149                        .borrow()
1150                        .nodes
1151                        .get(&added[..added.len() - 1])
1152                        .unwrap()
1153                        .node_id,
1154                    added[added.len() - 1],
1155                )
1156            };
1157
1158            self.node_id_counter += 1;
1159
1160            path_element.with_element(added, |element| match element {
1161                PathElement::Component { .. } => {
1162                    self.scope_id_counter += 1;
1163                    let scope_id = self.scope_id_counter;
1164
1165                    scope.borrow_mut().nodes.insert(
1166                        added,
1167                        PathNode {
1168                            node_id: self.node_id_counter,
1169                            scope_id: Some(scope_id),
1170                        },
1171                    );
1172                }
1173                PathElement::Element { element, .. } => {
1174                    mutations.added.push((
1175                        self.node_id_counter,
1176                        parent_node_id,
1177                        index_inside_parent,
1178                        element.clone(),
1179                    ));
1180
1181                    self.node_to_scope
1182                        .insert(self.node_id_counter, scope.borrow().id);
1183                    scope.borrow_mut().nodes.insert(
1184                        added,
1185                        PathNode {
1186                            node_id: self.node_id_counter,
1187                            scope_id: None,
1188                        },
1189                    );
1190                }
1191            });
1192        }
1193
1194        for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1195            for (x, y) in a.iter().zip(b.iter()) {
1196                match x.cmp(y) {
1197                    Ordering::Equal => continue,
1198                    non_eq => return non_eq.reverse(),
1199                }
1200            }
1201            a.len().cmp(&b.len())
1202        }) {
1203            parents_to_resync_scopes.insert(parent.clone());
1204
1205            let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1206
1207            for (from, to) in movements.into_iter().sorted_by_key(|e| e.0).rev() {
1208                let path_node = paths.remove(&from).unwrap();
1209
1210                let PathNode { node_id, scope_id } = path_node;
1211
1212                // Search for this moved node current position
1213                let from_path = scope
1214                    .borrow()
1215                    .nodes
1216                    .find_child_path(&parent, |v| v == Some(&path_node))
1217                    .unwrap();
1218
1219                let mut to_path = parent.to_vec();
1220                to_path.push(to);
1221
1222                if from_path == to_path {
1223                    continue;
1224                }
1225
1226                // Remove the node from the old position and add it to the new one
1227                let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1228                scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1229
1230                if let Some(scope_id) = scope_id {
1231                    let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1232
1233                    let scope = scope_rc.borrow();
1234
1235                    let scope_root_node_id = scope.nodes.get(&[0]).map(|node| node.node_id);
1236
1237                    // Mark the scope root node id as moved
1238                    mutations
1239                        .moved
1240                        .entry(scope.parent_node_id_in_parent)
1241                        .or_default()
1242                        .push((to, scope_root_node_id.unwrap()));
1243                } else {
1244                    // Mark the element as moved
1245                    mutations
1246                        .moved
1247                        .entry(*parent_node_id)
1248                        .or_default()
1249                        .push((to, node_id));
1250                }
1251            }
1252        }
1253
1254        for (modified, flags) in diff.modified {
1255            path_element.with_element(&modified, |element| match element {
1256                PathElement::Component { .. } => {
1257                    // Components never change when being diffed
1258                    unreachable!()
1259                }
1260                PathElement::Element { element, .. } => {
1261                    let node_id = scope
1262                        .borrow()
1263                        .nodes
1264                        .get(&modified)
1265                        .map(|path_node| path_node.node_id)
1266                        .unwrap();
1267                    mutations.modified.push((node_id, element.clone(), flags));
1268                }
1269            });
1270        }
1271
1272        // When a parent gets a new child, or a child is removed or moved we
1273        // resync its 1 level children scopes with their new path
1274        for parent in parents_to_resync_scopes {
1275            // But only if the parent already existed before otherwise its pointless
1276            // as Scopes will be created with the latest path already
1277            if diff.added.contains(&parent) {
1278                // TODO: Maybe do this check before inserting
1279                continue;
1280            }
1281
1282            // Update all the nested scopes in this Scope with their up to date paths
1283            scope
1284                .borrow_mut()
1285                .nodes
1286                .traverse_1_level(&parent, |p, path_node| {
1287                    if let Some(scope_id) = path_node.scope_id
1288                        && let Some(scope_rc) = self.scopes.get(&scope_id)
1289                    {
1290                        let mut scope = scope_rc.borrow_mut();
1291                        scope.path_in_parent = Box::from(p);
1292                    }
1293                });
1294        }
1295    }
1296}
1297
1298#[derive(Default, Debug)]
1299pub struct Diff {
1300    pub added: Vec<Box<[u32]>>,
1301
1302    pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1303
1304    pub removed: Vec<Box<[u32]>>,
1305
1306    pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1307}
1308
1309/// Converts a new-tree path to its corresponding old-tree path by checking, for each
1310/// segment, whether that position was the destination of a movement in `moved`. If so,
1311/// the original (`from`) index is substituted so the result can be used to look up nodes
1312/// in the pre-diff nodes tree.
1313fn resolve_old_path(new_path: &[u32], moved: &HashMap<Box<[u32]>, Vec<(u32, u32)>>) -> Vec<u32> {
1314    let mut old_path = Vec::with_capacity(new_path.len());
1315    for i in 0..new_path.len() {
1316        let new_parent = &new_path[..i];
1317        let new_index = new_path[i];
1318        if let Some(movements) = moved.get(new_parent)
1319            && let Some(&(from, _)) = movements.iter().find(|(_, to)| *to == new_index)
1320        {
1321            old_path.push(from);
1322            continue;
1323        }
1324        old_path.push(new_index);
1325    }
1326    old_path
1327}
1328
1329fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1330    if ancestor.len() > candidate.len() {
1331        return false;
1332    }
1333    candidate[..ancestor.len()] == *ancestor
1334}