Sketch in type-level changes to track insertion splits
This commit is contained in:
parent
bd6e972d0f
commit
ec54010e3c
2 changed files with 134 additions and 12 deletions
74
crates/text/src/locator.rs
Normal file
74
crates/text/src/locator.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Locator(SmallVec<[u32; 4]>);
|
||||||
|
|
||||||
|
impl Locator {
|
||||||
|
pub fn min() -> Self {
|
||||||
|
Self(smallvec![u32::MIN])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max() -> Self {
|
||||||
|
Self(smallvec![u32::MAX])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn between(lhs: &Self, rhs: &Self) -> Self {
|
||||||
|
let lhs = lhs.0.iter().copied().chain(iter::repeat(u32::MIN));
|
||||||
|
let rhs = rhs.0.iter().copied().chain(iter::repeat(u32::MAX));
|
||||||
|
let mut location = SmallVec::new();
|
||||||
|
for (lhs, rhs) in lhs.zip(rhs) {
|
||||||
|
let mid = lhs + (rhs.saturating_sub(lhs)) / 2;
|
||||||
|
location.push(mid);
|
||||||
|
if mid > lhs {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Locator {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::min()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[gpui::test(iterations = 100)]
|
||||||
|
fn test_locators(mut rng: StdRng) {
|
||||||
|
let mut lhs = Default::default();
|
||||||
|
let mut rhs = Default::default();
|
||||||
|
while lhs == rhs {
|
||||||
|
lhs = Locator(
|
||||||
|
(0..rng.gen_range(1..=5))
|
||||||
|
.map(|_| rng.gen_range(0..=100))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
rhs = Locator(
|
||||||
|
(0..rng.gen_range(1..=5))
|
||||||
|
.map(|_| rng.gen_range(0..=100))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs > rhs {
|
||||||
|
mem::swap(&mut lhs, &mut rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
let middle = Locator::between(&lhs, &rhs);
|
||||||
|
assert!(middle > lhs);
|
||||||
|
assert!(middle < rhs);
|
||||||
|
for ix in 0..middle.0.len() - 1 {
|
||||||
|
assert!(
|
||||||
|
middle.0[ix] == *lhs.0.get(ix).unwrap_or(&0)
|
||||||
|
|| middle.0[ix] == *rhs.0.get(ix).unwrap_or(&0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
mod anchor;
|
mod anchor;
|
||||||
|
mod locator;
|
||||||
mod operation_queue;
|
mod operation_queue;
|
||||||
mod patch;
|
mod patch;
|
||||||
mod point;
|
mod point;
|
||||||
|
@ -14,6 +15,7 @@ pub use anchor::*;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
|
use locator::Locator;
|
||||||
use operation_queue::OperationQueue;
|
use operation_queue::OperationQueue;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
pub use patch::Patch;
|
pub use patch::Patch;
|
||||||
|
@ -55,6 +57,7 @@ pub struct Snapshot {
|
||||||
deleted_text: Rope,
|
deleted_text: Rope,
|
||||||
undo_map: UndoMap,
|
undo_map: UndoMap,
|
||||||
fragments: SumTree<Fragment>,
|
fragments: SumTree<Fragment>,
|
||||||
|
insertions: SumTree<InsertionFragment>,
|
||||||
pub version: clock::Global,
|
pub version: clock::Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +384,7 @@ impl InsertionTimestamp {
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
struct Fragment {
|
struct Fragment {
|
||||||
|
id: Locator,
|
||||||
timestamp: InsertionTimestamp,
|
timestamp: InsertionTimestamp,
|
||||||
len: usize,
|
len: usize,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
@ -391,6 +395,7 @@ struct Fragment {
|
||||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
pub struct FragmentSummary {
|
pub struct FragmentSummary {
|
||||||
text: FragmentTextSummary,
|
text: FragmentTextSummary,
|
||||||
|
max_id: Locator,
|
||||||
max_version: clock::Global,
|
max_version: clock::Global,
|
||||||
min_insertion_version: clock::Global,
|
min_insertion_version: clock::Global,
|
||||||
max_insertion_version: clock::Global,
|
max_insertion_version: clock::Global,
|
||||||
|
@ -402,11 +407,17 @@ struct FragmentTextSummary {
|
||||||
deleted: usize,
|
deleted: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
fn add_summary(&mut self, summary: &'a FragmentSummary, _: &Option<clock::Global>) {
|
struct InsertionFragment {
|
||||||
self.visible += summary.text.visible;
|
timestamp: InsertionTimestamp,
|
||||||
self.deleted += summary.text.deleted;
|
split_offset: usize,
|
||||||
}
|
fragment_id: Locator,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
struct InsertionSummary {
|
||||||
|
max_timestamp: InsertionTimestamp,
|
||||||
|
max_split_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -452,6 +463,7 @@ pub struct UndoOperation {
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer {
|
pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer {
|
||||||
let mut fragments = SumTree::new();
|
let mut fragments = SumTree::new();
|
||||||
|
let mut insertions = SumTree::new();
|
||||||
|
|
||||||
let mut local_clock = clock::Local::new(replica_id);
|
let mut local_clock = clock::Local::new(replica_id);
|
||||||
let mut lamport_clock = clock::Lamport::new(replica_id);
|
let mut lamport_clock = clock::Lamport::new(replica_id);
|
||||||
|
@ -466,8 +478,10 @@ impl Buffer {
|
||||||
local_clock.observe(timestamp.local());
|
local_clock.observe(timestamp.local());
|
||||||
lamport_clock.observe(timestamp.lamport());
|
lamport_clock.observe(timestamp.lamport());
|
||||||
version.observe(timestamp.local());
|
version.observe(timestamp.local());
|
||||||
|
let fragment_id = Locator::between(&Locator::min(), &Locator::max());
|
||||||
fragments.push(
|
fragments.push(
|
||||||
Fragment {
|
Fragment {
|
||||||
|
id: fragment_id,
|
||||||
timestamp,
|
timestamp,
|
||||||
len: visible_text.len(),
|
len: visible_text.len(),
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -476,6 +490,14 @@ impl Buffer {
|
||||||
},
|
},
|
||||||
&None,
|
&None,
|
||||||
);
|
);
|
||||||
|
insertions.push(
|
||||||
|
InsertionFragment {
|
||||||
|
timestamp,
|
||||||
|
split_offset: 0,
|
||||||
|
fragment_id,
|
||||||
|
},
|
||||||
|
&(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer {
|
Buffer {
|
||||||
|
@ -483,6 +505,7 @@ impl Buffer {
|
||||||
visible_text,
|
visible_text,
|
||||||
deleted_text: Rope::new(),
|
deleted_text: Rope::new(),
|
||||||
fragments,
|
fragments,
|
||||||
|
insertions,
|
||||||
version,
|
version,
|
||||||
undo_map: Default::default(),
|
undo_map: Default::default(),
|
||||||
},
|
},
|
||||||
|
@ -504,13 +527,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot(&self) -> Snapshot {
|
pub fn snapshot(&self) -> Snapshot {
|
||||||
Snapshot {
|
self.snapshot.clone()
|
||||||
visible_text: self.visible_text.clone(),
|
|
||||||
deleted_text: self.deleted_text.clone(),
|
|
||||||
undo_map: self.undo_map.clone(),
|
|
||||||
fragments: self.fragments.clone(),
|
|
||||||
version: self.version.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replica_id(&self) -> ReplicaId {
|
pub fn replica_id(&self) -> ReplicaId {
|
||||||
|
@ -569,6 +586,7 @@ impl Buffer {
|
||||||
ranges: Vec::with_capacity(ranges.len()),
|
ranges: Vec::with_capacity(ranges.len()),
|
||||||
new_text: None,
|
new_text: None,
|
||||||
};
|
};
|
||||||
|
let mut insertions = Vec::new();
|
||||||
|
|
||||||
let mut ranges = ranges
|
let mut ranges = ranges
|
||||||
.map(|range| range.start.to_offset(&*self)..range.end.to_offset(&*self))
|
.map(|range| range.start.to_offset(&*self)..range.end.to_offset(&*self))
|
||||||
|
@ -2040,6 +2058,7 @@ impl sum_tree::Item for Fragment {
|
||||||
let max_insertion_version = min_insertion_version.clone();
|
let max_insertion_version = min_insertion_version.clone();
|
||||||
if self.visible {
|
if self.visible {
|
||||||
FragmentSummary {
|
FragmentSummary {
|
||||||
|
max_id: self.id.clone(),
|
||||||
text: FragmentTextSummary {
|
text: FragmentTextSummary {
|
||||||
visible: self.len,
|
visible: self.len,
|
||||||
deleted: 0,
|
deleted: 0,
|
||||||
|
@ -2050,6 +2069,7 @@ impl sum_tree::Item for Fragment {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FragmentSummary {
|
FragmentSummary {
|
||||||
|
max_id: self.id.clone(),
|
||||||
text: FragmentTextSummary {
|
text: FragmentTextSummary {
|
||||||
visible: 0,
|
visible: 0,
|
||||||
deleted: self.len,
|
deleted: self.len,
|
||||||
|
@ -2079,6 +2099,7 @@ impl sum_tree::Summary for FragmentSummary {
|
||||||
impl Default for FragmentSummary {
|
impl Default for FragmentSummary {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FragmentSummary {
|
FragmentSummary {
|
||||||
|
max_id: Locator::min(),
|
||||||
text: FragmentTextSummary::default(),
|
text: FragmentTextSummary::default(),
|
||||||
max_version: clock::Global::new(),
|
max_version: clock::Global::new(),
|
||||||
min_insertion_version: clock::Global::new(),
|
min_insertion_version: clock::Global::new(),
|
||||||
|
@ -2087,6 +2108,33 @@ impl Default for FragmentSummary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
|
||||||
|
fn add_summary(&mut self, summary: &'a FragmentSummary, _: &Option<clock::Global>) {
|
||||||
|
self.visible += summary.text.visible;
|
||||||
|
self.deleted += summary.text.deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sum_tree::Item for InsertionFragment {
|
||||||
|
type Summary = InsertionSummary;
|
||||||
|
|
||||||
|
fn summary(&self) -> Self::Summary {
|
||||||
|
InsertionSummary {
|
||||||
|
max_timestamp: self.timestamp,
|
||||||
|
max_split_offset: self.split_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sum_tree::Summary for InsertionSummary {
|
||||||
|
type Context = ();
|
||||||
|
|
||||||
|
fn add_summary(&mut self, summary: &Self, cx: &()) {
|
||||||
|
self.max_timestamp = summary.max_timestamp;
|
||||||
|
self.max_split_offset = summary.max_split_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct FullOffset(pub usize);
|
pub struct FullOffset(pub usize);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue