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