WIP: Move sum_tree module into gpui so we can use it in List

This commit is contained in:
Nathan Sobo 2021-08-20 16:18:39 -06:00
parent 2507f9b4d4
commit c3dda14490
16 changed files with 190 additions and 138 deletions

19
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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::*;

45
gpui/src/elements/list.rs Normal file
View File

@ -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<Mutex<StateInner>>);
struct StateInner {
elements: Vec<ElementBox>,
element_heights: SumTree<ElementHeight>,
}
#[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;
}
}

View File

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

View File

@ -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<Ordering> {
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<T: Item>(Arc<Node<T>>);
@ -253,8 +275,8 @@ impl<T: Item> SumTree<T> {
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<T>; 2 * TREE_BASE]>::new();
let mut summaries_to_append = ArrayVec::<T::Summary, { 2 * TREE_BASE }>::new();
let mut trees_to_append = ArrayVec::<SumTree<T>, { 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<T: Item> SumTree<T> {
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<T: Item> SumTree<T> {
let left_items;
let right_items;
let left_summaries;
let right_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>;
let right_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }>;
let midpoint = (child_count + child_count % 2) / 2;
{
@ -491,13 +513,13 @@ pub enum Node<T: Item> {
Internal {
height: u8,
summary: T::Summary,
child_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>,
child_trees: ArrayVec<[SumTree<T>; 2 * TREE_BASE]>,
child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }>,
child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }>,
},
Leaf {
summary: T::Summary,
items: ArrayVec<[T; 2 * TREE_BASE]>,
item_summaries: ArrayVec<[T::Summary; 2 * TREE_BASE]>,
items: ArrayVec<T, { 2 * TREE_BASE }>,
item_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }>,
},
}
@ -532,14 +554,14 @@ impl<T: Item> Node<T> {
}
}
fn child_trees(&self) -> &ArrayVec<[SumTree<T>; 2 * TREE_BASE]> {
fn child_trees(&self) -> &ArrayVec<SumTree<T>, { 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<T, { 2 * TREE_BASE }> {
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::<u8>::new();

View File

@ -13,7 +13,7 @@ struct StackEntry<'a, T: Item, S, U> {
#[derive(Clone)]
pub struct Cursor<'a, T: Item, S, U> {
tree: &'a SumTree<T>,
stack: ArrayVec<[StackEntry<'a, T, S, U>; 16]>,
stack: ArrayVec<StackEntry<'a, T, S, U>, 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::<T, { 2 * TREE_BASE }>::new();
let mut slice_item_summaries = ArrayVec::<T::Summary, { 2 * TREE_BASE }>::new();
let mut slice_items_summary = match aggregate {
SeekAggregate::Slice(_) => Some(T::Summary::default()),
_ => None,

View File

@ -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"

View File

@ -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<Option<SyntaxTree>>,
is_parsing: bool,
selections: HashMap<SelectionSetId, SelectionSet>,
deferred_ops: OperationQueue<Operation>,
deferred_ops: OperationQueue,
deferred_replicas: HashSet<ReplicaId>,
replica_id: ReplicaId,
remote_id: u64,
@ -483,6 +485,8 @@ pub enum Operation {
set_id: Option<SelectionSetId>,
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<proto::Operation> for &'a Operation {
lamport_timestamp: lamport_timestamp.value,
},
),
#[cfg(test)]
Operation::Test(_) => unimplemented!()
}),
}
}
@ -2834,12 +2846,6 @@ impl TryFrom<proto::Selection> 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<Content<'a>>) -> usize;
}

View File

@ -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<T: Operation>(SumTree<T>);
pub struct OperationQueue(SumTree<Operation>);
#[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<T: Operation> OperationQueue<T> {
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<T: Operation> OperationQueue<T> {
self.0.summary().len
}
pub fn insert(&mut self, mut ops: Vec<T>) {
ops.sort_by_key(|op| op.timestamp());
ops.dedup_by_key(|op| op.timestamp());
pub fn insert(&mut self, mut ops: Vec<Operation>) {
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<T: Operation> OperationQueue<T> {
clone
}
pub fn cursor(&self) -> Cursor<T, (), ()> {
pub fn cursor(&self) -> Cursor<Operation, (), ()> {
self.0.cursor()
}
}
impl<T: Operation> Item for T {
type Summary = OperationSummary;
fn summary(&self) -> Self::Summary {
OperationSummary {
key: OperationKey(self.timestamp()),
len: 1,
}
}
}
impl<T: Operation> 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
}
}
}

View File

@ -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 {

View File

@ -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},

View File

@ -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<Transform>, summary: TextSummary) {
transforms.push(Transform::isomorphic(summary));
}
impl SumTree<Transform> {
pub fn push_or_extend(&mut self, transform: Transform) {
trait SumTreeExt {
fn push_or_extend(&mut self, transform: Transform);
}
impl SumTreeExt for SumTree<Transform> {
fn push_or_extend(&mut self, transform: Transform) {
let mut transform = Some(transform);
self.update_last(
|last_transform| {

View File

@ -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;

View File

@ -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<Ordering> {
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;

View File

@ -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;