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