Skip to main content

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