Skip to main content

freya_core/
runner.rs

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