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)]
58pub enum MutationRemove {
59 Element { id: NodeId, index: u32 },
61 Scope { id: NodeId },
63}
64
65impl MutationRemove {
66 pub fn node_id(&self) -> NodeId {
67 match self {
68 Self::Element { id, .. } => *id,
69 Self::Scope { id } => *id,
70 }
71 }
72}
73
74#[derive(Default)]
75pub struct Mutations {
76 pub added: Vec<(NodeId, NodeId, u32, Rc<dyn ElementExt>)>,
77 pub modified: Vec<(NodeId, Rc<dyn ElementExt>, DiffModifies)>,
78 pub removed: Vec<MutationRemove>,
79 pub moved: HashMap<NodeId, Vec<(u32, NodeId)>>,
80}
81
82impl Debug for Mutations {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.write_fmt(format_args!(
85 "Added: {:?} Modified: {:?} Removed: {:?} Moved: {:?}",
86 self.added
87 .iter()
88 .map(|(a, b, c, _)| (*a, *b, *c))
89 .collect::<Vec<_>>(),
90 self.modified.iter().map(|(a, _, _)| *a).collect::<Vec<_>>(),
91 self.removed,
92 self.moved
93 ))
94 }
95}
96
97pub enum Message {
98 MarkScopeAsDirty(ScopeId),
99 PollTask(TaskId),
100}
101
102pub struct Runner {
103 pub scopes: FxHashMap<ScopeId, Rc<RefCell<Scope>>>,
104 pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
105
106 pub(crate) dirty_scopes: FxHashSet<ScopeId>,
107 pub(crate) dirty_tasks: VecDeque<TaskId>,
108
109 pub node_to_scope: FxHashMap<NodeId, ScopeId>,
110
111 pub(crate) node_id_counter: NodeId,
112 pub(crate) scope_id_counter: ScopeId,
113 pub(crate) task_id_counter: Rc<AtomicU64>,
114
115 pub(crate) tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
116
117 pub(crate) sender: futures_channel::mpsc::UnboundedSender<Message>,
118 pub(crate) receiver: futures_channel::mpsc::UnboundedReceiver<Message>,
119}
120
121impl Debug for Runner {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 f.debug_struct("Runner")
124 .field("dirty_scopes", &self.dirty_scopes.len())
125 .field("dirty_tasks", &self.dirty_tasks.len())
126 .field("node_to_scope", &self.node_to_scope.len())
127 .field("scopes", &self.scopes.len())
128 .field("scopes_storages", &self.scopes_storages.borrow().len())
129 .field("tasks", &self.tasks.borrow().len())
130 .finish()
131 }
132}
133
134impl Drop for Runner {
135 fn drop(&mut self) {
136 for (scope_id, _scope) in self
138 .scopes
139 .drain()
140 .sorted_by_key(|s| s.1.borrow().height)
141 .rev()
142 {
143 CurrentContext::run_with_reactive(
144 CurrentContext {
145 scope_id,
146 scopes_storages: self.scopes_storages.clone(),
147 tasks: self.tasks.clone(),
148 task_id_counter: self.task_id_counter.clone(),
149 sender: self.sender.clone(),
150 },
151 || {
152 let _scope = self.scopes_storages.borrow_mut().remove(&scope_id);
153 },
154 );
155 }
156 }
157}
158
159impl Runner {
160 pub fn new(root: impl Fn() -> Element + 'static) -> Self {
161 let (sender, receiver) = futures_channel::mpsc::unbounded::<Message>();
162 Self {
163 scopes: FxHashMap::from_iter([(
164 ScopeId::ROOT,
165 Rc::new(RefCell::new(Scope {
166 parent_node_id_in_parent: NodeId::ROOT,
167 path_in_parent: Box::from([]),
168 height: 0,
169 parent_id: None,
170 id: ScopeId::ROOT,
171 key: DiffKey::Root,
172 comp: Rc::new(move |_| root()),
173 props: Rc::new(()),
174 element: None,
175 nodes: {
176 let mut map = PathGraph::new();
177 map.insert(
178 &[],
179 PathNode {
180 node_id: NodeId::ROOT,
181 scope_id: None,
182 },
183 );
184 map
185 },
186 })),
187 )]),
188 scopes_storages: Rc::new(RefCell::new(FxHashMap::from_iter([(
189 ScopeId::ROOT,
190 ScopeStorage::new(None, |writer| {
191 ReactiveContext::new_for_scope(sender.clone(), ScopeId::ROOT, writer)
192 }),
193 )]))),
194
195 node_to_scope: FxHashMap::from_iter([(NodeId::ROOT, ScopeId::ROOT)]),
196
197 node_id_counter: NodeId::ROOT,
198 scope_id_counter: ScopeId::ROOT,
199 task_id_counter: Rc::default(),
200
201 dirty_tasks: VecDeque::default(),
202 dirty_scopes: FxHashSet::from_iter([ScopeId::ROOT]),
203
204 tasks: Rc::default(),
205
206 sender,
207 receiver,
208 }
209 }
210
211 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
212 #[cfg_attr(feature = "hotpath", hotpath::measure)]
213 pub fn verify_scopes_integrity(&self) {
214 let mut visited = FxHashSet::default();
215 let size = self.scopes.len();
216 let mut buffer = vec![ScopeId::ROOT];
217 while let Some(scope_id) = buffer.pop() {
218 if visited.contains(&scope_id) {
219 continue;
220 }
221 visited.insert(scope_id);
222 let scope = self.scopes.get(&scope_id).unwrap();
223 let scope = scope.borrow();
224 if let Some(parent) = scope.parent_id {
225 buffer.push(parent);
226 }
227 scope.nodes.traverse(&[], |_, &PathNode { scope_id, .. }| {
228 if let Some(scope_id) = scope_id {
229 buffer.push(scope_id);
230 }
231 });
232 }
233 assert_eq!(size, visited.len())
234 }
235
236 pub fn provide_root_context<T: 'static + Clone>(&mut self, context: impl FnOnce() -> T) -> T {
237 CurrentContext::run(
238 CurrentContext {
239 scope_id: ScopeId::ROOT,
240 scopes_storages: self.scopes_storages.clone(),
241 tasks: self.tasks.clone(),
242 task_id_counter: self.task_id_counter.clone(),
243 sender: self.sender.clone(),
244 },
245 move || {
246 let context = context();
247 let mut scopes_storages = self.scopes_storages.borrow_mut();
248 let root_scope_storage = scopes_storages.get_mut(&ScopeId::ROOT).unwrap();
249 root_scope_storage
250 .contexts
251 .insert(TypeId::of::<T>(), Rc::new(context.clone()));
252
253 context
254 },
255 )
256 }
257
258 pub fn handle_event(
259 &mut self,
260 node_id: impl Into<NodeId>,
261 event_name: EventName,
262 event_type: EventType,
263 bubbles: bool,
264 ) -> bool {
265 let node_id = node_id.into();
266 #[cfg(debug_assertions)]
267 tracing::info!("Handling event {event_name:?} for {node_id:?}");
268 let propagate = Rc::new(RefCell::new(bubbles));
269 let default = Rc::new(RefCell::new(true));
270
271 let Some(scope_id) = self.node_to_scope.get(&node_id) else {
272 return false;
273 };
274 let Some(path) = self
275 .scopes
276 .get(scope_id)
277 .unwrap()
278 .borrow()
279 .nodes
280 .find_path(|value| {
281 value
282 == Some(&PathNode {
283 node_id,
284 scope_id: None,
285 })
286 })
287 else {
288 return false;
289 };
290
291 let mut current_target = Some((path, *scope_id));
292 while let Some((path, scope_id)) = current_target.take() {
293 let scope = self.scopes.get(&scope_id).cloned().unwrap();
294 scope.borrow().with_element(&path, |element| {
295 match element {
296 PathElement::Component { .. } => {
297 unreachable!()
298 }
299 PathElement::Element { element, .. } => {
300 CurrentContext::run(
301 CurrentContext {
302 scope_id,
303 scopes_storages: self.scopes_storages.clone(),
304 tasks: self.tasks.clone(),
305 task_id_counter: self.task_id_counter.clone(),
306 sender: self.sender.clone(),
307 },
308 || {
309 match &event_type {
310 EventType::Mouse(data) => {
311 let event_handlers = element.events_handlers();
312 if let Some(event_handlers) = event_handlers {
313 match event_handlers.get(&event_name) {
314 Some(EventHandlerType::Mouse(handler)) => {
315 handler.call(Event {
316 data: data.clone(),
317 propagate: propagate.clone(),
318 default: default.clone(),
319 });
320 }
321 Some(_) => unreachable!(),
322 _ => {}
323 }
324 }
325 }
326 EventType::Keyboard(data) => {
327 let event_handlers = element.events_handlers();
328 if let Some(event_handlers) = event_handlers {
329 match event_handlers.get(&event_name) {
330 Some(EventHandlerType::Keyboard(handler)) => {
331 handler.call(Event {
332 data: data.clone(),
333 propagate: propagate.clone(),
334 default: default.clone(),
335 });
336 }
337 Some(_) => unreachable!(),
338 _ => {}
339 }
340 }
341 }
342 EventType::Sized(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::Sized(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::Wheel(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::Wheel(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::Touch(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::Touch(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::Pointer(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::Pointer(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::File(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::File(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::ImePreedit(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::ImePreedit(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 }
439
440 if *propagate.borrow() {
442 if path.len() > 1 {
443 current_target
445 .replace((path[..path.len() - 1].to_vec(), scope_id));
446 } else {
447 let mut parent_scope_id = scope.borrow().parent_id;
448 loop {
450 if let Some(parent_id) = parent_scope_id.take() {
451 let parent_scope =
452 self.scopes.get(&parent_id).unwrap();
453 let path = parent_scope.borrow().nodes.find_path(
454 |value| {
455 value
456 == Some(&PathNode {
457 node_id: scope
458 .borrow()
459 .parent_node_id_in_parent,
460 scope_id: None,
461 })
462 },
463 );
464 if let Some(path) = path {
465 current_target.replace((path, parent_id));
466 break;
467 } else {
468 parent_scope_id =
469 parent_scope.borrow().parent_id;
470 }
471 } else {
472 return;
473 }
474 }
475 }
476 }
477 },
478 )
479 }
480 }
481 });
482 }
483 *default.borrow()
484 }
485
486 #[cfg_attr(feature = "hotpath", hotpath::measure)]
487 pub async fn handle_events(&mut self) {
488 loop {
489 while let Ok(Some(msg)) = self.receiver.try_next() {
490 match msg {
491 Message::MarkScopeAsDirty(scope_id) => {
492 self.dirty_scopes.insert(scope_id);
493 }
494 Message::PollTask(task_id) => {
495 self.dirty_tasks.push_back(task_id);
496 }
497 }
498 }
499
500 if !self.dirty_scopes.is_empty() {
501 return;
502 }
503
504 while let Some(task_id) = self.dirty_tasks.pop_front() {
505 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
506 continue;
507 };
508 let mut task = task.borrow_mut();
509 let waker = task.waker.clone();
510
511 let mut cx = std::task::Context::from_waker(&waker);
512
513 CurrentContext::run(
514 {
515 let Some(scope) = self.scopes.get(&task.scope_id) else {
516 continue;
517 };
518 CurrentContext {
519 scope_id: scope.borrow().id,
520 scopes_storages: self.scopes_storages.clone(),
521 tasks: self.tasks.clone(),
522 task_id_counter: self.task_id_counter.clone(),
523 sender: self.sender.clone(),
524 }
525 },
526 || {
527 let poll_result = task.future.poll(&mut cx);
528 if poll_result.is_ready() {
529 self.tasks.borrow_mut().remove(&task_id);
530 }
531 },
532 );
533 }
534
535 if !self.dirty_scopes.is_empty() {
536 return;
537 }
538
539 while let Some(msg) = self.receiver.next().await {
540 match msg {
541 Message::MarkScopeAsDirty(scope_id) => {
542 self.dirty_scopes.insert(scope_id);
543 }
544 Message::PollTask(task_id) => {
545 self.dirty_tasks.push_back(task_id);
546 }
547 }
548 }
549 }
550 }
551
552 #[cfg_attr(feature = "hotpath", hotpath::measure)]
554 pub fn handle_events_immediately(&mut self) {
555 while let Ok(Some(msg)) = self.receiver.try_next() {
556 match msg {
557 Message::MarkScopeAsDirty(scope_id) => {
558 self.dirty_scopes.insert(scope_id);
559 }
560 Message::PollTask(task_id) => {
561 self.dirty_tasks.push_back(task_id);
562 }
563 }
564 }
565
566 if !self.dirty_scopes.is_empty() {
567 return;
568 }
569
570 while let Some(task_id) = self.dirty_tasks.pop_front() {
572 let Some(task) = self.tasks.borrow().get(&task_id).cloned() else {
573 continue;
574 };
575 let mut task = task.borrow_mut();
576 let waker = task.waker.clone();
577
578 let mut cx = std::task::Context::from_waker(&waker);
579
580 CurrentContext::run(
581 {
582 let Some(scope) = self.scopes.get(&task.scope_id) else {
583 continue;
584 };
585 CurrentContext {
586 scope_id: scope.borrow().id,
587 scopes_storages: self.scopes_storages.clone(),
588 tasks: self.tasks.clone(),
589 task_id_counter: self.task_id_counter.clone(),
590 sender: self.sender.clone(),
591 }
592 },
593 || {
594 let poll_result = task.future.poll(&mut cx);
595 if poll_result.is_ready() {
596 self.tasks.borrow_mut().remove(&task_id);
597 }
598 },
599 );
600 }
601 }
602
603 #[cfg_attr(feature = "hotpath", hotpath::measure)]
604 pub fn sync_and_update(&mut self) -> Mutations {
605 self.handle_events_immediately();
606 use itertools::Itertools;
607
608 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
609 self.verify_scopes_integrity();
610
611 let mut mutations = Mutations::default();
612
613 let dirty_scopes = self
614 .dirty_scopes
615 .drain()
616 .filter_map(|id| self.scopes.get(&id).cloned())
617 .sorted_by_key(|s| s.borrow().height)
618 .map(|s| s.borrow().id)
619 .collect::<Box<[_]>>();
620
621 let mut visited_scopes = FxHashSet::default();
622
623 for scope_id in dirty_scopes {
624 if visited_scopes.contains(&scope_id) {
626 continue;
627 }
628
629 let Some(scope_rc) = self.scopes.get(&scope_id).cloned() else {
630 continue;
631 };
632
633 let scope_id = scope_rc.borrow().id;
634
635 let element = CurrentContext::run_with_reactive(
636 CurrentContext {
637 scope_id,
638 scopes_storages: self.scopes_storages.clone(),
639 tasks: self.tasks.clone(),
640 task_id_counter: self.task_id_counter.clone(),
641 sender: self.sender.clone(),
642 },
643 || {
644 let scope = scope_rc.borrow();
645 (scope.comp)(scope.props.clone())
646 },
647 );
648
649 let path_element = PathElement::from_element(vec![0], element);
650 let mut diff = Diff::default();
651 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
652
653 self.apply_diff(&scope_rc, diff, &mut mutations, &path_element);
654
655 self.run_scope(
656 &scope_rc,
657 &path_element,
658 &mut mutations,
659 &mut visited_scopes,
660 );
661
662 let mut scopes_storages = self.scopes_storages.borrow_mut();
663 let scope_storage = scopes_storages.get_mut(&scope_rc.borrow().id).unwrap();
664 scope_storage.current_value = 0;
665 scope_storage.current_run += 1;
666
667 scope_rc.borrow_mut().element = Some(path_element);
668 }
669
670 mutations
671 }
672
673 #[cfg_attr(feature = "hotpath", hotpath::measure)]
674 fn run_scope(
675 &mut self,
676 scope: &Rc<RefCell<Scope>>,
677 element: &PathElement,
678 mutations: &mut Mutations,
679 visited_scopes: &mut FxHashSet<ScopeId>,
680 ) {
681 visited_scopes.insert(scope.borrow().id);
682 match element {
683 PathElement::Component {
684 comp,
685 props,
686 key,
687 path,
688 } => {
689 let assigned_scope_id = scope
691 .borrow()
692 .nodes
693 .get(path)
694 .and_then(|path_node| path_node.scope_id)
695 .unwrap();
696
697 let parent_node_id = if path.as_ref() == [0] {
698 scope.borrow().parent_node_id_in_parent
699 } else {
700 scope
701 .borrow()
702 .nodes
703 .get(&path[..path.len() - 1])
704 .unwrap()
705 .node_id
706 };
707
708 if let Some(Ok(mut existing_scope)) = self
709 .scopes
710 .get(&assigned_scope_id)
711 .map(|s| s.try_borrow_mut())
712 {
713 let key_changed = existing_scope.key != *key;
714 if key_changed || existing_scope.props.changed(props.as_ref()) {
715 self.dirty_scopes.insert(assigned_scope_id);
716 existing_scope.props = props.clone();
717
718 if key_changed {
719 self.scopes_storages
720 .borrow_mut()
721 .get_mut(&assigned_scope_id)
722 .unwrap()
723 .reset();
724 }
725 }
726 } else {
727 self.scopes.insert(
728 assigned_scope_id,
729 Rc::new(RefCell::new(Scope {
730 parent_node_id_in_parent: parent_node_id,
731 path_in_parent: path.clone(),
732 height: scope.borrow().height + 1,
733 parent_id: Some(scope.borrow().id),
734 id: assigned_scope_id,
735 key: key.clone(),
736 comp: comp.clone(),
737 props: props.clone(),
738 element: None,
739 nodes: PathGraph::default(),
740 })),
741 );
742 self.scopes_storages.borrow_mut().insert(
743 assigned_scope_id,
744 ScopeStorage::new(Some(scope.borrow().id), |writer| {
745 ReactiveContext::new_for_scope(
746 self.sender.clone(),
747 assigned_scope_id,
748 writer,
749 )
750 }),
751 );
752 self.dirty_scopes.insert(assigned_scope_id);
753 }
754
755 let was_dirty = self.dirty_scopes.remove(&assigned_scope_id);
756
757 if !was_dirty {
758 return;
760 }
761
762 let scope_rc = self.scopes.get(&assigned_scope_id).cloned().unwrap();
763
764 let element = hotpath::measure_block!("Scope Rendering", {
765 CurrentContext::run_with_reactive(
766 CurrentContext {
767 scope_id: assigned_scope_id,
768 scopes_storages: self.scopes_storages.clone(),
769 tasks: self.tasks.clone(),
770 task_id_counter: self.task_id_counter.clone(),
771 sender: self.sender.clone(),
772 },
773 || {
774 let scope = scope_rc.borrow();
775 (scope.comp)(scope.props.clone())
776 },
777 )
778 });
779
780 let path_element = PathElement::from_element(vec![0], element);
781 let mut diff = Diff::default();
782 path_element.diff(scope_rc.borrow().element.as_ref(), &mut diff);
783
784 self.apply_diff(&scope_rc, diff, mutations, &path_element);
785
786 self.run_scope(&scope_rc, &path_element, mutations, visited_scopes);
787 let mut scopes_storages = self.scopes_storages.borrow_mut();
788 let scope_storage = scopes_storages.get_mut(&assigned_scope_id).unwrap();
789 scope_storage.current_value = 0;
790 scope_storage.current_run += 1;
791
792 scope_rc.borrow_mut().element = Some(path_element);
793 }
794 PathElement::Element { elements, .. } => {
795 for element in elements.iter() {
796 self.run_scope(scope, element, mutations, visited_scopes);
797 }
798 }
799 }
800 }
801
802 #[cfg_attr(feature = "hotpath", hotpath::measure)]
803 fn apply_diff(
804 &mut self,
805 scope: &Rc<RefCell<Scope>>,
806 diff: Diff,
807 mutations: &mut Mutations,
808 path_element: &PathElement,
809 ) {
810 let mut moved_nodes =
811 FxHashMap::<Box<[u32]>, (NodeId, FxHashMap<u32, PathNode>)>::default();
812 let mut parents_to_resync_scopes = FxHashSet::default();
813
814 for (parent, movements) in &diff.moved {
817 parents_to_resync_scopes.insert(parent.clone());
818 let paths = moved_nodes.entry(parent.clone()).or_insert_with(|| {
819 let parent_node_id = scope.borrow().nodes.get(parent).unwrap().node_id;
820 (parent_node_id, FxHashMap::default())
821 });
822
823 for (from, _to) in movements.iter() {
824 let mut path = parent.to_vec();
825 path.push(*from);
826
827 let path_node = scope.borrow().nodes.get(&path).cloned().unwrap();
828
829 paths.1.insert(*from, path_node);
830 }
831 }
832
833 let mut selected_roots: HashMap<&[u32], HashSet<&[u32]>> = HashMap::default();
835 let mut scope_removal_buffer = vec![];
836
837 'remove: for removed in diff.removed.iter().sorted_by(|a, b| {
857 for (x, y) in a.iter().zip(b.iter()) {
858 match x.cmp(y) {
859 Ordering::Equal => continue,
860 non_eq => return non_eq.reverse(),
861 }
862 }
863 b.len().cmp(&a.len())
864 }) {
865 parents_to_resync_scopes.insert(Box::from(&removed[..removed.len() - 1]));
866
867 let path_node = scope.borrow().nodes.get(removed).cloned();
868 if let Some(PathNode { node_id, scope_id }) = path_node {
869 if let Some(scope_id) = scope_id {
870 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
872 scope.borrow_mut().nodes.remove(removed);
873 } else {
874 mutations.removed.push(MutationRemove::Element {
876 id: node_id,
877 index: *removed.last().unwrap(),
878 });
879
880 for (root, inner) in &mut selected_roots {
882 if is_descendant(removed, root) {
883 inner.insert(removed);
884 continue 'remove;
885 }
886 }
887
888 selected_roots.retain(|root, _| !is_descendant(root, removed));
890
891 selected_roots
892 .entry(&removed[..removed.len() - 1])
893 .or_default()
894 .insert(removed);
895 }
896 } else {
897 unreachable!()
898 }
899 }
900
901 for (root, removed) in selected_roots {
903 scope.borrow_mut().nodes.retain(
904 root,
905 |p, _| !removed.contains(p),
906 |_, &PathNode { scope_id, node_id }| {
907 if let Some(scope_id) = scope_id {
908 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
910 } else {
911 self.node_to_scope.remove(&node_id).unwrap();
912 }
913 },
914 );
915 }
916
917 let mut scope_removal_queue = VecDeque::new();
918
919 while let Some(scope_rc) = scope_removal_buffer.pop() {
920 scope_removal_queue.push_front(scope_rc.clone());
923
924 let scope = scope_rc.borrow_mut();
925
926 let mut scope_root_node_id = None;
927
928 scope
930 .nodes
931 .traverse(&[], |path, &PathNode { scope_id, node_id }| {
932 if let Some(scope_id) = scope_id {
933 scope_removal_buffer.push(self.scopes.get(&scope_id).cloned().unwrap());
934 } else {
935 self.node_to_scope.remove(&node_id).unwrap();
936 }
937 if path == [0] {
938 scope_root_node_id = Some(node_id);
939 }
940 });
941
942 mutations.removed.push(MutationRemove::Scope {
945 id: scope_root_node_id.unwrap(),
946 });
947 }
948
949 for scope_rc in scope_removal_queue {
951 let scope = scope_rc.borrow_mut();
952
953 self.scopes.remove(&scope.id);
954
955 CurrentContext::run_with_reactive(
957 CurrentContext {
958 scope_id: scope.id,
959 scopes_storages: self.scopes_storages.clone(),
960 tasks: self.tasks.clone(),
961 task_id_counter: self.task_id_counter.clone(),
962 sender: self.sender.clone(),
963 },
964 || {
965 let _scope = self.scopes_storages.borrow_mut().remove(&scope.id);
967 },
968 );
969
970 self.tasks
972 .borrow_mut()
973 .retain(|_task_id, task| task.borrow().scope_id != scope.id);
974 }
975
976 for added in diff
996 .added
997 .iter()
998 .sorted_by(|a, b| {
999 for (x, y) in a.iter().zip(b.iter()) {
1000 match x.cmp(y) {
1001 Ordering::Equal => continue,
1002 non_eq => return non_eq.reverse(),
1003 }
1004 }
1005 b.len().cmp(&a.len())
1006 })
1007 .rev()
1008 {
1009 let (parent_node_id, index_inside_parent) = if added.as_ref() == [0] {
1010 let mut index_inside_parent = 0;
1015
1016 let parent_id = scope.borrow().parent_id;
1017 let scope_id = scope.borrow().id;
1018 let parent_node_id = scope.borrow().parent_node_id_in_parent;
1019
1020 if let Some(parent_id) = parent_id {
1021 let mut buffer = Some((parent_id, parent_node_id, scope_id));
1022 while let Some((parent_id, parent_node_id, scope_id)) = buffer.take() {
1023 let parent_scope = self.scopes.get(&parent_id).unwrap();
1024 let parent_scope = parent_scope.borrow();
1025
1026 let scope = self.scopes.get(&scope_id).unwrap();
1027 let scope = scope.borrow();
1028
1029 let path_node_parent = parent_scope.nodes.find(|v| {
1030 if let Some(v) = v {
1031 v.node_id == parent_node_id
1032 } else {
1033 false
1034 }
1035 });
1036
1037 if let Some(path_node_parent) = path_node_parent {
1038 if let Some(scope_id) = path_node_parent.scope_id {
1039 if let Some(parent_id) = parent_scope.parent_id {
1040 buffer.replace((
1042 parent_id,
1043 parent_scope.parent_node_id_in_parent,
1044 scope_id,
1045 ));
1046 } else {
1047 assert_eq!(scope_id, ScopeId::ROOT);
1048 }
1049 } else {
1050 index_inside_parent = *scope.path_in_parent.last().unwrap();
1052 }
1053 } else if let Some(new_parent_id) = parent_scope.parent_id {
1054 buffer.replace((
1056 new_parent_id,
1057 parent_scope.parent_node_id_in_parent,
1058 parent_id,
1059 ));
1060 }
1061 }
1062 } else {
1063 assert_eq!(scope_id, ScopeId::ROOT);
1064 }
1065
1066 (parent_node_id, index_inside_parent)
1067 } else {
1068 parents_to_resync_scopes.insert(Box::from(&added[..added.len() - 1]));
1070 (
1071 scope
1072 .borrow()
1073 .nodes
1074 .get(&added[..added.len() - 1])
1075 .unwrap()
1076 .node_id,
1077 added[added.len() - 1],
1078 )
1079 };
1080
1081 self.node_id_counter += 1;
1082
1083 path_element.with_element(added, |element| match element {
1084 PathElement::Component { .. } => {
1085 self.scope_id_counter += 1;
1086 let scope_id = self.scope_id_counter;
1087
1088 scope.borrow_mut().nodes.insert(
1089 added,
1090 PathNode {
1091 node_id: self.node_id_counter,
1092 scope_id: Some(scope_id),
1093 },
1094 );
1095 }
1096 PathElement::Element { element, .. } => {
1097 mutations.added.push((
1098 self.node_id_counter,
1099 parent_node_id,
1100 index_inside_parent,
1101 element.clone(),
1102 ));
1103
1104 self.node_to_scope
1105 .insert(self.node_id_counter, scope.borrow().id);
1106 scope.borrow_mut().nodes.insert(
1107 added,
1108 PathNode {
1109 node_id: self.node_id_counter,
1110 scope_id: None,
1111 },
1112 );
1113 }
1114 });
1115 }
1116
1117 for (parent, movements) in diff.moved.into_iter().sorted_by(|(a, _), (b, _)| {
1118 for (x, y) in a.iter().zip(b.iter()) {
1119 match x.cmp(y) {
1120 Ordering::Equal => continue,
1121 non_eq => return non_eq.reverse(),
1122 }
1123 }
1124 b.len().cmp(&a.len())
1125 }) {
1126 parents_to_resync_scopes.insert(parent.clone());
1127
1128 let (parent_node_id, paths) = moved_nodes.get_mut(&parent).unwrap();
1129
1130 for (from, to) in movements.into_iter().sorted_by_key(|e| e.0).rev() {
1131 let path_node = paths.remove(&from).unwrap();
1132
1133 let PathNode { node_id, scope_id } = path_node;
1134
1135 let from_path = scope
1137 .borrow()
1138 .nodes
1139 .find_child_path(&parent, |v| v == Some(&path_node))
1140 .unwrap();
1141
1142 let mut to_path = parent.to_vec();
1143 to_path.push(to);
1144
1145 if from_path == to_path {
1146 continue;
1147 }
1148
1149 let path_entry = scope.borrow_mut().nodes.remove(&from_path).unwrap();
1151 scope.borrow_mut().nodes.insert_entry(&to_path, path_entry);
1152
1153 if let Some(scope_id) = scope_id {
1154 let scope_rc = self.scopes.get(&scope_id).cloned().unwrap();
1155
1156 let scope = scope_rc.borrow();
1157
1158 let scope_root_node_id = scope.nodes.get(&[0]).map(|node| node.node_id);
1159
1160 mutations
1162 .moved
1163 .entry(scope.parent_node_id_in_parent)
1164 .or_default()
1165 .push((to, scope_root_node_id.unwrap()));
1166 } else {
1167 mutations
1169 .moved
1170 .entry(*parent_node_id)
1171 .or_default()
1172 .push((to, node_id));
1173 }
1174 }
1175 }
1176
1177 for (modified, flags) in diff.modified {
1178 path_element.with_element(&modified, |element| match element {
1179 PathElement::Component { .. } => {
1180 unreachable!()
1182 }
1183 PathElement::Element { element, .. } => {
1184 let node_id = scope
1185 .borrow()
1186 .nodes
1187 .get(&modified)
1188 .map(|path_node| path_node.node_id)
1189 .unwrap();
1190 mutations.modified.push((node_id, element.clone(), flags));
1191 }
1192 });
1193 }
1194
1195 for parent in parents_to_resync_scopes {
1198 if diff.added.contains(&parent) {
1201 continue;
1203 }
1204
1205 scope
1207 .borrow_mut()
1208 .nodes
1209 .traverse_1_level(&parent, |p, path_node| {
1210 if let Some(scope_id) = path_node.scope_id
1211 && let Some(scope_rc) = self.scopes.get(&scope_id)
1212 {
1213 let mut scope = scope_rc.borrow_mut();
1214 scope.path_in_parent = Box::from(p);
1215 }
1216 });
1217 }
1218 }
1219}
1220
1221#[derive(Default, Debug)]
1222pub struct Diff {
1223 pub added: Vec<Box<[u32]>>,
1224
1225 pub modified: Vec<(Box<[u32]>, DiffModifies)>,
1226
1227 pub removed: Vec<Box<[u32]>>,
1228
1229 pub moved: HashMap<Box<[u32]>, Vec<(u32, u32)>>,
1230}
1231
1232fn is_descendant(candidate: &[u32], ancestor: &[u32]) -> bool {
1233 if ancestor.len() > candidate.len() {
1234 return false;
1235 }
1236 candidate[..ancestor.len()] == *ancestor
1237}