Extract sum_tree to its own crate

Nathan Sobo and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

Cargo.lock                               |  11 +
Cargo.toml                               |   2 
gpui/Cargo.toml                          |   2 
gpui/src/elements/list.rs                |   2 
gpui/src/lib.rs                          |   1 
sum_tree/Cargo.toml                      |  12 +
sum_tree/src/cursor.rs                   |   0 
sum_tree/src/lib.rs                      | 178 ++++++++++++++-----------
zed/Cargo.toml                           |   1 
zed/src/channel.rs                       |   2 
zed/src/editor/buffer.rs                 |   2 
zed/src/editor/buffer/operation_queue.rs |   2 
zed/src/editor/buffer/rope.rs            |   2 
zed/src/editor/display_map/fold_map.rs   |   6 
zed/src/editor/display_map/wrap_map.rs   |   8 
zed/src/util.rs                          |   4 
zed/src/worktree.rs                      |   2 
17 files changed, 133 insertions(+), 104 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -2175,7 +2175,6 @@ name = "gpui"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "arrayvec 0.7.1",
  "async-task",
  "backtrace",
  "bindgen",
@@ -2212,6 +2211,7 @@ dependencies = [
  "simplelog",
  "smallvec",
  "smol",
+ "sum_tree",
  "time 0.3.2",
  "tiny-skia",
  "tree-sitter",
@@ -4940,6 +4940,14 @@ version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
 
+[[package]]
+name = "sum_tree"
+version = "0.1.0"
+dependencies = [
+ "arrayvec 0.7.1",
+ "rand 0.8.3",
+]
+
 [[package]]
 name = "surf"
 version = "2.2.0"
@@ -5927,6 +5935,7 @@ dependencies = [
  "simplelog",
  "smallvec",
  "smol",
+ "sum_tree",
  "surf",
  "tempdir",
  "thiserror",

Cargo.toml 🔗

@@ -1,5 +1,5 @@
 [workspace]
-members = ["fsevent", "gpui", "gpui_macros", "server", "zed", "zrpc"]
+members = ["fsevent", "gpui", "gpui_macros", "server", "sum_tree", "zed", "zrpc"]
 default-members = ["zed"]
 
 [patch.crates-io]

gpui/Cargo.toml 🔗

@@ -8,7 +8,6 @@ version = "0.1.0"
 test-support = []
 
 [dependencies]
-arrayvec = "0.7.1"
 async-task = "4.0.3"
 backtrace = "0.3"
 ctor = "0.1"
@@ -31,6 +30,7 @@ serde = { version = "1.0.125", features = ["derive"] }
 serde_json = "1.0.64"
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2"
+sum_tree = { path = "../sum_tree" }
 time = { version = "0.3" }
 tiny-skia = "0.5"
 tree-sitter = "0.19"

gpui/src/elements/list.rs 🔗

@@ -4,11 +4,11 @@ use crate::{
         vector::{vec2f, Vector2F},
     },
     json::json,
-    sum_tree::{self, Bias, SumTree},
     DebugContext, Element, ElementBox, ElementRc, Event, EventContext, LayoutContext, PaintContext,
     SizeConstraint,
 };
 use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc};
+use sum_tree::{self, Bias, SumTree};
 
 pub struct List {
     state: ListState,

gpui/src/lib.rs 🔗

@@ -1,7 +1,6 @@
 mod app;
 pub use app::*;
 mod assets;
-pub mod sum_tree;
 #[cfg(test)]
 mod test;
 pub use assets::*;

sum_tree/Cargo.toml 🔗

@@ -0,0 +1,12 @@
+[package]
+name = "sum_tree"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+arrayvec = "0.7.1"
+
+[dev-dependencies]
+rand = "0.8.3"

gpui/src/sum_tree.rs → sum_tree/src/lib.rs 🔗

@@ -674,101 +674,115 @@ mod tests {
         );
     }
 
