diff --git a/Cargo.lock b/Cargo.lock index 817a0e7c91..426b99d932 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,6 +162,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" + [[package]] name = "ascii" version = "1.0.0" @@ -626,7 +632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.5.2", "constant_time_eq", ] @@ -637,7 +643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.5.2", "cc", "cfg-if 0.1.10", "constant_time_eq", @@ -2145,6 +2151,7 @@ name = "gpui" version = "0.1.0" dependencies = [ "anyhow", + "arrayvec 0.7.1", "async-task", "backtrace", "bindgen", @@ -2632,7 +2639,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e30b1df631d23875f230ed3ddd1a88c231f269a04b2044eb6ca87e763b5f4c42" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", ] [[package]] @@ -2665,7 +2672,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitflags 1.2.1", "cfg-if 1.0.0", "ryu", @@ -5164,7 +5171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.5.2", "bytemuck", "cfg-if 1.0.0", "png 0.16.8", @@ -5792,7 +5799,7 @@ name = "zed" version = "0.1.0" dependencies = [ "anyhow", - "arrayvec", + "arrayvec 0.7.1", "async-trait", "async-tungstenite", "cargo-bundle", diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml index 535fe16155..be3d47e42a 100644 --- a/gpui/Cargo.toml +++ b/gpui/Cargo.toml @@ -5,6 +5,7 @@ name = "gpui" version = "0.1.0" [dependencies] +arrayvec = "0.7.1" async-task = "4.0.3" backtrace = "0.3" ctor = "0.1" diff --git a/gpui/src/elements.rs b/gpui/src/elements.rs index 3d03574094..14d33b8e90 100644 --- a/gpui/src/elements.rs +++ b/gpui/src/elements.rs @@ -7,6 +7,7 @@ mod event_handler; mod flex; mod label; mod line_box; +mod list; mod mouse_event_handler; mod stack; mod svg; @@ -22,6 +23,7 @@ pub use event_handler::*; pub use flex::*; pub use label::*; pub use line_box::*; +pub use list::*; pub use mouse_event_handler::*; pub use stack::*; pub use svg::*; diff --git a/gpui/src/elements/list.rs b/gpui/src/elements/list.rs new file mode 100644 index 0000000000..fd3e06f295 --- /dev/null +++ b/gpui/src/elements/list.rs @@ -0,0 +1,45 @@ +use crate::sum_tree::{self, SumTree}; +use parking_lot::Mutex; +use std::sync::Arc; + +use crate::ElementBox; + +pub struct List { + state: ListState, +} + +pub struct ListState(Arc>); + +struct StateInner { + elements: Vec, + element_heights: SumTree, +} + +#[derive(Clone, Debug)] +enum ElementHeight { + Pending, + Ready(f32), +} + +#[derive(Clone, Debug, Default)] +struct ElementHeightSummary { + pending_count: usize, + height: f32, +} + +impl sum_tree::Item for ElementHeight { + type Summary = ElementHeightSummary; + + fn summary(&self) -> Self::Summary { + todo!() + } +} + +impl sum_tree::Summary for ElementHeightSummary { + type Context = (); + + fn add_summary(&mut self, summary: &Self, cx: &Self::Context) { + self.pending_count += summary.pending_count; + self.height += summary.height; + } +} diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs index f2f935d142..308674d45d 100644 --- a/gpui/src/lib.rs +++ b/gpui/src/lib.rs @@ -1,6 +1,7 @@ mod app; pub use app::*; mod assets; +pub mod sum_tree; #[cfg(test)] mod test; pub use assets::*; diff --git a/zed/src/sum_tree.rs b/gpui/src/sum_tree.rs similarity index 95% rename from zed/src/sum_tree.rs rename to gpui/src/sum_tree.rs index eb13c2a86c..eeb53cc188 100644 --- a/zed/src/sum_tree.rs +++ b/gpui/src/sum_tree.rs @@ -1,6 +1,5 @@ mod cursor; -use crate::util::Bias; use arrayvec::ArrayVec; pub use cursor::Cursor; pub use cursor::FilterCursor; @@ -47,6 +46,29 @@ impl<'a, S: Summary, T: Dimension<'a, S> + Ord> SeekDimension<'a, S> for T { } } +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub enum Bias { + Left, + Right, +} + +impl PartialOrd for Bias { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Bias { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Left, Self::Left) => Ordering::Equal, + (Self::Left, Self::Right) => Ordering::Less, + (Self::Right, Self::Right) => Ordering::Equal, + (Self::Right, Self::Left) => Ordering::Greater, + } + } +} + #[derive(Debug, Clone)] pub struct SumTree(Arc>); @@ -253,8 +275,8 @@ impl SumTree { summary.add_summary(other_node.summary(), cx); let height_delta = *height - other_node.height(); - let mut summaries_to_append = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new(); - let mut trees_to_append = ArrayVec::<[SumTree; 2 * TREE_BASE]>::new(); + let mut summaries_to_append = ArrayVec::::new(); + let mut trees_to_append = ArrayVec::, { 2 * TREE_BASE }>::new(); if height_delta == 0 { summaries_to_append.extend(other_node.child_summaries().iter().cloned()); trees_to_append.extend(other_node.child_trees().iter().cloned()); @@ -277,8 +299,8 @@ impl SumTree { let child_count = child_trees.len() + trees_to_append.len(); if child_count > 2 * TREE_BASE { - let left_summaries: ArrayVec<_>; - let right_summaries: ArrayVec<_>; + let left_summaries: ArrayVec<_, { 2 * TREE_BASE }>; + let right_summaries: ArrayVec<_, { 2 * TREE_BASE }>; let left_trees; let right_trees; @@ -323,7 +345,7 @@ impl SumTree { let left_items; let right_items; let left_summaries; - let right_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>; + let right_summaries: ArrayVec; let midpoint = (child_count + child_count % 2) / 2; { @@ -491,13 +513,13 @@ pub enum Node { Internal { height: u8, summary: T::Summary, - child_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>, - child_trees: ArrayVec<[SumTree; 2 * TREE_BASE]>, + child_summaries: ArrayVec, + child_trees: ArrayVec, { 2 * TREE_BASE }>, }, Leaf { summary: T::Summary, - items: ArrayVec<[T; 2 * TREE_BASE]>, - item_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>, + items: ArrayVec, + item_summaries: ArrayVec, }, } @@ -532,14 +554,14 @@ impl Node { } } - fn child_trees(&self) -> &ArrayVec<[SumTree; 2 * TREE_BASE]> { + fn child_trees(&self) -> &ArrayVec, { 2 * TREE_BASE }> { match self { Node::Internal { child_trees, .. } => child_trees, Node::Leaf { .. } => panic!("Leaf nodes have no child trees"), } } - fn items(&self) -> &ArrayVec<[T; 2 * TREE_BASE]> { + fn items(&self) -> &ArrayVec { match self { Node::Leaf { items, .. } => items, Node::Internal { .. } => panic!("Internal nodes have no items"), @@ -603,7 +625,7 @@ mod tests { ); } - #[gpui::test(iterations = 100)] + #[crate::test(self, iterations = 100)] fn test_random(mut rng: StdRng) { let rng = &mut rng; let mut tree = SumTree::::new(); diff --git a/zed/src/sum_tree/cursor.rs b/gpui/src/sum_tree/cursor.rs similarity index 99% rename from zed/src/sum_tree/cursor.rs rename to gpui/src/sum_tree/cursor.rs index 8a62cf4b48..990fe30900 100644 --- a/zed/src/sum_tree/cursor.rs +++ b/gpui/src/sum_tree/cursor.rs @@ -13,7 +13,7 @@ struct StackEntry<'a, T: Item, S, U> { #[derive(Clone)] pub struct Cursor<'a, T: Item, S, U> { tree: &'a SumTree, - stack: ArrayVec<[StackEntry<'a, T, S, U>; 16]>, + stack: ArrayVec, 16>, seek_dimension: S, sum_dimension: U, did_seek: bool, @@ -495,8 +495,8 @@ where ref item_summaries, .. } => { - let mut slice_items = ArrayVec::<[T; 2 * TREE_BASE]>::new(); - let mut slice_item_summaries = ArrayVec::<[T::Summary; 2 * TREE_BASE]>::new(); + let mut slice_items = ArrayVec::::new(); + let mut slice_item_summaries = ArrayVec::::new(); let mut slice_items_summary = match aggregate { SeekAggregate::Slice(_) => Some(T::Summary::default()), _ => None, diff --git a/zed/Cargo.toml b/zed/Cargo.toml index 25aafaac5f..03bcdd3a56 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -18,8 +18,8 @@ test-support = ["tempdir", "zrpc/test-support"] [dependencies] anyhow = "1.0.38" -arrayvec = "0.5.2" async-trait = "0.1" +arrayvec = "0.7.1" async-tungstenite = { version = "0.14", features = ["async-tls"] } crossbeam-channel = "0.5.0" ctor = "0.1.20" diff --git a/zed/src/editor/buffer.rs b/zed/src/editor/buffer.rs index 9b1f169e48..eb57328a1e 100644 --- a/zed/src/editor/buffer.rs +++ b/zed/src/editor/buffer.rs @@ -1,30 +1,30 @@ mod anchor; +mod operation_queue; mod point; pub mod rope; mod selection; +use crate::{ + language::{Language, Tree}, + settings::{HighlightId, HighlightMap}, + time::{self, ReplicaId}, + util::Bias, + worktree::{File, Worktree}, +}; pub use anchor::*; +use anyhow::{anyhow, Result}; +use gpui::{ + sum_tree::{self, FilterCursor, SumTree}, + AppContext, Entity, ModelContext, ModelHandle, Task, +}; +use lazy_static::lazy_static; +use operation_queue::OperationQueue; use parking_lot::Mutex; pub use point::*; pub use rope::{Chunks, Rope, TextSummary}; use seahash::SeaHasher; pub use selection::*; use similar::{ChangeTag, TextDiff}; -use tree_sitter::{InputEdit, Parser, QueryCursor}; -use zrpc::proto; - -use crate::{ - language::{Language, Tree}, - operation_queue::{self, OperationQueue}, - settings::{HighlightId, HighlightMap}, - sum_tree::{self, FilterCursor, SumTree}, - time::{self, ReplicaId}, - util::Bias, - worktree::{File, Worktree}, -}; -use anyhow::{anyhow, Result}; -use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; -use lazy_static::lazy_static; use std::{ cell::RefCell, cmp, @@ -37,6 +37,8 @@ use std::{ sync::Arc, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; +use tree_sitter::{InputEdit, Parser, QueryCursor}; +use zrpc::proto; #[derive(Clone, Default)] struct DeterministicState; @@ -120,7 +122,7 @@ pub struct Buffer { syntax_tree: Mutex>, is_parsing: bool, selections: HashMap, - deferred_ops: OperationQueue, + deferred_ops: OperationQueue, deferred_replicas: HashSet, replica_id: ReplicaId, remote_id: u64, @@ -483,6 +485,8 @@ pub enum Operation { set_id: Option, lamport_timestamp: time::Lamport, }, + #[cfg(test)] + Test(time::Lamport), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1390,6 +1394,8 @@ impl Buffer { } self.lamport_clock.observe(lamport_timestamp); } + #[cfg(test)] + Operation::Test(_) => {} } Ok(()) } @@ -1731,6 +1737,8 @@ impl Buffer { Operation::SetActiveSelections { set_id, .. } => { set_id.map_or(true, |set_id| self.selections.contains_key(&set_id)) } + #[cfg(test)] + Operation::Test(_) => true, } } } @@ -2564,6 +2572,8 @@ impl Operation { Operation::SetActiveSelections { lamport_timestamp, .. } => *lamport_timestamp, + #[cfg(test)] + Operation::Test(lamport_timestamp) => *lamport_timestamp, } } @@ -2630,6 +2640,8 @@ impl<'a> Into for &'a Operation { lamport_timestamp: lamport_timestamp.value, }, ), + #[cfg(test)] + Operation::Test(_) => unimplemented!() }), } } @@ -2834,12 +2846,6 @@ impl TryFrom for Selection { } } -impl operation_queue::Operation for Operation { - fn timestamp(&self) -> time::Lamport { - self.lamport_timestamp() - } -} - pub trait ToOffset { fn to_offset<'a>(&self, content: impl Into>) -> usize; } diff --git a/zed/src/operation_queue.rs b/zed/src/editor/buffer/operation_queue.rs similarity index 68% rename from zed/src/operation_queue.rs rename to zed/src/editor/buffer/operation_queue.rs index 681d135870..b6efc3fc9c 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/editor/buffer/operation_queue.rs @@ -1,26 +1,27 @@ -use crate::{ - sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary}, - time, -}; +use super::Operation; +use crate::time; +use gpui::sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary}; use std::{fmt::Debug, ops::Add}; -pub trait Operation: Clone + Debug + Eq { - fn timestamp(&self) -> time::Lamport; -} - #[derive(Clone, Debug)] -pub struct OperationQueue(SumTree); +pub struct OperationQueue(SumTree); #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] pub struct OperationKey(time::Lamport); #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct OperationSummary { - key: OperationKey, - len: usize, + pub key: OperationKey, + pub len: usize, } -impl OperationQueue { +impl OperationKey { + pub fn new(timestamp: time::Lamport) -> Self { + Self(timestamp) + } +} + +impl OperationQueue { pub fn new() -> Self { OperationQueue(SumTree::new()) } @@ -29,9 +30,9 @@ impl OperationQueue { self.0.summary().len } - pub fn insert(&mut self, mut ops: Vec) { - ops.sort_by_key(|op| op.timestamp()); - ops.dedup_by_key(|op| op.timestamp()); + pub fn insert(&mut self, mut ops: Vec) { + ops.sort_by_key(|op| op.lamport_timestamp()); + ops.dedup_by_key(|op| op.lamport_timestamp()); self.0 .edit(ops.into_iter().map(Edit::Insert).collect(), &()); } @@ -42,30 +43,11 @@ impl OperationQueue { clone } - pub fn cursor(&self) -> Cursor { + pub fn cursor(&self) -> Cursor { self.0.cursor() } } -impl Item for T { - type Summary = OperationSummary; - - fn summary(&self) -> Self::Summary { - OperationSummary { - key: OperationKey(self.timestamp()), - len: 1, - } - } -} - -impl KeyedItem for T { - type Key = OperationKey; - - fn key(&self) -> Self::Key { - OperationKey(self.timestamp()) - } -} - impl Summary for OperationSummary { type Context = (); @@ -95,6 +77,25 @@ impl<'a> Dimension<'a, OperationSummary> for OperationKey { } } +impl Item for Operation { + type Summary = OperationSummary; + + fn summary(&self) -> Self::Summary { + OperationSummary { + key: OperationKey::new(self.lamport_timestamp()), + len: 1, + } + } +} + +impl KeyedItem for Operation { + type Key = OperationKey; + + fn key(&self) -> Self::Key { + OperationKey::new(self.lamport_timestamp()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -107,27 +108,21 @@ mod tests { assert_eq!(queue.len(), 0); queue.insert(vec![ - TestOperation(clock.tick()), - TestOperation(clock.tick()), + Operation::Test(clock.tick()), + Operation::Test(clock.tick()), ]); assert_eq!(queue.len(), 2); - queue.insert(vec![TestOperation(clock.tick())]); + queue.insert(vec![Operation::Test(clock.tick())]); assert_eq!(queue.len(), 3); drop(queue.drain()); assert_eq!(queue.len(), 0); - queue.insert(vec![TestOperation(clock.tick())]); + queue.insert(vec![Operation::Test(clock.tick())]); assert_eq!(queue.len(), 1); } #[derive(Clone, Debug, Eq, PartialEq)] struct TestOperation(time::Lamport); - - impl Operation for TestOperation { - fn timestamp(&self) -> time::Lamport { - self.0 - } - } } diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 58cd58fd3c..e9732e92c2 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -1,9 +1,7 @@ use super::Point; -use crate::{ - sum_tree::{self, SumTree}, - util::Bias, -}; +use crate::util::Bias; use arrayvec::ArrayString; +use gpui::sum_tree::{self, SumTree}; use smallvec::SmallVec; use std::{cmp, ops::Range, str}; @@ -61,7 +59,7 @@ impl Rope { if last_chunk.0.len() + first_new_chunk_ref.0.len() <= 2 * CHUNK_BASE { last_chunk.0.push_str(&first_new_chunk.take().unwrap().0); } else { - let mut text = ArrayString::<[_; 4 * CHUNK_BASE]>::new(); + let mut text = ArrayString::<{ 4 * CHUNK_BASE }>::new(); text.push_str(&last_chunk.0); text.push_str(&first_new_chunk_ref.0); let (left, right) = text.split_at(find_split_ix(&text)); @@ -330,7 +328,7 @@ impl<'a> Iterator for Chunks<'a> { } #[derive(Clone, Debug, Default)] -struct Chunk(ArrayString<[u8; 2 * CHUNK_BASE]>); +struct Chunk(ArrayString<{ 2 * CHUNK_BASE }>); impl Chunk { fn to_point(&self, target: usize) -> Point { diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 3bfa1f3d24..1c24bacafc 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -2,14 +2,11 @@ use super::{ buffer::{AnchorRangeExt, TextSummary}, Anchor, Buffer, Point, ToOffset, }; -use crate::{ - editor::buffer, - settings::HighlightId, +use crate::{editor::buffer, settings::HighlightId, time, util::Bias}; +use gpui::{ sum_tree::{self, Cursor, FilterCursor, SumTree}, - time, - util::Bias, + AppContext, ModelHandle, }; -use gpui::{AppContext, ModelHandle}; use parking_lot::Mutex; use std::{ cmp::{self, Ordering}, diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index 358730748b..e831d80042 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -3,14 +3,11 @@ use super::{ line_wrapper::LineWrapper, tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary}, }; -use crate::{ - editor::Point, - settings::HighlightId, +use crate::{editor::Point, settings::HighlightId, util::Bias, Settings}; +use gpui::{ sum_tree::{self, Cursor, SumTree}, - util::Bias, - Settings, + Entity, ModelContext, Task, }; -use gpui::{Entity, ModelContext, Task}; use lazy_static::lazy_static; use smol::future::yield_now; use std::{collections::VecDeque, ops::Range, time::Duration}; @@ -816,8 +813,12 @@ fn push_isomorphic(transforms: &mut Vec, summary: TextSummary) { transforms.push(Transform::isomorphic(summary)); } -impl SumTree { - pub fn push_or_extend(&mut self, transform: Transform) { +trait SumTreeExt { + fn push_or_extend(&mut self, transform: Transform); +} + +impl SumTreeExt for SumTree { + fn push_or_extend(&mut self, transform: Transform) { let mut transform = Some(transform); self.update_last( |last_transform| { diff --git a/zed/src/lib.rs b/zed/src/lib.rs index 445647010c..45af7e355c 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -7,11 +7,9 @@ pub mod fs; mod fuzzy; pub mod language; pub mod menus; -mod operation_queue; pub mod project_browser; pub mod rpc; pub mod settings; -mod sum_tree; #[cfg(any(test, feature = "test-support"))] pub mod test; pub mod theme; diff --git a/zed/src/util.rs b/zed/src/util.rs index 5bae8b7a99..8ca2cfd9b6 100644 --- a/zed/src/util.rs +++ b/zed/src/util.rs @@ -1,30 +1,8 @@ use futures::Future; +pub use gpui::sum_tree::Bias; use rand::prelude::*; use std::cmp::Ordering; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] -pub enum Bias { - Left, - Right, -} - -impl PartialOrd for Bias { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Bias { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (Self::Left, Self::Left) => Ordering::Equal, - (Self::Left, Self::Right) => Ordering::Less, - (Self::Right, Self::Right) => Ordering::Equal, - (Self::Right, Self::Left) => Ordering::Greater, - } - } -} - pub fn post_inc(value: &mut usize) -> usize { let prev = *value; *value += 1; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index d9a8aa889b..6c7ba34a15 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -8,7 +8,6 @@ use crate::{ fuzzy::CharBag, language::LanguageRegistry, rpc::{self, proto}, - sum_tree::{self, Cursor, Edit, SumTree}, time::{self, ReplicaId}, util::{log_async_errors, Bias}, }; @@ -17,8 +16,10 @@ use anyhow::{anyhow, Result}; use futures::{Stream, StreamExt}; pub use fuzzy::{match_paths, PathMatch}; use gpui::{ - executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, - Task, UpgradeModelHandle, WeakModelHandle, + executor, + sum_tree::{self, Cursor, Edit, SumTree}, + AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, + UpgradeModelHandle, WeakModelHandle, }; use lazy_static::lazy_static; use parking_lot::Mutex;