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 Element { id: NodeId, index: u32 },
61 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 (Element { index: a, .. }, Element { index: b, .. }) => b.cmp(a),
77 (Element { .. }, Scope { .. }) => Ordering::Less,
79 (Scope { .. }, Element { .. }) => Ordering::Greater,
80 (Scope { id: a }, Scope { id: b }) => b.cmp(a),
82 }
83 }
84}
85
86impl MutationRemove {
87 pub fn node_id(&self) -> NodeId {
88 match self {
89 Self::Element { id, .. } => *id,
90 Self::Scope { id } => *id,
91 }
92 }
93}
94
95#[derive(Default)]
96pub struct Mutations {
97 pub added: Vec<(NodeId, NodeId, u32, Rc<dyn ElementExt>)>,
98 pub modified: Vec<(NodeId, Rc<dyn ElementExt>, DiffModifies)>,
99 pub removed: Vec<MutationRemove>,
100 pub moved: HashMap<NodeId, Vec<(u32, NodeId)>>,
101}
102
103impl Debug for Mutations {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.write_fmt(format_args!(
106 "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
107 self.added
108 .iter()
109 .map(|(a, b, c, _)| (*a, *b, *c))
110 .collect::<Vec<_>>(),
111 self.modified.iter().map(|(a, _, _)| *a).collect::<Vec<_>>(),
112 self.removed,
113 self.moved
114 ))
115 }
116}
117
118pub enum Message {
119 MarkScopeAsDirty(ScopeId),
120 PollTask(TaskId),
121}
122
123pub struct Runner {
124 pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
125 pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
126
127 pub(crate) dirty_scopes: FxHashSet<ScopeId>,
128 pub(crate) dirty_tasks: VecDeque<TaskId>,
129
130 pub node_to_scope: FxHashMap<NodeId, ScopeId>,
131
132 pub(crate) node_id_counter: NodeId,
133 pub(crate) scope_id_counter: ScopeId,
134 pub(crate) task_id_counter: Rc<AtomicU64>,
135
136 pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
137
138 pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
139 pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
140}
141
142impl Debug for Runner {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 f.debug_struct("Runner")
145 .field("dirty_scopes", &self.dirty_scopes.len())
146 .field("dirty_tasks", &self.dirty_tasks.len())
147 .field("node_to_scope", &self.node_to_scope.len())
148 .field("scopes", &self.scopes.len())
149 .field("scopes_storages", &self.scopes_storages.borrow().len())
150 .field("tasks", &self.tasks.borrow().len())
151 .finish()
152 }
153}
154
155impl Drop for Runner {
156 fn drop(&mut self) {
157 for (scope_id, _scope) in self
159 .scopes
160 .drain()
161 .sorted_by_key(|s| s.1.borrow().height)
162 .rev()
163 {
164 CurrentContext::run_with_reactive(
165 CurrentContext {
166 scope_id,
167 scopes_storages: self.scopes_storages.clone(),
168 tasks: self.tasks.clone(),
169 task_id_counter: self.task_id_counter.clone(),
170 sender: self.sender.clone(),
171 },
172 || {
173 let mut _removed_tasks = Vec::new();
174
175 self.tasks.borrow_mut().retain(|task_id, task| {
176 if task.borrow().scope_id == scope_id {
177 _removed_tasks.push((*task_id, task.clone()));
178 false
179 } else {
180 true
181 }
182 });
183 drop(_removed_tasks);
184 let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
185 },
186 );
187 }
188 }
189}
190
191impl Runner {
192 pub fn new(root: impl Fn() -> Element + 'static) -> Self {
193 let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
194 Self {
195 scopes: FxHashMap::from_iter([(
196 ScopeId::ROOT,
197 Rc::new(RefCell::new(Scope {
198 parent_node_id_in_parent: NodeId::ROOT,
199 path_in_parent: Box::from([]),
200 height: 0,
201 parent_id: None,
202 id: ScopeId::ROOT,
203 key: DiffKey::Root,
204 comp: Rc::new(move |_| root()),
205 props: Rc::new(()),
206 element: None,
207 nodes: {
208 let mut map = PathGraph::new();
209 map.insert(
210 &[],
211 PathNode {
212 node_id: NodeId::ROOT,
213 scope_id: None,
214 },
215 );
216 map
217 },
218 })),
219 )]),
220 scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
221 ScopeId::ROOT,
222 ScopeStorage::new(None, |writer| {
223 ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
224 }),
225 )]))),
226
227 node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
228
229 node_id_counter: NodeId::ROOT,
230 scope_id_counter: ScopeId::ROOT,
231 task_id_counter: Rc::default(),
232
233 dirty_tasks: VecDeque::default(),
234 dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
235
236 tasks: Rc::default(),
237
238 sender,
239 receiver,
240 }
241 }
242
243 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
244 #[cfg_attr(feature = "hotpath", hotpath::measure)]
245 pub fn verify_scopes_integrity(&self) {
246 let mut visited = FxHashSet::default();
247 let size = self.scopes.len();
248 let mut buffer = vec![ScopeId::ROOT];
249 while let Some(scope_id) = buffer.pop() {
250 if visited.contains(&scope_id) {
251 continue;
252 }
253 visited.insert(scope_id);
254 let scope = self.scopes.get(&scope_id).unwrap();
255 let scope = scope.borrow();
256 if let Some(parent) = scope.parent_id {
257 buffer.push(parent);
258 }
259 scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
260 if let Some(scope_id) = scope_id {
261 buffer.push(scope_id);
262 }
263 });
264 }
265 assert_eq!(size, visited.len())
266 }
267
268 pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
269 CurrentContext::run(
270 CurrentContext {
271 scope_id: ScopeId::ROOT,
272 scopes_storages: self.scopes_storages.clone(),
273 tasks: self.tasks.clone(),
274 task_id_counter: self.task_id_counter.clone(),
275 sender: self.sender.clone(),
276 },
277 move || {
278 let context = context();
279 let mut scopes_storages = self.scopes_storages.borrow_mut();
280 let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
281 root_scope_storage
282 .contexts
283 .insert(TypeId::of::<T>(), Rc::new(context.clone()));
284
285 context
286 },
287 )
288 }
289
290 pub fn handle_event(
291 &mut self,
292 node_id: impl Into<NodeId>,
293 event_name: EventName,
294 event_type: EventType,
295 bubbles: bool,
296 ) -> bool {
297 let node_id = node_id.into();
298 #[cfg(debug_assertions)]
299 tracing::info!("Handling event {event_name:?} for {node_id:?}");
300 let propagate = Rc::new(RefCell::new(bubbles));
301 let default = Rc::new(RefCell::new(true));
302
303 let Some(scope_id) = self.node_to_scope.get(&node_id) else {
304 return false;
305 };
306 let Some(path) = self
307 .scopes
308 .get(scope_id)
309 .unwrap()
310 .borrow()
311 .nodes
312 .find_path(|value| {
313 value
314 == Some(&PathNode {
315 node_id,
316 scope_id: None,
317 })
318 })
319 else {
320 return false;
321 };
322
323 let mut current_target = Some((path, *scope_id));
324 while let Some((path, scope_id)) = current_target.take() {
325 let scope = self.scopes.get(&scope_id).cloned().unwrap();
326 scope.borrow().with_element(&path, |element| {
327 match element {
328 PathElement::Component { .. } => {
329 unreachable!()
330 }
331 PathElement::Element { element, .. } => {
332 CurrentContext::run(
333 CurrentContext {
334 scope_id,
335 scopes_storages: self.scopes_storages.clone(),
336 tasks: self.tasks.clone(),
337 task_id_counter: self.task_id_counter.clone(),
338 sender: self.sender.clone(),
339 },
340 || {
341 match &event_type {
342 EventType::Mouse(data) => {
343 let event_handlers = element.events_handlers();
344 if let Some(event_handlers) = event_handlers {
345 match event_handlers.get(&event_name) {
346 Some(EventHandlerType::Mouse(handler)) => {
347 handler.call(Event {
348 data: data.clone(),
349 propagate: propagate.clone(),
350 default: default.clone(),
351 });
352 }
353 Some(_) => unreachable!(),
354 _ => {}
355 }
356 }
357 }
358 EventType::Keyboard(data) => {
359 let event_handlers = element.events_handlers();
360 if let Some(event_handlers) = event_handlers {
361 match event_handlers.get(&event_name) {
362 Some(EventHandlerType::Keyboard(handler)) => {
363 handler.call(Event {
364 data: data.clone(),
365 propagate: propagate.clone(),
366 default: default.clone(),
367 });
368 }
369 Some(_) => unreachable!(),
370 _ => {}
371 }
372 }
373 }
374 EventType::Sized(data) => {
375 let event_handlers = element.events_handlers();
376 if let Some(event_handlers) = event_handlers {
377 match event_handlers.get(&event_name) {
378 Some(EventHandlerType::Sized(handler)) => {
379 handler.call(Event {
380 data: data.clone(),
381 propagate: propagate.clone(),
382 default: default.clone(),
383 });
384 }
385 Some(_) => unreachable!(),
386 _ => {}
387 }
388 }
389 }
390 EventType::Wheel(data) => {
391 let event_handlers = element.events_handlers();
392 if let Some(event_handlers) = event_handlers {
393 match event_handlers.get(&event_name) {
394 Some(EventHandlerType::Wheel(handler)) => {
395 handler.call(Event {
396 data: data.clone(),
397 propagate: propagate.clone(),
398 default: default.clone(),
399 });
400 }
401 Some(_) => unreachable!(),
402 _ => {}
403 }
404 }
405 }
406 EventType::Touch(data) => {
407 let event_handlers = element.events_handlers();
408 if let Some(event_handlers) = event_handlers {
409 match event_handlers.get(&event_name) {
410 Some(EventHandlerType::Touch(handler)) => {
411 handler.call(Event {
412 data: data.clone(),
413 propagate: propagate.clone(),
414 default: default.clone(),
415 });
416 }
417 Some(_) => unreachable!(),
418 _ => {}
419 }
420 }
421 }
422 EventType::Pointer(data) => {
423 let event_handlers = element.events_handlers();
424 if let Some(event_handlers) = event_handlers {
425 match event_handlers.get(&event_name) {
426 Some(EventHandlerType::Pointer(handler)) => {
427 handler.call(Event {
428 data: data.clone(),
429 propagate: propagate.clone(),
430 default: default.clone(),
431 });
432 }
433 Some(_) => unreachable!(),
434 _ => {}
435 }
436 }
437 }
438 EventType::File(data) => {
439 let event_handlers = element.events_handlers();
440 if let Some(event_handlers) = event_handlers {
441 match event_handlers.get(&event_name) {
442 Some(EventHandlerType::File(handler)) => {
443 handler.call(Event {
444 data: data.clone(),
445 propagate: propagate.clone(),
446 default: default.clone(),
447 });
448 }
449 Some(_) => unreachable!(),
450 _ => {}
451 }
452 }
453 }
454 EventType::ImePreedit(data) => {
455 let event_handlers = element.events_handlers();
456 if let Some(event_handlers) = event_handlers {
457 match event_handlers.get(&event_name) {
458 Some(EventHandlerType::ImePreedit(handler)) => {
459 handler.call(Event {
460 data: data.clone(),
461 propagate: propagate.clone(),
462 default: default.clone(),
463 });
464 }
465 Some(_) => unreachable!(),
466 _ => {}
467 }
468 }
469 }
470 }
471
472 if *propagate.borrow() {
474 if path.len() > 1 {
475 current_target
477 .replace((path[..path.len() - 1].to_vec(), scope_id));
478 } else {
479 let mut parent_scope_id = scope.borrow().parent_id;
480 loop {
482 if let Some(parent_id) = parent_scope_id.take() {
483 let parent_scope =
484 self.scopes.get(&parent_id).unwrap();
485 let path = parent_scope.borrow().nodes.find_path(
486 |value| {
487 value
488 == Some(&PathNode {
489 node_id: scope
490 .borrow()
491 .parent_node_id_in_parent,
492 scope_id: None,
493 })
494 },
495 );
496 if let Some(path) = path {
497 current_target.replace((path, parent_id));
498 break;
499 } else {
500 parent_scope_id =
501 parent_scope.borrow().parent_id;
502 }
503 } else {
504 return;
505 }
506 }
507 }
508 }
509 },
510 )
511 }
512 }
513 });
514 }
515 *default.borrow()
516 }
517
518 #[cfg_attr(feature = "hotpath", hotpath::measure)]
519 pub async fn handle_events(&mut self) {
520 loop {
521 while let Ok(msg) = self.receiver.try_recv() {
522 match msg {
523 Message::MarkScopeAsDirty(scope_id) => {
524 self.dirty_scopes.insert(scope_id);
525 }
526 Message::PollTask(task_id) => {
527 self.dirty_tasks.push_back(task_id);
528 }
529 }
530 }
531
532 if !self.dirty_scopes.is_empty() {
533 return;
534 }
535
536 while let Some(task_id) = self.dirty_tasks.pop_front() {
537 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
538 continue;
539 };
540 let mut task = task.borrow_mut();
541 let waker = task.waker.clone();
542
543 let mut cx = std::task::Context::from_waker(&waker);
544
545 CurrentContext::run(
546 {
547 let Some(scope) = self.scopes.get(&task.scope_id) else {
548 continue;
549 };
550 CurrentContext {
551 scope_id: scope.borrow().id,
552 scopes_storages: self.scopes_storages.clone(),
553 tasks: self.tasks.clone(),
554 task_id_counter: self.task_id_counter.clone(),
555 sender: self.sender.clone(),
556 }
557 },
558 || {
559 let poll_result = task.future.poll(&mut cx);
560 if poll_result.is_ready() {
561 let _ = self.tasks.borrow_mut().remove(&task_id);
562 }
563 },
564 );
565 }
566
567 if !self.dirty_scopes.is_empty() {
568 return;
569 }
570
571 while let Some(msg) = self.receiver.next().await {
572 match msg {
573 Message::MarkScopeAsDirty(scope_id) => {
574 self.dirty_scopes.insert(scope_id);
575 }
576 Message::PollTask(task_id) => {
577 self.dirty_tasks.push_back(task_id);
578 }
579 }
580 }
581 }
582 }
583
584 #[cfg_attr(feature = "hotpath", hotpath::measure)]
586 pub fn handle_events_immediately(&mut self) {
587 while let Ok(msg) = self.receiver.try_recv() {
588 match msg {
589 Message::MarkScopeAsDirty(scope_id) => {
590 self.dirty_scopes.insert(scope_id);
591 }
592 Message::PollTask(task_id) => {
593 self.dirty_tasks.push_back(task_id);
594 }
595 }
596 }
597
598 if !self.dirty_scopes.is_empty() {
599 return;
600 }
601
602 while let Some(task_id) = self.dirty_tasks.pop_front() {
604 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
605 continue;
606 };
607 let mut task = task.borrow_mut();
608 let waker = task.waker.clone();
609
610 let mut cx = std::task::Context::from_waker(&waker);
611
612 CurrentContext::run(
613 {
614 let Some(scope) = self.scopes.get(&task.scope_id) else {
615 continue;
616 };
617 CurrentContext {
618 scope_id: scope.borrow().id,
619 scopes_storages: self.scopes_storages.clone(),
620 tasks: self.tasks.clone(),
621 task_id_counter: self.task_id_counter.clone(),
622 sender: self.sender.clone(),
623 }
624 },
625 || {
626 let poll_result = task.future.poll(&mut cx);
627 if poll_result.is_ready() {
628 let _ = self.tasks.borrow_mut().remove(&task_id);
629 }
630 },
631 );
632 }
633 }
634
635 #[cfg_attr(feature = "hotpath", hotpath::measure)]
636 pub fn sync_and_update(&mut self) -> Mutations {
637 self.handle_events_immediately();
638 use itertools::Itertools;
639
640 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
641 self.verify_scopes_integrity();
642
643 let mut mutations = Mutations::default();
644
645 let dirty_scopes = self
646 .dirty_scopes
647 .drain()
648 .filter_map(|id| self.scopes.get(&id).cloned())
649 .sorted_by_key(|s| s.borrow().height)
650 .map(|s| s.borrow().id)
651 .collect::<Box<[_]>>();
652
653 let mut visited_scopes = FxHashSet::default();
654
655 for scope_id in dirty_scopes {
656 if visited_scopes.contains(&scope_id) {
658 continue;
659 }
660
661 let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
662 continue;
663 };
664
665 let scope_id = scope_rc.borrow().id;
666
667 let element = CurrentContext::run_with_reactive(
668 CurrentContext {
669 scope_id,
670 scopes_storages: self.scopes_storages.clone(),
671 tasks: self.tasks.clone(),
672 task_id_counter: self.task_id_counter.clone(),
673 sender: self.sender.clone(),
674 },
675 || {
676 let scope = scope_rc.borrow();
677 (scope.comp)(scope.props.clone())
678 },
679 );
680
681 let path_element = PathElement::from_element(vec![0], element);
682 let mut diff = Diff::default();
683 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
684
685 self.apply_diff(&scope_rc, diff, &mut mutations, &path_element);
686
687 self.run_scope(
688 &scope_rc,
689 &path_element,
690 &mut mutations,
691 &mut visited_scopes,
692 );
693
694 let mut scopes_storages = self.scopes_storages.borrow_mut();
695 let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
696 scope_storage.current_value = 0;
697 scope_storage.current_run += 1;
698
699 scope_rc.borrow_mut().element = Some(path_element);
700 }
701
702 mutations
703 }
704
705 pub fn run_in<T>(&self, run: impl FnOnce() -> T) -> T {
706 CurrentContext::run(
707 CurrentContext {
708 scope_id: ScopeId::ROOT,
709 scopes_storages: self.scopes_storages.clone(),
710 tasks: self.tasks.clone(),
711 task_id_counter: self.task_id_counter.clone(),
712 sender: self.sender.clone(),
713 },
714 run,
715 )
716 }
717
718 #[cfg_attr(feature = "hotpath", hotpath::measure)]
719 fn run_scope(
720 &mut self,
721 scope: &Rc<RefCell<Scope>>,
722 element: &PathElement,
723 mutations: &mut Mutations,
724 visited_scopes: &mut FxHashSet<ScopeId>,
725 ) {
726 visited_scopes.insert(scope.borrow().id);
727 match element {
728 PathElement::Component {
729 comp,
730 props,
731 key,
732 path,
733 } => {
734 let assigned_scope_id = scope
736 .borrow()
737 .nodes
738 .get(path)
739 .and_then(|path_node| path_node.scope_id)
740 .unwrap();
741
742 let parent_node_id = if path.as_ref() == [0] {
743 scope.borrow().parent_node_id_in_parent
744 } else {
745 scope
746 .borrow()
747 .nodes
748 .get(&path[..path.len() - 1])
749 .unwrap()
750 .node_id
751 };
752
753 if let Some(Ok(mut existing_scope)) = self
754 .scopes
755 .get(&assigned_scope_id)
756 .map(|s| s.try_borrow_mut())
757 {
758 let key_changed = existing_scope.key != *key;
759 if key_changed || existing_scope.props.changed(props.as_ref()) {
760 self.dirty_scopes.insert(assigned_scope_id);
761 existing_scope.props = props.clone();
762
763 if key_changed {
764 self.scopes_storages
765 .borrow_mut()
766 .get_mut(&assigned_scope_id)
767 .unwrap()
768 .reset();
769 }
770 }
771 } else {
772 self.scopes.insert(
773 assigned_scope_id,
774 Rc::new(RefCell::new(Scope {
775 parent_node_id_in_parent: parent_node_id,
776 path_in_parent: path.clone(),
777 height: scope.borrow().height + 1,
778 parent_id: Some(scope.borrow().id),
779 id: assigned_scope_id,
780 key: key.clone(),
781 comp: comp.clone(),
782 props: props.clone(),
783 element: None,
784 nodes: PathGraph::default(),
785 })),
786 );
787 self.scopes_storages.borrow_mut().insert(
788 assigned_scope_id,
789 ScopeStorage::new(Some(scope.borrow().id), |writer| {
790 ReactiveContext::new_for_scope(
791 self.sender.clone(),
792 assigned_scope_id,
793 writer,
794 )
795 }),
796 );
797 self.dirty_scopes.insert(assigned_scope_id);
798 }
799
800 let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
801
802 if !was_dirty {
803 return;
805 }
806
807 let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
808
809 let element = hotpath::measure_block!("Scope Rendering", {
810 CurrentContext::run_with_reactive(
811 CurrentContext {
812 scope_id: assigned_scope_id,
813 scopes_storages: self.scopes_storages.clone(),
814 tasks: self.tasks.clone(),
815 task_id_counter: self.task_id_counter.clone(),
816 sender: self.sender.clone(),
817 },
818 || {
819 let scope = scope_rc.borrow();
820 (scope.comp)(scope.props.clone())
821 },
822 )
823 });
824
825 let path_element = PathElement::from_element(vec![0], element);
826 let mut diff = Diff::default();
827 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
828
829 self.apply_diff(&scope_rc, diff, mutations, &path_element);
830
831 self.run_scope(&scope_rc, &path_element, mutations, visited_scopes);
832 let mut scopes_storages = self.scopes_storages.borrow_mut();
833 let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
834 scope_storage.current_value = 0;
835 scope_storage.current_run += 1;
836
837 scope_rc.borrow_mut().element = Some(path_element);
838 }
839 PathElement::Element { elements, .. } => {
840 for element in elements.iter() {
841 self.run_scope(scope, element, mutations, visited_scopes);
842 }
843 }
844 }
845 }
846
847 fn find_scope_root_parent_info(
850 &self,
851 parent_id: Option<ScopeId>,
852 parent_node_id: NodeId,
853 scope_id: ScopeId,
854 ) -> (NodeId, u32) {
855 let mut index_inside_parent = 0;
856
857 if let Some(parent_id) = parent_id {
858 let mut buffer = Some((parent_id, parent_node_id, scope_id));
859 while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
860 let parent_scope = self.scopes.get(&parent_id).unwrap();
861 let parent_scope = parent_scope.borrow();
862
863 let scope = self.scopes.get(&scope_id).unwrap();
864 let scope = scope.borrow();
865
866 let path_node_parent = parent_scope.nodes.find(|v| {
867 if let Some(v) = v {
868 v.node_id == parent_node_id
869 } else {
870 false
871 }
872 });
873
874 if let Some(path_node_parent) = path_node_parent {
875 if let Some(scope_id) = path_node_parent.scope_id {
876 if let Some(parent_id) = parent_scope.parent_id {
877 buffer.replace((
879 parent_id,
880 parent_scope.parent_node_id_in_parent,
881 scope_id,
882 ));
883 } else {
884 assert_eq!(scope_id, ScopeId::ROOT);
885 }
886 } else {
887 index_inside_parent = *scope.path_in_parent.last().unwrap();
889 return (parent_node_id, index_inside_parent);
890 }
891 } else if let Some(new_parent_id) = parent_scope.parent_id {
892 buffer.replace((
894 new_parent_id,
895 parent_scope.parent_node_id_in_parent,
896 parent_id,
897 ));
898 }
899 }
900 } else {
901 assert_eq!(scope_id, ScopeId::ROOT);
902 }
903
904 (parent_node_id, index_inside_parent)
905 }
906
907 #[cfg_attr(feature = "hotpath", hotpath::measure)]
908 fn apply_diff(
909 &mut self,
910 scope: &Rc<RefCell<Scope>>,
911 diff: Diff,
912 mutations: &mut Mutations,
913 path_element: &PathElement,
914 ) {
915 let mut moved_nodes =
916 FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
917 let mut parents_to_resync_scopes = FxHashSet::default();
918
919 for (parent, movements) in &diff.moved {
922 parents_to_resync_scopes.insert(parent.clone());
923 let old_parent = resolve_old_path(parent, &diff.moved);
926 let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
927 let parent_node_id = scope.borrow().nodes.get(&old_parent).unwrap().node_id;
928 (parent_node_id, FxHashMap::default())
929 });
930
931 for (from, _to) in movements.iter() {
932 let mut old_child_path = old_parent.clone();
933 old_child_path.push(*from);
934
935 let path_node = scope.borrow().nodes.get(&old_child_path).cloned().unwrap();
936
937 paths.1.insert(*from, path_node);
938 }
939 }
940
941 let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
943 let mut scope_removal_buffer = vec![];
944
945 'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
965 for (x, y) in a.iter().zip(b.iter()) {
966 match x.cmp(y) {
967 Ordering::Equal => continue,
968 non_eq => return non_eq.reverse(),
969 }
970 }
971 b.len().cmp(&a.len())
972 }) {
973 parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
974
975 let path_node = scope.borrow().nodes.get(removed).cloned();
976 if let Some(PathNode { node_id, scope_id }) = path_node {
977 if scope_id.is_none() {
978 let index_inside_parent = if removed.as_ref() == [0] {
979 let parent_id = scope.borrow().parent_id;
980 let scope_id = scope.borrow().id;
981 let parent_node_id = scope.borrow().parent_node_id_in_parent;
982 self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
983 .1
984 } else {
985 removed[removed.len() - 1]
987 };
988
989 mutations.removed.push(MutationRemove::Element {
991 id: node_id,
992 index: index_inside_parent,
993 });
994 }
995
996 for (root, inner) in &mut selected_roots {
998 if is_descendant(removed, root) {
999 inner.insert(removed);
1000 continue 'remove;
1001 }
1002 }
1003
1004 selected_roots.retain(|root, _| !is_descendant(root, removed));
1006
1007 selected_roots
1008 .entry(&removed[..removed.len() - 1])
1009 .or_default()
1010 .insert(removed);
1011 } else {
1012 unreachable!()
1013 }
1014 }
1015
1016 for (root, removed) in selected_roots.iter().sorted_by(|(a, _), (b, _)| {
1018 for (x, y) in a.iter().zip(b.iter()) {
1019 match x.cmp(y) {
1020 Ordering::Equal => continue,
1021 non_eq => return non_eq.reverse(),
1022 }
1023 }
1024 b.len().cmp(&a.len())
1025 }) {
1026 scope.borrow_mut().nodes.retain(
1027 root,
1028 |p, _| !removed.contains(p),
1029 |_, &PathNode { scope_id, node_id }| {
1030 if let Some(scope_id) = scope_id {
1031 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1033 } else {
1034 self.node_to_scope.remove(&node_id).unwrap();
1035 }
1036 },
1037 );
1038 }
1039
1040 let mut scope_removal_queue = VecDeque::new();
1041
1042 while let Some(scope_rc) = scope_removal_buffer.pop() {
1043 scope_removal_queue.push_front(scope_rc.clone());
1046
1047 let scope = scope_rc.borrow_mut();
1048
1049 let mut scope_root_node_id = None;
1050
1051 scope
1053 .nodes
1054 .traverse(&[], |path, &PathNode { scope_id, node_id }| {
1055 if let Some(scope_id) = scope_id {
1056 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
1057 } else {
1058 self.node_to_scope.remove(&node_id).unwrap();
1059 }
1060 if path == [0] {
1061 scope_root_node_id = Some(node_id);
1062 }
1063 });
1064
1065 mutations.removed.push(MutationRemove::Scope {
1068 id: scope_root_node_id.unwrap(),
1069 });
1070 }
1071
1072 for scope_rc in scope_removal_queue {
1074 let scope = scope_rc.borrow_mut();
1075
1076 self.scopes.remove(&scope.id);
1077
1078 CurrentContext::run_with_reactive(
1080 CurrentContext {
1081 scope_id: scope.id,
1082 scopes_storages: self.scopes_storages.clone(),
1083 tasks: self.tasks.clone(),
1084 task_id_counter: self.task_id_counter.clone(),
1085 sender: self.sender.clone(),
1086 },
1087 || {
1088 let mut _removed_tasks = Vec::new();
1090
1091 self.tasks.borrow_mut().retain(|task_id, task| {
1092 if task.borrow().scope_id == scope.id {
1093 _removed_tasks.push((*task_id, task.clone()));
1094 false
1095 } else {
1096 true
1097 }
1098 });
1099 drop(_removed_tasks);
1100 let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
1102 },
1103 );
1104 }
1105
1106 for added in diff
1126 .added
1127 .iter()
1128 .sorted_by(|a, b| {
1129 for (x, y) in a.iter().zip(b.iter()) {
1130 match x.cmp(y) {
1131 Ordering::Equal => continue,
1132 non_eq => return non_eq.reverse(),
1133 }
1134 }
1135 b.len().cmp(&a.len())
1136 })
1137 .rev()
1138 {
1139 let (parent_node_id, index_inside_parent) = if added.as_ref() == [0] {
1140 let parent_id = scope.borrow().parent_id;
1141 let scope_id = scope.borrow().id;
1142 let parent_node_id = scope.borrow().parent_node_id_in_parent;
1143 self.find_scope_root_parent_info(parent_id, parent_node_id, scope_id)
1144 } else {
1145 parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
1147 (
1148 scope
1149 .borrow()
1150 .nodes
1151 .get(&added[..added.len() - 1])
1152 .unwrap()
1153 .node_id,
1154 added[added.len() - 1],
1155 )
1156 };
1157
1158 self.node_id_counter += 1;
1159
1160 path_element.with_element(added, |element| match element {
1161 PathElement::Component { .. } => {
1162 self.scope_id_counter += 1;
1163 let scope_id = self.scope_id_counter;
1164
1165 scope.borrow_mut().nodes.insert(
1166 added,
1167 PathNode {
1168 node_id: self.node_id_counter,
1169 scope_id: Some(scope_id),
1170 },
1171 );
1172 }
1173 PathElement::Element { element, .. } => {
1174 mutations.added.push((
1175 self.node_id_counter,
1176 parent_node_id,
1177 index_inside_parent,
1178 element.clone(),
1179 ));
1180
1181 self.node_to_scope
1182 .insert(self.node_id_counter, scope.borrow().id);
1183 scope.borrow_mut().nodes.insert(
1184 added,
1185 PathNode {
1186 node_id: self.node_id_counter,
1187 scope_id: None,
1188 },
1189 );
1190 }
1191 });
1192 }
1193
1194 for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1195 for (x, y) in a.iter().zip(b.iter()) {
1196 match x.cmp(y) {
1197 Ordering::Equal => continue,
1198 non_eq => return non_eq.reverse(),
1199 }
1200 }
1201 a.len().cmp(&b.len())
1202 }) {
1203 parents_to_resync_scopes.insert(parent.clone());
1204
1205 let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1206
1207 for (from, to) in movements.into_iter().sorted_by_key(|e| e.0).rev() {
1208 let path_node = paths.remove(&from).unwrap();
1209
1210 let PathNode { node_id, scope_id } = path_node;
1211
1212 let from_path = scope
1214 .borrow()
1215 .nodes
1216 .find_child_path(&parent, |v| v == Some(&path_node))
1217 .unwrap();
1218
1219 let mut to_path = parent.to_vec();
1220 to_path.push(to);
1221
1222 if from_path == to_path {
1223 continue;
1224 }
1225
1226 let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1228 scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1229
1230 if let Some(scope_id) = scope_id {
1231 let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1232
1233 let scope = scope_rc.borrow();
1234
1235 let scope_root_node_id = scope.nodes.get(&[0]).map(|node| node.node_id);
1236
1237 mutations
1239 .moved
1240 .entry(scope.parent_node_id_in_parent)
1241 .or_default()
1242 .push((to, scope_root_node_id.unwrap()));
1243 } else {
1244 mutations
1246 .moved
1247 .entry(*parent_node_id)
1248 .or_default()
1249 .push((to, node_id));
1250 }
1251 }
1252 }
1253
1254 for (modified, flags) in diff.modified {
1255 path_element.with_element(&modified, |element| match element {
1256 PathElement::Component { .. } => {
1257 unreachable!()
1259 }
1260 PathElement::Element { element, .. } => {
1261 let node_id = scope
1262 .borrow()
1263 .nodes
1264 .get(&modified)
1265 .map(|path_node| path_node.node_id)
1266 .unwrap();
1267 mutations.modified.push((node_id, element.clone(), flags));
1268 }
1269 });
1270 }
1271
1272 for parent in parents_to_resync_scopes {
1275 if diff.added.contains(&parent) {
1278 continue;
1280 }
1281
1282 scope
1284 .borrow_mut()
1285 .nodes
1286 .traverse_1_level(&parent, |p, path_node| {
1287 if let Some(scope_id) = path_node.scope_id
1288 && let Some(scope_rc) = self.scopes.get(&scope_id)
1289 {
1290 let mut scope = scope_rc.borrow_mut();
1291 scope.path_in_parent = Box::from(p);
1292 }
1293 });
1294 }
1295 }
1296}
1297
1298#[derive(Default, Debug)]
1299pub struct Diff {
1300 pub added: Vec<Box<[u32]>>,
1301
1302 pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1303
1304 pub removed: Vec<Box<[u32]>>,
1305
1306 pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1307}
1308
1309fn resolve_old_path(new_path: &[u32], moved: &HashMap<Box<[u32]>, Vec<(u32, u32)>>) -> Vec<u32> {
1314 let mut old_path = Vec::with_capacity(new_path.len());
1315 for i in 0..new_path.len() {
1316 let new_parent = &new_path[..i];
1317 let new_index = new_path[i];
1318 if let Some(movements) = moved.get(new_parent)
1319 && let Some(&(from, _)) = movements.iter().find(|(_, to)| *to == new_index)
1320 {
1321 old_path.push(from);
1322 continue;
1323 }
1324 old_path.push(new_index);
1325 }
1326 old_path
1327}
1328
1329fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1330 if ancestor.len() > candidate.len() {
1331 return false;
1332 }
1333 candidate[..ancestor.len()] == *ancestor
1334}