-    #[crate::test(self, iterations = 100)]
-    fn test_random(mut rng: StdRng) {
-        let rng = &mut rng;
-        let mut tree = SumTree::<u8>::new();
-        let count = rng.gen_range(0..10);
-        tree.extend(rng.sample_iter(distributions::Standard).take(count), &());
-
-        for _ in 0..5 {
-            let splice_end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
-            let splice_start = rng.gen_range(0..splice_end + 1);
-            let count = rng.gen_range(0..3);
-            let tree_end = tree.extent::<Count>(&());
-            let new_items = rng
-                .sample_iter(distributions::Standard)
-                .take(count)
-                .collect::<Vec<u8>>();
-
-            let mut reference_items = tree.items(&());
-            reference_items.splice(splice_start..splice_end, new_items.clone());
-
-            tree = {
-                let mut cursor = tree.cursor::<Count>();
-                let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right, &());
-                new_tree.extend(new_items, &());
-                cursor.seek(&Count(splice_end), Bias::Right, &());
-                new_tree.push_tree(cursor.slice(&tree_end, Bias::Right, &()), &());
-                new_tree
-            };
-
-            assert_eq!(tree.items(&()), reference_items);
-
-            let mut filter_cursor = tree.filter::<_, Count>(|summary| summary.contains_even, &());
-            let mut reference_filter = tree
-                .items(&())
-                .into_iter()
-                .enumerate()
-                .filter(|(_, item)| (item & 1) == 0);
-            while let Some(actual_item) = filter_cursor.item() {
-                let (reference_index, reference_item) = reference_filter.next().unwrap();
-                assert_eq!(actual_item, &reference_item);
-                assert_eq!(filter_cursor.start().0, reference_index);
-                filter_cursor.next(&());
-            }
-            assert!(reference_filter.next().is_none());
+    #[test]
+    fn test_random() {
+        let mut starting_seed = 0;
+        if let Ok(value) = std::env::var("SEED") {
+            starting_seed = value.parse().expect("invalid SEED variable");
+        }
+        let mut num_iterations = 100;
+        if let Ok(value) = std::env::var("ITERATIONS") {
+            num_iterations = value.parse().expect("invalid ITERATIONS variable");
+        }
 
-            let mut pos = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
-            let mut before_start = false;
-            let mut cursor = tree.cursor::<Count>();
-            cursor.seek(&Count(pos), Bias::Right, &());
+        for seed in starting_seed..(starting_seed + num_iterations) {
+            let mut rng = StdRng::seed_from_u64(seed);
+
+            let rng = &mut rng;
+            let mut tree = SumTree::<u8>::new();
+            let count = rng.gen_range(0..10);
+            tree.extend(rng.sample_iter(distributions::Standard).take(count), &());
+
+            for _ in 0..5 {
+                let splice_end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
+                let splice_start = rng.gen_range(0..splice_end + 1);
+                let count = rng.gen_range(0..3);
+                let tree_end = tree.extent::<Count>(&());
+                let new_items = rng
+                    .sample_iter(distributions::Standard)
+                    .take(count)
+                    .collect::<Vec<u8>>();
+
+                let mut reference_items = tree.items(&());
+                reference_items.splice(splice_start..splice_end, new_items.clone());
+
+                tree = {
+                    let mut cursor = tree.cursor::<Count>();
+                    let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right, &());
+                    new_tree.extend(new_items, &());
+                    cursor.seek(&Count(splice_end), Bias::Right, &());
+                    new_tree.push_tree(cursor.slice(&tree_end, Bias::Right, &()), &());
+                    new_tree
+                };
+
+                assert_eq!(tree.items(&()), reference_items);
+
+                let mut filter_cursor =
+                    tree.filter::<_, Count>(|summary| summary.contains_even, &());
+                let mut reference_filter = tree
+                    .items(&())
+                    .into_iter()
+                    .enumerate()
+                    .filter(|(_, item)| (item & 1) == 0);
+                while let Some(actual_item) = filter_cursor.item() {
+                    let (reference_index, reference_item) = reference_filter.next().unwrap();
+                    assert_eq!(actual_item, &reference_item);
+                    assert_eq!(filter_cursor.start().0, reference_index);
+                    filter_cursor.next(&());
+                }
+                assert!(reference_filter.next().is_none());
 
-            for i in 0..10 {
-                assert_eq!(cursor.start().0, pos);
+                let mut pos = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
+                let mut before_start = false;
+                let mut cursor = tree.cursor::<Count>();
+                cursor.seek(&Count(pos), Bias::Right, &());
 
-                if pos > 0 {
-                    assert_eq!(cursor.prev_item().unwrap(), &reference_items[pos - 1]);
-                } else {
-                    assert_eq!(cursor.prev_item(), None);
-                }
+                for i in 0..10 {
+                    assert_eq!(cursor.start().0, pos);
 
-                if pos < reference_items.len() && !before_start {
-                    assert_eq!(cursor.item().unwrap(), &reference_items[pos]);
-                } else {
-                    assert_eq!(cursor.item(), None);
-                }
+                    if pos > 0 {
+                        assert_eq!(cursor.prev_item().unwrap(), &reference_items[pos - 1]);
+                    } else {
+                        assert_eq!(cursor.prev_item(), None);
+                    }
 
-                if i < 5 {
-                    cursor.next(&());
-                    if pos < reference_items.len() {
-                        pos += 1;
-                        before_start = false;
+                    if pos < reference_items.len() && !before_start {
+                        assert_eq!(cursor.item().unwrap(), &reference_items[pos]);
+                    } else {
+                        assert_eq!(cursor.item(), None);
                     }
-                } else {
-                    cursor.prev(&());
-                    if pos == 0 {
-                        before_start = true;
+
+                    if i < 5 {
+                        cursor.next(&());
+                        if pos < reference_items.len() {
+                            pos += 1;
+                            before_start = false;
+                        }
+                    } else {
+                        cursor.prev(&());
+                        if pos == 0 {
+                            before_start = true;
+                        }
+                        pos = pos.saturating_sub(1);
                     }
-                    pos = pos.saturating_sub(1);
                 }
             }
-        }
 
-        for _ in 0..10 {
-            let end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
-            let start = rng.gen_range(0..end + 1);
-            let start_bias = if rng.gen() { Bias::Left } else { Bias::Right };
-            let end_bias = if rng.gen() { Bias::Left } else { Bias::Right };
+            for _ in 0..10 {
+                let end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
+                let start = rng.gen_range(0..end + 1);
+                let start_bias = if rng.gen() { Bias::Left } else { Bias::Right };
+                let end_bias = if rng.gen() { Bias::Left } else { Bias::Right };
 
-            let mut cursor = tree.cursor::<Count>();
-            cursor.seek(&Count(start), start_bias, &());
-            let slice = cursor.slice(&Count(end), end_bias, &());
+                let mut cursor = tree.cursor::<Count>();
+                cursor.seek(&Count(start), start_bias, &());
+                let slice = cursor.slice(&Count(end), end_bias, &());
 
-            cursor.seek(&Count(start), start_bias, &());
-            let summary = cursor.summary::<_, Sum>(&Count(end), end_bias, &());
+                cursor.seek(&Count(start), start_bias, &());
+                let summary = cursor.summary::<_, Sum>(&Count(end), end_bias, &());
 
-            assert_eq!(summary.0, slice.summary().sum);
+                assert_eq!(summary.0, slice.summary().sum);
+            }
         }
     }
 

zed/Cargo.toml 🔗

@@ -51,6 +51,7 @@ similar = "1.3"
 simplelog = "0.9"
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2.5"
+sum_tree = { "path" = "../sum_tree" }
 surf = "2.2"
 tempdir = { version = "0.3.7", optional = true }
 thiserror = "1.0.29"

zed/src/channel.rs 🔗

@@ -5,7 +5,6 @@ use crate::{
 };
 use anyhow::{anyhow, Context, Result};
 use gpui::{
-    sum_tree::{self, Bias, SumTree},
     AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
 };
 use postage::prelude::Stream;
@@ -16,6 +15,7 @@ use std::{
     ops::Range,
     sync::Arc,
 };
+use sum_tree::{self, Bias, SumTree};
 use time::OffsetDateTime;
 use zrpc::{
     proto::{self, ChannelMessageSent},

zed/src/editor/buffer.rs 🔗

@@ -13,8 +13,8 @@ use crate::{
 };
 pub use anchor::*;
 use anyhow::{anyhow, Result};
+use sum_tree::{self, FilterCursor, SumTree};
 use gpui::{
-    sum_tree::{self, FilterCursor, SumTree},
     AppContext, Entity, ModelContext, ModelHandle, Task,
 };
 use lazy_static::lazy_static;

zed/src/editor/buffer/operation_queue.rs 🔗

@@ -1,7 +1,7 @@
 use super::Operation;
 use crate::time;
-use gpui::sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary};
 use std::{fmt::Debug, ops::Add};
+use sum_tree::{Cursor, Dimension, Edit, Item, KeyedItem, SumTree, Summary};
 
 #[derive(Clone, Debug)]
 pub struct OperationQueue(SumTree<Operation>);

zed/src/editor/buffer/rope.rs 🔗

@@ -1,9 +1,9 @@
 use super::Point;
 use crate::util::Bias;
 use arrayvec::ArrayString;
-use gpui::sum_tree::{self, SumTree};
 use smallvec::SmallVec;
 use std::{cmp, ops::Range, str};
+use sum_tree::{self, SumTree};
 
 #[cfg(test)]
 const CHUNK_BASE: usize = 6;

zed/src/editor/display_map/fold_map.rs 🔗

@@ -3,10 +3,7 @@ use super::{
     Anchor, Buffer, Point, ToOffset,
 };
 use crate::{editor::buffer, settings::HighlightId, time, util::Bias};
-use gpui::{
-    sum_tree::{self, Cursor, FilterCursor, SumTree},
-    AppContext, ModelHandle,
-};
+use gpui::{AppContext, ModelHandle};
 use parking_lot::Mutex;
 use std::{
     cmp::{self, Ordering},
@@ -14,6 +11,7 @@ use std::{
     ops::Range,
     sync::atomic::{AtomicUsize, Ordering::SeqCst},
 };
+use sum_tree::{self, Cursor, FilterCursor, SumTree};
 
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 pub struct FoldPoint(pub super::Point);

zed/src/editor/display_map/wrap_map.rs 🔗

@@ -3,15 +3,11 @@ use super::{
     tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary},
 };
 use crate::{editor::Point, settings::HighlightId, util::Bias};
-use gpui::{
-    fonts::FontId,
-    sum_tree::{self, Cursor, SumTree},
-    text_layout::LineWrapper,
-    Entity, ModelContext, Task,
-};
+use gpui::{fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, Task};
 use lazy_static::lazy_static;
 use smol::future::yield_now;
 use std::{collections::VecDeque, ops::Range, time::Duration};
+use sum_tree::{self, Cursor, SumTree};
 
 pub struct WrapMap {
     snapshot: Snapshot,

zed/src/util.rs 🔗

@@ -1,11 +1,11 @@
-use futures::{Future};
-pub use gpui::sum_tree::Bias;
+use futures::Future;
 use rand::prelude::*;
 use std::{
     cmp::Ordering,
     pin::Pin,
     task::{Context, Poll},
 };
+pub use sum_tree::Bias;
 
 pub fn post_inc(value: &mut usize) -> usize {
     let prev = *value;

zed/src/worktree.rs 🔗

@@ -15,9 +15,9 @@ use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
 use anyhow::{anyhow, Result};
 use futures::{Stream, StreamExt};
 pub use fuzzy::{match_paths, PathMatch};
+use sum_tree::{self, Edit, SeekTarget, SumTree};
 use gpui::{
     executor,
-    sum_tree::{self, Edit, SeekTarget, SumTree},
     AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
     UpgradeModelHandle, WeakModelHandle,
 };