@@ -5,6 +5,7 @@ mod text;
pub use anchor::*;
pub use point::*;
+use ropey::Rope;
use seahash::SeaHasher;
pub use selection::*;
use similar::{ChangeTag, TextDiff};
@@ -25,6 +26,7 @@ use std::{
cmp,
hash::BuildHasher,
iter::{self, Iterator},
+ mem,
ops::{AddAssign, Range},
str,
sync::Arc,
@@ -57,6 +59,8 @@ type HashMap<K, V> = std::collections::HashMap<K, V>;
type HashSet<T> = std::collections::HashSet<T>;
pub struct Buffer {
+ visible_text: Rope,
+ deleted_text: Rope,
fragments: SumTree<Fragment>,
insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
pub version: time::Global,
@@ -92,6 +96,7 @@ struct Transaction {
#[derive(Clone)]
pub struct History {
+ // TODO: Turn this into a String or Rope, maybe.
pub base_text: Arc<str>,
ops: HashMap<time::Local, EditOperation>,
undo_stack: Vec<Transaction>,
@@ -285,15 +290,14 @@ pub struct Insertion {
id: time::Local,
parent_id: time::Local,
offset_in_parent: usize,
- text: Text,
lamport_timestamp: time::Lamport,
}
#[derive(Eq, PartialEq, Clone, Debug)]
struct Fragment {
id: FragmentId,
- insertion: Insertion,
- text: Text,
+ insertion: Arc<Insertion>,
+ range_in_insertion: Range<usize>,
deletions: HashSet<time::Local>,
max_undos: time::Global,
visible: bool,
@@ -301,11 +305,24 @@ struct Fragment {
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct FragmentSummary {
- text_summary: TextSummary,
+ text: FragmentTextSummary,
max_fragment_id: FragmentId,
max_version: time::Global,
}
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
+struct FragmentTextSummary {
+ visible: TextSummary,
+ deleted: TextSummary,
+}
+
+impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
+ fn add_summary(&mut self, summary: &'a FragmentSummary) {
+ self.visible += &summary.text.visible;
+ self.deleted += &summary.text.deleted;
+ }
+}
+
#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd)]
struct FragmentExtent {
chars: usize,
@@ -348,7 +365,7 @@ pub struct EditOperation {
end_id: time::Local,
end_offset: usize,
version_in_range: time::Global,
- new_text: Option<Text>,
+ new_text: Option<String>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -418,16 +435,17 @@ impl Buffer {
saved_mtime = UNIX_EPOCH;
}
+ let mut visible_text = Rope::new();
let mut insertion_splits = HashMap::default();
let mut fragments = SumTree::new();
- let base_insertion = Insertion {
+ let base_text = Rope::from(history.base_text.as_ref());
+ let base_insertion = Arc::new(Insertion {
id: time::Local::default(),
parent_id: time::Local::default(),
offset_in_parent: 0,
- text: history.base_text.clone().into(),
lamport_timestamp: time::Lamport::default(),
- };
+ });
insertion_splits.insert(
base_insertion.id,
@@ -437,45 +455,43 @@ impl Buffer {
extent: 0,
},
&(),
+ &(),
),
);
fragments.push(
- Fragment {
- id: FragmentId::min_value().clone(),
- insertion: base_insertion.clone(),
- text: base_insertion.text.slice(0..0),
- deletions: Default::default(),
- max_undos: Default::default(),
- visible: true,
- },
+ Fragment::new(
+ FragmentId::min_value().clone(),
+ base_insertion.clone(),
+ 0..0,
+ ),
+ &FragmentContext::default(),
&(),
);
- if base_insertion.text.len() > 0 {
+ if base_text.len_chars() > 0 {
let base_fragment_id =
FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value());
+ let range_in_insertion = 0..base_text.len_chars();
+ visible_text = base_text.clone();
insertion_splits.get_mut(&base_insertion.id).unwrap().push(
InsertionSplit {
fragment_id: base_fragment_id.clone(),
- extent: base_insertion.text.len(),
+ extent: range_in_insertion.end,
},
&(),
+ &(),
);
fragments.push(
- Fragment {
- id: base_fragment_id,
- text: base_insertion.text.clone(),
- insertion: base_insertion,
- deletions: Default::default(),
- max_undos: Default::default(),
- visible: true,
- },
+ Fragment::new(base_fragment_id, base_insertion, range_in_insertion.clone()),
+ &FragmentContext::new(base_text, Rope::new(), Default::default()),
&(),
);
}
Self {
+ visible_text,
+ deleted_text: Rope::new(),
fragments,
insertion_splits,
version: time::Global::new(),
@@ -613,29 +629,7 @@ impl Buffer {
}
pub fn text_summary_for_range(&self, range: Range<usize>) -> TextSummary {
- let mut summary = TextSummary::default();
-
- let mut cursor = self.fragments.cursor::<usize, usize>();
- cursor.seek(&range.start, SeekBias::Right, &());
-
- if let Some(fragment) = cursor.item() {
- let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
- let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
- summary += fragment.text.slice(summary_start..summary_end).summary();
- cursor.next();
- }
-
- if range.end > *cursor.start() {
- summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
-
- if let Some(fragment) = cursor.item() {
- let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
- let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
- summary += fragment.text.slice(summary_start..summary_end).summary();
- }
- }
-
- summary
+ TextSummary::from(self.visible_text.slice(range))
}
pub fn len(&self) -> usize {
@@ -654,37 +648,39 @@ impl Buffer {
}
pub fn rightmost_point(&self) -> Point {
- self.fragments.summary().text_summary.rightmost_point
+ todo!()
+ // self.fragments.summary().text_summary.rightmost_point
}
pub fn rightmost_point_in_range(&self, range: Range<usize>) -> Point {
- let mut summary = TextSummary::default();
+ todo!()
+ // let mut summary = TextSummary::default();
- let mut cursor = self.fragments.cursor::<usize, usize>();
- cursor.seek(&range.start, SeekBias::Right, &());
+ // let mut cursor = self.fragments.cursor::<usize, usize>();
+ // cursor.seek(&range.start, SeekBias::Right, &());
- if let Some(fragment) = cursor.item() {
- let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
- let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
- summary += fragment.text.slice(summary_start..summary_end).summary();
- cursor.next();
- }
+ // if let Some(fragment) = cursor.item() {
+ // let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
+ // let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
+ // summary += fragment.text.slice(summary_start..summary_end).summary();
+ // cursor.next();
+ // }
- if range.end > *cursor.start() {
- summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
+ // if range.end > *cursor.start() {
+ // summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
- if let Some(fragment) = cursor.item() {
- let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
- let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
- summary += fragment.text.slice(summary_start..summary_end).summary();
- }
- }
+ // if let Some(fragment) = cursor.item() {
+ // let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
+ // let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
+ // summary += fragment.text.slice(summary_start..summary_end).summary();
+ // }
+ // }
- summary.rightmost_point
+ // summary.rightmost_point
}
pub fn max_point(&self) -> Point {
- self.fragments.extent()
+ TextSummary::from(&self.visible_text).lines
}
pub fn line(&self, row: u32) -> Result<String> {
@@ -807,7 +803,7 @@ impl Buffer {
where
I: IntoIterator<Item = Range<S>>,
S: ToOffset,
- T: Into<Text>,
+ T: Into<String>,
{
self.start_transaction_at(None, Instant::now())?;
@@ -827,7 +823,7 @@ impl Buffer {
old_ranges
.into_iter()
.filter(|old_range| new_text.is_some() || old_range.end > old_range.start),
- new_text.clone(),
+ new_text.into(),
);
for op in &ops {
@@ -1022,7 +1018,7 @@ impl Buffer {
edit.start_offset,
edit.end_id,
edit.end_offset,
- edit.new_text.as_ref().cloned(),
+ edit.new_text.as_deref(),
&edit.version_in_range,
edit.id,
lamport_timestamp,
@@ -1064,12 +1060,11 @@ impl Buffer {
start_offset: usize,
end_id: time::Local,
end_offset: usize,
- new_text: Option<Text>,
+ new_text: Option<&str>,
version_in_range: &time::Global,
local_timestamp: time::Local,
lamport_timestamp: time::Lamport,
) -> Result<()> {
- let mut new_text = new_text.as_ref().cloned();
let start_fragment_id = self.resolve_fragment_id(start_id, start_offset)?;
let end_fragment_id = self.resolve_fragment_id(end_id, end_offset)?;
@@ -1077,12 +1072,24 @@ impl Buffer {
let last_id = old_fragments.extent::<FragmentIdRef>().0.unwrap();
let last_id_ref = FragmentIdRef::new(&last_id);
- let mut cursor = old_fragments.cursor::<FragmentIdRef, ()>();
+ let mut cursor = old_fragments.cursor::<FragmentIdRef, FragmentTextSummary>();
let mut new_fragments =
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
+ let mut new_visible_text = self.visible_text.clone();
+ let mut new_deleted_text = self.deleted_text.clone();
- if start_offset == cursor.item().unwrap().end_offset() {
- new_fragments.push(cursor.item().unwrap().clone(), &());
+ let start_fragment = cursor.item().unwrap();
+ if start_offset == start_fragment.range_in_insertion.end {
+ // TODO: maybe don't recompute this fragment and its summary.
+ new_fragments.push(
+ cursor.item().unwrap().clone(),
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ cursor.start().clone(),
+ ),
+ &(),
+ );
cursor.next();
}
@@ -1097,12 +1104,12 @@ impl Buffer {
let split_start = if start_fragment_id == fragment.id {
start_offset
} else {
- fragment.start_offset()
+ fragment.range_in_insertion.start
};
let split_end = if end_fragment_id == fragment.id {
end_offset
} else {
- fragment.end_offset()
+ fragment.range_in_insertion.end
};
let (before_range, within_range, after_range) = self.split_fragment(
cursor.prev_item().as_ref().unwrap(),
@@ -1121,30 +1128,84 @@ impl Buffer {
None
};
if let Some(fragment) = before_range {
- new_fragments.push(fragment, &());
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
}
if let Some(fragment) = insertion {
- new_fragments.push(fragment, &());
+ new_visible_text.insert(
+ new_fragments.summary().text.visible.chars,
+ new_text.unwrap(),
+ );
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
}
if let Some(mut fragment) = within_range {
if fragment.was_visible(&version_in_range, &self.undo_map) {
fragment.deletions.insert(local_timestamp);
fragment.visible = false;
+
+ // TODO: avoid calling to_string on rope slice.
+ let deleted_start = new_fragments.summary().text.visible.chars;
+ let deleted_range = deleted_start..deleted_start + fragment.len();
+ new_deleted_text.insert(
+ new_fragments.summary().text.deleted.chars,
+ &new_visible_text.slice(deleted_range).to_string(),
+ );
+ new_visible_text.remove(deleted_range);
}
- new_fragments.push(fragment, &());
+
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
}
if let Some(fragment) = after_range {
- new_fragments.push(fragment, &());
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
}
} else {
if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
+ let new_text = new_text.take().unwrap();
+ let fragment = self.build_fragment_to_insert(
+ cursor.prev_item().as_ref().unwrap(),
+ Some(&fragment),
+ new_text,
+ local_timestamp,
+ lamport_timestamp,
+ );
+ new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
new_fragments.push(
- self.build_fragment_to_insert(
- cursor.prev_item().as_ref().unwrap(),
- Some(&fragment),
- new_text.take().unwrap(),
- local_timestamp,
- lamport_timestamp,
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
),
&(),
);
@@ -1155,27 +1216,54 @@ impl Buffer {
{
fragment.deletions.insert(local_timestamp);
fragment.visible = false;
+
+ // TODO: avoid calling to_string on rope slice.
+ let deleted_start = new_fragments.summary().text.visible.chars;
+ let deleted_range = deleted_start..deleted_start + fragment.len();
+ new_deleted_text.insert(
+ new_fragments.summary().text.deleted.chars,
+ &new_visible_text.slice(deleted_range).to_string(),
+ );
+ new_visible_text.remove(deleted_range);
}
- new_fragments.push(fragment, &());
+
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
}
cursor.next();
}
if let Some(new_text) = new_text {
+ let fragment = self.build_fragment_to_insert(
+ cursor.prev_item().as_ref().unwrap(),
+ None,
+ new_text,
+ local_timestamp,
+ lamport_timestamp,
+ );
+ new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
new_fragments.push(
- self.build_fragment_to_insert(
- cursor.prev_item().as_ref().unwrap(),
- None,
- new_text,
- local_timestamp,
- lamport_timestamp,
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
),
&(),
);
}
new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
+ self.visible_text = new_visible_text;
+ self.deleted_text = new_deleted_text;
self.fragments = new_fragments;
self.local_clock.observe(local_timestamp);
self.lamport_clock.observe(lamport_timestamp);
@@ -1251,6 +1339,8 @@ impl Buffer {
fn apply_undo(&mut self, undo: UndoOperation) -> Result<()> {
let mut new_fragments;
+ let mut new_visible_text = self.visible_text.clone();
+ let mut new_deleted_text = self.deleted_text.clone();
self.undo_map.insert(undo);
let edit = &self.history.ops[&undo.edit_id];
@@ -1267,9 +1357,38 @@ impl Buffer {
loop {
let mut fragment = cursor.item().unwrap().clone();
- fragment.visible = fragment.is_visible(&self.undo_map);
+ let was_visible =
+ mem::replace(&mut fragment.visible, fragment.is_visible(&self.undo_map));
fragment.max_undos.observe(undo.id);
- new_fragments.push(fragment, &());
+
+ // TODO: avoid calling to_string on rope slice.
+ if fragment.visible && !was_visible {
+ let visible_start = new_fragments.summary().text.deleted.chars;
+ let visible_range = visible_start..visible_start + fragment.len();
+ new_visible_text.insert(
+ new_fragments.summary().text.visible.chars,
+ &new_deleted_text.slice(visible_range).to_string(),
+ );
+ new_deleted_text.remove(visible_range);
+ } else if !fragment.visible && was_visible {
+ let deleted_start = new_fragments.summary().text.visible.chars;
+ let deleted_range = deleted_start..deleted_start + fragment.len();
+ new_deleted_text.insert(
+ new_fragments.summary().text.deleted.chars,
+ &new_visible_text.slice(deleted_range).to_string(),
+ );
+ new_visible_text.remove(deleted_range);
+ }
+
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
cursor.next();
if let Some(split_id) = insertion_splits.next() {
new_fragments.push_tree(
@@ -1291,10 +1410,41 @@ impl Buffer {
if edit.version_in_range.observed(fragment.insertion.id)
|| fragment.insertion.id == undo.edit_id
{
- fragment.visible = fragment.is_visible(&self.undo_map);
+ let was_visible = mem::replace(
+ &mut fragment.visible,
+ fragment.is_visible(&self.undo_map),
+ );
fragment.max_undos.observe(undo.id);
+
+ // TODO: avoid calling to_string on rope slice.
+ if fragment.visible && !was_visible {
+ let visible_start = new_fragments.summary().text.deleted.chars;
+ let visible_range = visible_start..visible_start + fragment.len();
+ new_visible_text.insert(
+ new_fragments.summary().text.visible.chars,
+ &new_deleted_text.slice(visible_range).to_string(),
+ );
+ new_deleted_text.remove(visible_range);
+ } else if !fragment.visible && was_visible {
+ let deleted_start = new_fragments.summary().text.visible.chars;
+ let deleted_range = deleted_start..deleted_start + fragment.len();
+ new_deleted_text.insert(
+ new_fragments.summary().text.deleted.chars,
+ &new_visible_text.slice(deleted_range).to_string(),
+ );
+ new_visible_text.remove(deleted_range);
+ }
}
- new_fragments.push(fragment, &());
+
+ new_fragments.push(
+ fragment,
+ &FragmentContext::new(
+ new_visible_text.clone(),
+ new_deleted_text.clone(),
+ new_fragments.summary().text,
+ ),
+ &(),
+ );
cursor.next();
}
}
@@ -1372,7 +1522,7 @@ impl Buffer {
.clone())
}
- fn splice_fragments<I>(&mut self, mut old_ranges: I, new_text: Option<Text>) -> Vec<Operation>
+ fn splice_fragments<I>(&mut self, mut old_ranges: I, new_text: Option<String>) -> Vec<Operation>
where
I: Iterator<Item = Range<usize>>,
{
@@ -1386,6 +1536,9 @@ impl Buffer {
let old_fragments = self.fragments.clone();
let mut cursor = old_fragments.cursor::<usize, usize>();
let mut new_fragments = SumTree::new();
+ let mut new_visible_text = self.visible_text.clone();
+ let mut new_deleted_text = self.deleted_text.clone();
+
new_fragments.push_tree(
cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
&(),
@@ -1412,7 +1565,7 @@ impl Buffer {
.unwrap();
let mut splits_cursor = old_split_tree.cursor::<usize, ()>();
let mut new_split_tree =
- splits_cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
+ splits_cursor.slice(&fragment.range_in_insertion.start, SeekBias::Right, &());
// Find all splices that start or end within the current fragment. Then, split the
// fragment and reassemble it in both trees accounting for the deleted and the newly
@@ -1421,38 +1574,43 @@ impl Buffer {
let range = cur_range.clone().unwrap();
if range.start > fragment_start {
let mut prefix = fragment.clone();
- prefix.set_end_offset(prefix.start_offset() + (range.start - fragment_start));
+ prefix.range_in_insertion.end =
+ prefix.range_in_insertion.start + (range.start - fragment_start);
prefix.id =
FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
- fragment.set_start_offset(prefix.end_offset());
- new_fragments.push(prefix.clone(), &());
+ fragment.range_in_insertion.start = prefix.range_in_insertion.end;
+ new_fragments.push(
+ prefix.clone(),
+ &FragmentContext::new(new_visible_text.clone()) & (),
+ );
new_split_tree.push(
InsertionSplit {
- extent: prefix.end_offset() - prefix.start_offset(),
+ extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start,
fragment_id: prefix.id,
},
&(),
+ &(),
);
fragment_start = range.start;
}
if range.end == fragment_start {
end_id = Some(new_fragments.last().unwrap().insertion.id);
- end_offset = Some(new_fragments.last().unwrap().end_offset());
+ end_offset = Some(new_fragments.last().unwrap().range_in_insertion.end);
} else if range.end == fragment_end {
end_id = Some(fragment.insertion.id);
- end_offset = Some(fragment.end_offset());
+ end_offset = Some(fragment.range_in_insertion.end);
}
if range.start == fragment_start {
start_id = Some(new_fragments.last().unwrap().insertion.id);
- start_offset = Some(new_fragments.last().unwrap().end_offset());
+ start_offset = Some(new_fragments.last().unwrap().range_in_insertion.end);
if let Some(new_text) = new_text.clone() {
let new_fragment = self.build_fragment_to_insert(
&new_fragments.last().unwrap(),
Some(&fragment),
- new_text,
+ &new_text,
local_timestamp,
lamport_timestamp,
);
@@ -1463,7 +1621,8 @@ impl Buffer {
if range.end < fragment_end {
if range.end > fragment_start {
let mut prefix = fragment.clone();
- prefix.set_end_offset(prefix.start_offset() + (range.end - fragment_start));
+ prefix.range_in_insertion.end =
+ prefix.range_in_insertion.start + (range.end - fragment_start);
prefix.id =
FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
version_in_range.observe_all(&fragment_summary.max_version);
@@ -1471,18 +1630,19 @@ impl Buffer {
prefix.deletions.insert(local_timestamp);
prefix.visible = false;
}
- fragment.set_start_offset(prefix.end_offset());
+ fragment.range_in_insertion.start = prefix.range_in_insertion.end;
new_fragments.push(prefix.clone(), &());
new_split_tree.push(
InsertionSplit {
- extent: prefix.end_offset() - prefix.start_offset(),
+ extent: prefix.range_in_insertion.end
+ - prefix.range_in_insertion.start,
fragment_id: prefix.id,
},
&(),
);
fragment_start = range.end;
end_id = Some(fragment.insertion.id);
- end_offset = Some(fragment.start_offset());
+ end_offset = Some(fragment.range_in_insertion.start);
}
} else {
version_in_range.observe_all(&fragment_summary.max_version);
@@ -1525,7 +1685,7 @@ impl Buffer {
}
new_split_tree.push(
InsertionSplit {
- extent: fragment.end_offset() - fragment.start_offset(),
+ extent: fragment.range_in_insertion.end - fragment.range_in_insertion.start,
fragment_id: fragment.id.clone(),
},
&(),
@@ -1558,7 +1718,7 @@ impl Buffer {
if range.end == fragment_end {
end_id = Some(fragment.insertion.id);
- end_offset = Some(fragment.end_offset());
+ end_offset = Some(fragment.range_in_insertion.end);
ops.push(Operation::Edit {
edit: EditOperation {
id: local_timestamp,
@@ -1611,9 +1771,9 @@ impl Buffer {
edit: EditOperation {
id: local_timestamp,
start_id: last_fragment.insertion.id,
- start_offset: last_fragment.end_offset(),
+ start_offset: last_fragment.range_in_insertion.end,
end_id: last_fragment.insertion.id,
- end_offset: last_fragment.end_offset(),
+ end_offset: last_fragment.range_in_insertion.end,
version_in_range: time::Global::new(),
new_text: new_text.clone(),
},
@@ -1647,24 +1807,26 @@ impl Buffer {
fragment: &Fragment,
range: Range<usize>,
) -> (Option<Fragment>, Option<Fragment>, Option<Fragment>) {
- debug_assert!(range.start >= fragment.start_offset());
- debug_assert!(range.start <= fragment.end_offset());
- debug_assert!(range.end <= fragment.end_offset());
- debug_assert!(range.end >= fragment.start_offset());
+ debug_assert!(range.start >= fragment.range_in_insertion.start);
+ debug_assert!(range.start <= fragment.range_in_insertion.end);
+ debug_assert!(range.end <= fragment.range_in_insertion.end);
+ debug_assert!(range.end >= fragment.range_in_insertion.start);
- if range.end == fragment.start_offset() {
+ if range.end == fragment.range_in_insertion.start {
(None, None, Some(fragment.clone()))
- } else if range.start == fragment.end_offset() {
+ } else if range.start == fragment.range_in_insertion.end {
(Some(fragment.clone()), None, None)
- } else if range.start == fragment.start_offset() && range.end == fragment.end_offset() {
+ } else if range.start == fragment.range_in_insertion.start
+ && range.end == fragment.range_in_insertion.end
+ {
(None, Some(fragment.clone()), None)
} else {
let mut prefix = fragment.clone();
- let after_range = if range.end < fragment.end_offset() {
+ let after_range = if range.end < fragment.range_in_insertion.end {
let mut suffix = prefix.clone();
- suffix.set_start_offset(range.end);
- prefix.set_end_offset(range.end);
+ suffix.range_in_insertion.start = range.end;
+ prefix.range_in_insertion.end = range.end;
prefix.id = FragmentId::between(&prev_fragment.id, &suffix.id);
Some(suffix)
} else {
@@ -1673,15 +1835,15 @@ impl Buffer {
let within_range = if range.start != range.end {
let mut suffix = prefix.clone();
- suffix.set_start_offset(range.start);
- prefix.set_end_offset(range.start);
+ suffix.range_in_insertion.start = range.start;
+ prefix.range_in_insertion.end = range.start;
prefix.id = FragmentId::between(&prev_fragment.id, &suffix.id);
Some(suffix)
} else {
None
};
- let before_range = if range.start > fragment.start_offset() {
+ let before_range = if range.start > fragment.range_in_insertion.start {
Some(prefix)
} else {
None
@@ -1692,15 +1854,17 @@ impl Buffer {
.remove(&fragment.insertion.id)
.unwrap();
let mut cursor = old_split_tree.cursor::<usize, ()>();
- let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
+ let mut new_split_tree =
+ cursor.slice(&fragment.range_in_insertion.start, SeekBias::Right, &());
if let Some(ref fragment) = before_range {
new_split_tree.push(
InsertionSplit {
- extent: range.start - fragment.start_offset(),
+ extent: range.start - fragment.range_in_insertion.start,
fragment_id: fragment.id.clone(),
},
&(),
+ &(),
);
}
@@ -1711,16 +1875,18 @@ impl Buffer {
fragment_id: fragment.id.clone(),
},
&(),
+ &(),
);
}
if let Some(ref fragment) = after_range {
new_split_tree.push(
InsertionSplit {
- extent: fragment.end_offset() - range.end,
+ extent: fragment.range_in_insertion.end - range.end,
fragment_id: fragment.id.clone(),
},
&(),
+ &(),
);
}
@@ -1741,8 +1907,8 @@ impl Buffer {
&mut self,
prev_fragment: &Fragment,
next_fragment: Option<&Fragment>,
- text: Text,
- local_timestamp: time::Local,
+ text: &str,
+ insertion_id: time::Local,
lamport_timestamp: time::Lamport,
) -> Fragment {
let new_fragment_id = FragmentId::between(
@@ -1752,25 +1918,28 @@ impl Buffer {
.unwrap_or(&FragmentId::max_value()),
);
+ // TODO: extent could be expressed in bytes, which would save a linear scan.
+ let range_in_insertion = 0..text.chars().count();
let mut split_tree = SumTree::new();
split_tree.push(
InsertionSplit {
- extent: text.len(),
+ extent: range_in_insertion.len(),
fragment_id: new_fragment_id.clone(),
},
&(),
+ &(),
);
- self.insertion_splits.insert(local_timestamp, split_tree);
+ self.insertion_splits.insert(insertion_id, split_tree);
Fragment::new(
new_fragment_id,
- Insertion {
- id: local_timestamp,
+ Arc::new(Insertion {
+ id: insertion_id,
parent_id: prev_fragment.insertion.id,
- offset_in_parent: prev_fragment.end_offset(),
- text,
+ offset_in_parent: prev_fragment.range_in_insertion.end,
lamport_timestamp,
- },
+ }),
+ range_in_insertion,
)
}
@@ -1811,7 +1980,7 @@ impl Buffer {
cursor.seek(&offset, seek_bias, &());
let fragment = cursor.item().unwrap();
let offset_in_fragment = offset - cursor.start();
- let offset_in_insertion = fragment.start_offset() + offset_in_fragment;
+ let offset_in_insertion = fragment.range_in_insertion.start + offset_in_fragment;
let anchor = Anchor::Middle {
insertion_id: fragment.insertion.id,
offset: offset_in_insertion,
@@ -1883,7 +2052,7 @@ impl Buffer {
if fragment.visible {
summary += fragment
.text
- .slice(..offset - fragment.start_offset())
+ .slice(..offset - fragment.range_in_insertion.start)
.summary();
}
Ok(summary)
@@ -1910,6 +2079,7 @@ impl Buffer {
impl Clone for Buffer {
fn clone(&self) -> Self {
Self {
+ visible_text: self.visible_text.clone(),
fragments: self.fragments.clone(),
insertion_splits: self.insertion_splits.clone(),
version: self.version.clone(),
@@ -2205,45 +2375,17 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentIdRef<'a> {
}
impl Fragment {
- fn new(id: FragmentId, insertion: Insertion) -> Self {
+ fn new(id: FragmentId, insertion: Arc<Insertion>, range_in_insertion: Range<usize>) -> Self {
Self {
id,
- text: insertion.text.clone(),
insertion,
+ range_in_insertion,
deletions: Default::default(),
max_undos: Default::default(),
visible: true,
}
}
- fn start_offset(&self) -> usize {
- self.text.range().start
- }
-
- fn set_start_offset(&mut self, offset: usize) {
- self.text = self.insertion.text.slice(offset..self.end_offset());
- }
-
- fn end_offset(&self) -> usize {
- self.text.range().end
- }
-
- fn set_end_offset(&mut self, offset: usize) {
- self.text = self.insertion.text.slice(self.start_offset()..offset);
- }
-
- fn visible_len(&self) -> usize {
- if self.visible {
- self.len()
- } else {
- 0
- }
- }
-
- fn len(&self) -> usize {
- self.text.len()
- }
-
fn is_visible(&self, undos: &UndoMap) -> bool {
!undos.is_undone(self.insertion.id) && self.deletions.iter().all(|d| undos.is_undone(*d))
}
@@ -2256,19 +2398,41 @@ impl Fragment {
.all(|d| !version.observed(*d) || undos.was_undone(*d, version))
}
- fn point_for_offset(&self, offset: usize) -> Result<Point> {
- Ok(self.text.point_for_offset(offset))
+ fn len(&self) -> usize {
+ self.range_in_insertion.len()
}
- fn offset_for_point(&self, point: Point) -> Result<usize> {
- Ok(self.text.offset_for_point(point))
+ fn visible_len(&self) -> usize {
+ if self.visible {
+ self.range_in_insertion.len()
+ } else {
+ 0
+ }
+ }
+}
+
+#[derive(Default)]
+struct FragmentContext {
+ visible_text: Rope,
+ deleted_text: Rope,
+ start: FragmentTextSummary,
+}
+
+impl FragmentContext {
+ fn new(visible_text: Rope, deleted_text: Rope, start: FragmentTextSummary) -> Self {
+ Self {
+ visible_text,
+ deleted_text,
+ start,
+ }
}
}
impl sum_tree::Item for Fragment {
+ type Context = FragmentContext;
type Summary = FragmentSummary;
- fn summary(&self) -> Self::Summary {
+ fn summary(&self, ctx: &FragmentContext) -> Self::Summary {
let mut max_version = time::Global::new();
max_version.observe(self.insertion.id);
for deletion in &self.deletions {
@@ -1,84 +1,45 @@
-use super::Point;
-use crate::sum_tree::{self, SeekBias, SumTree};
-use arrayvec::ArrayVec;
-use std::{
- cmp,
- fmt::{self, Debug},
- ops::{Bound, Index, Range, RangeBounds},
- sync::Arc,
-};
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-enum Run {
- Newline,
- Chars { len: usize, char_size: u8 },
-}
-
-#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
-struct ByteOffset(usize);
+use ropey::{Rope, RopeSlice};
-impl sum_tree::Item for Run {
- type Summary = TextSummary;
-
- fn summary(&self) -> Self::Summary {
- match *self {
- Run::Newline => TextSummary {
- chars: 1,
- bytes: 1,
- lines: Point::new(1, 0),
- first_line_len: 0,
- rightmost_point: Point::new(0, 0),
- },
- Run::Chars { len, char_size } => TextSummary {
- chars: len,
- bytes: len * char_size as usize,
- lines: Point::new(0, len as u32),
- first_line_len: len as u32,
- rightmost_point: Point::new(0, len as u32),
- },
- }
- }
-}
-
-impl Run {
- fn char_size(&self) -> u8 {
- match self {
- Run::Newline => 1,
- Run::Chars { char_size, .. } => *char_size,
- }
- }
-}
+use super::Point;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TextSummary {
pub chars: usize,
pub bytes: usize,
pub lines: Point,
- pub first_line_len: u32,
- pub rightmost_point: Point,
}
-impl sum_tree::Summary for TextSummary {
- type Context = ();
+impl<'a> From<RopeSlice<'a>> for TextSummary {
+ fn from(slice: RopeSlice<'a>) -> Self {
+ let last_row = slice.len_lines() - 1;
+ let last_column = slice.line(last_row).len_chars();
+ Self {
+ chars: slice.len_chars(),
+ bytes: slice.len_bytes(),
+ lines: Point::new(last_row as u32, last_column as u32),
+ }
+ }
+}
- fn add_summary(&mut self, other: &Self, _: &()) {
- *self += other;
+impl<'a> From<&'a Rope> for TextSummary {
+ fn from(text: &'a Rope) -> Self {
+ Self::from(text.slice(..))
}
}
impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
fn add_assign(&mut self, other: &'a Self) {
- let joined_line_len = self.lines.column + other.first_line_len;
- if joined_line_len > self.rightmost_point.column {
- self.rightmost_point = Point::new(self.lines.row, joined_line_len);
- }
- if other.rightmost_point.column > self.rightmost_point.column {
- self.rightmost_point = self.lines + &other.rightmost_point;
- }
-
- if self.lines.row == 0 {
- self.first_line_len += other.first_line_len;
- }
+ // let joined_line_len = self.lines.column + other.first_line_len;
+ // if joined_line_len > self.rightmost_point.column {
+ // self.rightmost_point = Point::new(self.lines.row, joined_line_len);
+ // }
+ // if other.rightmost_point.column > self.rightmost_point.column {
+ // self.rightmost_point = self.lines + &other.rightmost_point;
+ // }
+
+ // if self.lines.row == 0 {
+ // self.first_line_len += other.first_line_len;
+ // }
self.chars += other.chars;
self.bytes += other.bytes;
@@ -91,371 +52,3 @@ impl std::ops::AddAssign<Self> for TextSummary {
*self += &other;
}
}
-
-impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary {
- fn add_summary(&mut self, other: &TextSummary) {
- *self += other;
- }
-}
-
-impl<'a> sum_tree::Dimension<'a, TextSummary> for Point {
- fn add_summary(&mut self, summary: &TextSummary) {
- *self += &summary.lines;
- }
-}
-
-impl<'a> sum_tree::Dimension<'a, TextSummary> for ByteOffset {
- fn add_summary(&mut self, summary: &TextSummary) {
- self.0 += summary.bytes
- }
-}
-
-impl<'a> sum_tree::Dimension<'a, TextSummary> for usize {
- fn add_summary(&mut self, summary: &TextSummary) {
- *self += summary.chars;
- }
-}
-
-#[derive(Clone)]
-pub struct Text {
- text: Arc<str>,
- runs: SumTree<Run>,
- range: Range<usize>,
-}
-
-impl From<String> for Text {
- fn from(text: String) -> Self {
- Self::from(Arc::from(text))
- }
-}
-
-impl<'a> From<&'a str> for Text {
- fn from(text: &'a str) -> Self {
- Self::from(Arc::from(text))
- }
-}
-
-impl From<Arc<str>> for Text {
- fn from(text: Arc<str>) -> Self {
- let mut runs = Vec::new();
-
- let mut chars_len = 0;
- let mut run_char_size = 0;
- let mut run_chars = 0;
-
- let mut chars = text.chars();
- loop {
- let ch = chars.next();
- let ch_size = ch.map_or(0, |ch| ch.len_utf8());
- if run_chars != 0 && (ch.is_none() || ch == Some('\n') || run_char_size != ch_size) {
- runs.push(Run::Chars {
- len: run_chars,
- char_size: run_char_size as u8,
- });
- run_chars = 0;
- }
- run_char_size = ch_size;
-
- match ch {
- Some('\n') => runs.push(Run::Newline),
- Some(_) => run_chars += 1,
- None => break,
- }
- chars_len += 1;
- }
-
- let mut tree = SumTree::new();
- tree.extend(runs, &());
- Text {
- text,
- runs: tree,
- range: 0..chars_len,
- }
- }
-}
-
-impl Debug for Text {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("Text").field(&self.as_str()).finish()
- }
-}
-
-impl PartialEq for Text {
- fn eq(&self, other: &Self) -> bool {
- self.text == other.text
- }
-}
-
-impl Eq for Text {}
-
-impl<T: RangeBounds<usize>> Index<T> for Text {
- type Output = str;
-
- fn index(&self, range: T) -> &Self::Output {
- let start = match range.start_bound() {
- Bound::Included(start) => cmp::min(self.range.start + start, self.range.end),
- Bound::Excluded(_) => unimplemented!(),
- Bound::Unbounded => self.range.start,
- };
- let end = match range.end_bound() {
- Bound::Included(end) => cmp::min(self.range.start + end + 1, self.range.end),
- Bound::Excluded(end) => cmp::min(self.range.start + end, self.range.end),
- Bound::Unbounded => self.range.end,
- };
-
- let byte_start = self.abs_byte_offset_for_offset(start);
- let byte_end = self.abs_byte_offset_for_offset(end);
- &self.text[byte_start..byte_end]
- }
-}
-
-impl Text {
- pub fn range(&self) -> Range<usize> {
- self.range.clone()
- }
-
- pub fn as_str(&self) -> &str {
- &self[..]
- }
-
- pub fn slice<T: RangeBounds<usize>>(&self, range: T) -> Text {
- let start = match range.start_bound() {
- Bound::Included(start) => cmp::min(self.range.start + start, self.range.end),
- Bound::Excluded(_) => unimplemented!(),
- Bound::Unbounded => self.range.start,
- };
- let end = match range.end_bound() {
- Bound::Included(end) => cmp::min(self.range.start + end + 1, self.range.end),
- Bound::Excluded(end) => cmp::min(self.range.start + end, self.range.end),
- Bound::Unbounded => self.range.end,
- };
-
- Text {
- text: self.text.clone(),
- runs: self.runs.clone(),
- range: start..end,
- }
- }
-
- pub fn line_len(&self, row: u32) -> u32 {
- let mut cursor = self.runs.cursor::<usize, Point>();
- cursor.seek(&self.range.start, SeekBias::Right, &());
- let absolute_row = cursor.start().row + row;
-
- let mut cursor = self.runs.cursor::<Point, usize>();
- cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right, &());
- let prefix_len = self.range.start.saturating_sub(*cursor.start());
- let line_len =
- cursor.summary::<usize>(&Point::new(absolute_row + 1, 0), SeekBias::Left, &());
- let suffix_len = cursor.start().saturating_sub(self.range.end);
-
- line_len
- .saturating_sub(prefix_len)
- .saturating_sub(suffix_len) as u32
- }
-
- pub fn len(&self) -> usize {
- self.range.end - self.range.start
- }
-
- pub fn lines(&self) -> Point {
- self.abs_point_for_offset(self.range.end) - &self.abs_point_for_offset(self.range.start)
- }
-
- pub fn rightmost_point(&self) -> Point {
- let lines = self.lines();
-
- let mut candidates = ArrayVec::<[Point; 3]>::new();
- candidates.push(lines);
- if lines.row > 0 {
- candidates.push(Point::new(0, self.line_len(0)));
- if lines.row > 1 {
- let mut cursor = self.runs.cursor::<usize, Point>();
- cursor.seek(&self.range.start, SeekBias::Right, &());
- let absolute_start_row = cursor.start().row;
-
- let mut cursor = self.runs.cursor::<Point, usize>();
- cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right, &());
- let summary = cursor.summary::<TextSummary>(
- &Point::new(absolute_start_row + lines.row, 0),
- SeekBias::Left,
- &(),
- );
-
- candidates.push(Point::new(1, 0) + &summary.rightmost_point);
- }
- }
-
- candidates.into_iter().max_by_key(|p| p.column).unwrap()
- }
-
- pub fn point_for_offset(&self, offset: usize) -> Point {
- self.abs_point_for_offset(self.range.start + offset)
- - &self.abs_point_for_offset(self.range.start)
- }
-
- pub fn offset_for_point(&self, point: Point) -> usize {
- let mut cursor = self.runs.cursor::<Point, TextSummary>();
- let abs_point = self.abs_point_for_offset(self.range.start) + &point;
- cursor.seek(&abs_point, SeekBias::Right, &());
- let overshoot = abs_point - &cursor.start().lines;
- let abs_offset = cursor.start().chars + overshoot.column as usize;
- abs_offset - self.range.start
- }
-
- pub fn summary(&self) -> TextSummary {
- TextSummary {
- chars: self.range.end - self.range.start,
- bytes: self.abs_byte_offset_for_offset(self.range.end)
- - self.abs_byte_offset_for_offset(self.range.start),
- lines: self.abs_point_for_offset(self.range.end)
- - &self.abs_point_for_offset(self.range.start),
- first_line_len: self.line_len(0),
- rightmost_point: self.rightmost_point(),
- }
- }
-
- fn abs_point_for_offset(&self, offset: usize) -> Point {
- let mut cursor = self.runs.cursor::<usize, TextSummary>();
- cursor.seek(&offset, SeekBias::Right, &());
- let overshoot = (offset - cursor.start().chars) as u32;
- cursor.start().lines + &Point::new(0, overshoot)
- }
-
- fn abs_byte_offset_for_offset(&self, offset: usize) -> usize {
- let mut cursor = self.runs.cursor::<usize, TextSummary>();
- cursor.seek(&offset, SeekBias::Right, &());
- let overshoot = offset - cursor.start().chars;
- cursor.start().bytes + overshoot * cursor.item().map_or(0, |run| run.char_size()) as usize
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::collections::HashSet;
- use std::iter::FromIterator;
-
- #[test]
- fn test_basic() {
- let text = Text::from(String::from("ab\ncd€\nfghij\nkl¢m"));
- assert_eq!(text.len(), 17);
- assert_eq!(text.as_str(), "ab\ncd€\nfghij\nkl¢m");
- assert_eq!(text.lines(), Point::new(3, 4));
- assert_eq!(text.line_len(0), 2);
- assert_eq!(text.line_len(1), 3);
- assert_eq!(text.line_len(2), 5);
- assert_eq!(text.line_len(3), 4);
- assert_eq!(text.rightmost_point(), Point::new(2, 5));
-
- let b_to_g = text.slice(1..9);
- assert_eq!(b_to_g.as_str(), "b\ncd€\nfg");
- assert_eq!(b_to_g.len(), 8);
- assert_eq!(b_to_g.lines(), Point::new(2, 2));
- assert_eq!(b_to_g.line_len(0), 1);
- assert_eq!(b_to_g.line_len(1), 3);
- assert_eq!(b_to_g.line_len(2), 2);
- assert_eq!(b_to_g.line_len(3), 0);
- assert_eq!(b_to_g.rightmost_point(), Point::new(1, 3));
-
- let d_to_i = text.slice(4..11);
- assert_eq!(d_to_i.as_str(), "d€\nfghi");
- assert_eq!(&d_to_i[1..5], "€\nfg");
- assert_eq!(d_to_i.len(), 7);
- assert_eq!(d_to_i.lines(), Point::new(1, 4));
- assert_eq!(d_to_i.line_len(0), 2);
- assert_eq!(d_to_i.line_len(1), 4);
- assert_eq!(d_to_i.line_len(2), 0);
- assert_eq!(d_to_i.rightmost_point(), Point::new(1, 4));
-
- let d_to_j = text.slice(4..=11);
- assert_eq!(d_to_j.as_str(), "d€\nfghij");
- assert_eq!(&d_to_j[1..], "€\nfghij");
- assert_eq!(d_to_j.len(), 8);
- }
-
- #[test]
- fn test_random() {
- use rand::prelude::*;
-
- for seed in 0..100 {
- println!("buffer::text seed: {}", seed);
- let rng = &mut StdRng::seed_from_u64(seed);
-
- let len = rng.gen_range(0..50);
- let mut string = String::new();
- for _ in 0..len {
- if rng.gen_ratio(1, 5) {
- string.push('\n');
- } else {
- string.push(rng.gen());
- }
- }
- let text = Text::from(string.clone());
-
- for _ in 0..10 {
- let start = rng.gen_range(0..text.len() + 1);
- let end = rng.gen_range(start..text.len() + 2);
-
- let string_slice = string
- .chars()
- .skip(start)
- .take(end - start)
- .collect::<String>();
- let expected_line_endpoints = string_slice
- .split('\n')
- .enumerate()
- .map(|(row, line)| Point::new(row as u32, line.chars().count() as u32))
- .collect::<Vec<_>>();
- let text_slice = text.slice(start..end);
-
- assert_eq!(text_slice.lines(), lines(&string_slice));
-
- let mut rightmost_points: HashSet<Point> = HashSet::new();
- for endpoint in &expected_line_endpoints {
- if let Some(rightmost_point) = rightmost_points.iter().next().cloned() {
- if endpoint.column > rightmost_point.column {
- rightmost_points.clear();
- }
- if endpoint.column >= rightmost_point.column {
- rightmost_points.insert(*endpoint);
- }
- } else {
- rightmost_points.insert(*endpoint);
- }
-
- assert_eq!(text_slice.line_len(endpoint.row as u32), endpoint.column);
- }
-
- assert!(rightmost_points.contains(&text_slice.rightmost_point()));
-
- for _ in 0..10 {
- let offset = rng.gen_range(0..string_slice.chars().count() + 1);
- let point = lines(&string_slice.chars().take(offset).collect::<String>());
- assert_eq!(text_slice.point_for_offset(offset), point);
- assert_eq!(text_slice.offset_for_point(point), offset);
- if offset < string_slice.chars().count() {
- assert_eq!(
- &text_slice[offset..offset + 1],
- String::from_iter(string_slice.chars().nth(offset)).as_str()
- );
- }
- }
- }
- }
- }
-
- pub fn lines(s: &str) -> Point {
- let mut row = 0;
- let mut column = 0;
- for ch in s.chars() {
- if ch == '\n' {
- row += 1;
- column = 0;
- } else {
- column += 1;
- }
- }
- Point::new(row, column)
- }
-}