From 0e9441e5cde9f08e7130b51e68315a33454b9e19 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 May 2021 18:35:48 +0200 Subject: [PATCH 01/20] WIP Co-Authored-By: Nathan Sobo --- Cargo.lock | 10 + zed/Cargo.toml | 1 + zed/src/editor/buffer/mod.rs | 546 ++++++++++++++++--------- zed/src/editor/buffer/text.rs | 461 ++------------------- zed/src/editor/display_map/fold_map.rs | 20 +- zed/src/operation_queue.rs | 3 +- zed/src/sum_tree/mod.rs | 111 ++--- zed/src/worktree.rs | 3 +- 8 files changed, 478 insertions(+), 677 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21a08332c5e08e81c35c0d9d4db343a38983d0d6..d6e0804b04a7f9d441d0d30aebfd2fe4e9817d8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2188,6 +2188,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" +[[package]] +name = "ropey" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f3ef16589fdbb3e8fbce3dca944c08e61f39c7f16064b21a257d68ea911a83" +dependencies = [ + "smallvec", +] + [[package]] name = "roxmltree" version = "0.14.1" @@ -2978,6 +2987,7 @@ dependencies = [ "parking_lot", "postage", "rand 0.8.3", + "ropey", "rust-embed", "seahash", "serde 1.0.125", diff --git a/zed/Cargo.toml b/zed/Cargo.toml index 2302fc650954c18cb5ee21b902087436575e8006..370f580484df803ac439fe52942fbea1e3e9257b 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -31,6 +31,7 @@ num_cpus = "1.13.0" parking_lot = "0.11.1" postage = {version = "0.4.1", features = ["futures-traits"]} rand = "0.8.3" +ropey = "1.2" rust-embed = "5.9.0" seahash = "4.1" serde = {version = "1", features = ["derive"]} diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 1bdf050f0539dba4553782d41f82e07abfe6fde2..a2caa6cb380a224f9f9266acc5e30bb7d002a461 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -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 = std::collections::HashMap; type HashSet = std::collections::HashSet; pub struct Buffer { + visible_text: Rope, + deleted_text: Rope, fragments: SumTree, insertion_splits: HashMap>, 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, ops: HashMap, undo_stack: Vec, @@ -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, + range_in_insertion: Range, deletions: HashSet, 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, + new_text: Option, } #[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) -> TextSummary { - let mut summary = TextSummary::default(); - - let mut cursor = self.fragments.cursor::(); - 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::(&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) -> Point { - let mut summary = TextSummary::default(); + todo!() + // let mut summary = TextSummary::default(); - let mut cursor = self.fragments.cursor::(); - cursor.seek(&range.start, SeekBias::Right, &()); + // let mut cursor = self.fragments.cursor::(); + // 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::(&range.end, SeekBias::Right, &()); + // if range.end > *cursor.start() { + // summary += cursor.summary::(&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 { @@ -807,7 +803,7 @@ impl Buffer { where I: IntoIterator>, S: ToOffset, - T: Into, + T: Into, { 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, + 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::().0.unwrap(); let last_id_ref = FragmentIdRef::new(&last_id); - let mut cursor = old_fragments.cursor::(); + let mut cursor = old_fragments.cursor::(); 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(&mut self, mut old_ranges: I, new_text: Option) -> Vec + fn splice_fragments(&mut self, mut old_ranges: I, new_text: Option) -> Vec where I: Iterator>, { @@ -1386,6 +1536,9 @@ impl Buffer { let old_fragments = self.fragments.clone(); let mut cursor = old_fragments.cursor::(); 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::(); 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, ) -> (Option, Option, Option) { - 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::(); - 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, range_in_insertion: Range) -> 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 { - Ok(self.text.point_for_offset(offset)) + fn len(&self) -> usize { + self.range_in_insertion.len() } - fn offset_for_point(&self, point: Point) -> Result { - 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 { @@ -2277,14 +2441,22 @@ impl sum_tree::Item for Fragment { max_version.observe_all(&self.max_undos); if self.visible { + let start = ctx.start.visible.chars; FragmentSummary { - text_summary: self.text.summary(), + text: FragmentTextSummary { + visible: TextSummary::from(ctx.visible_text.slice(start..start + self.len())), + deleted: TextSummary::default(), + }, max_fragment_id: self.id.clone(), max_version, } } else { + let start = ctx.start.deleted.chars; FragmentSummary { - text_summary: TextSummary::default(), + text: FragmentTextSummary { + visible: TextSummary::default(), + deleted: TextSummary::from(ctx.deleted_text.slice(start..start + self.len())), + }, max_fragment_id: self.id.clone(), max_version, } @@ -2295,8 +2467,9 @@ impl sum_tree::Item for Fragment { impl sum_tree::Summary for FragmentSummary { type Context = (); - fn add_summary(&mut self, other: &Self, _: &()) { - self.text_summary += &other.text_summary; + fn add_summary(&mut self, other: &Self, _: &Self::Context) { + self.text.visible += &other.text.visible; + self.text.deleted += &other.text.deleted; debug_assert!(self.max_fragment_id <= other.max_fragment_id); self.max_fragment_id = other.max_fragment_id.clone(); self.max_version.observe_all(&other.max_version); @@ -2306,7 +2479,7 @@ impl sum_tree::Summary for FragmentSummary { impl Default for FragmentSummary { fn default() -> Self { FragmentSummary { - text_summary: TextSummary::default(), + text: FragmentTextSummary::default(), max_fragment_id: FragmentId::min_value().clone(), max_version: time::Global::new(), } @@ -2349,9 +2522,10 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for usize { } impl sum_tree::Item for InsertionSplit { + type Context = (); type Summary = InsertionSplitSummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { InsertionSplitSummary { extent: self.extent, } diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 03b8b528547c5abab29bd6e650835044a9ebe684..715c6774628f075accb988a4899d94702087ecde 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -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> 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 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, - runs: SumTree, - range: Range, -} - -impl From 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> for Text { - fn from(text: Arc) -> 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> Index 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 { - self.range.clone() - } - - pub fn as_str(&self) -> &str { - &self[..] - } - - pub fn slice>(&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::(); - cursor.seek(&self.range.start, SeekBias::Right, &()); - let absolute_row = cursor.start().row + row; - - let mut cursor = self.runs.cursor::(); - cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right, &()); - let prefix_len = self.range.start.saturating_sub(*cursor.start()); - let line_len = - cursor.summary::(&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::(); - cursor.seek(&self.range.start, SeekBias::Right, &()); - let absolute_start_row = cursor.start().row; - - let mut cursor = self.runs.cursor::(); - cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right, &()); - let summary = cursor.summary::( - &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::(); - 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::(); - 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::(); - 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::(); - let expected_line_endpoints = string_slice - .split('\n') - .enumerate() - .map(|(row, line)| Point::new(row as u32, line.chars().count() as u32)) - .collect::>(); - let text_slice = text.slice(start..end); - - assert_eq!(text_slice.lines(), lines(&string_slice)); - - let mut rightmost_points: HashSet = 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::()); - 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) - } -} diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index e33013565f66fc1a2d0a650850a1ad7e46e751e0..d0cbe60c5817d9c31da3fab2841ba94eeb8fea29 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -38,6 +38,7 @@ impl FoldMap { display_text: None, }, &(), + &(), )), last_sync: Mutex::new(buffer.version()), } @@ -72,7 +73,8 @@ impl FoldMap { } pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint { - DisplayPoint(self.sync(ctx).summary().display.rightmost_point) + todo!() + // DisplayPoint(self.sync(ctx).summary().display.rightmost_point) } pub fn folds_in_range<'a, T>( @@ -122,7 +124,7 @@ impl FoldMap { let mut cursor = self.folds.cursor::<_, ()>(); for fold in folds { new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer); - new_tree.push(fold, buffer); + new_tree.push(fold, &(), buffer); } new_tree.push_tree(cursor.suffix(buffer), buffer); new_tree @@ -341,6 +343,7 @@ impl FoldMap { display_text: None, }, &(), + &(), ); } @@ -352,14 +355,15 @@ impl FoldMap { chars: 1, bytes: '…'.len_utf8(), lines: Point::new(0, 1), - first_line_len: 1, - rightmost_point: Point::new(0, 1), + // first_line_len: 1, + // rightmost_point: Point::new(0, 1), }, buffer: buffer.text_summary_for_range(fold.start..fold.end), }, display_text: Some('…'), }, &(), + &(), ); } } @@ -377,6 +381,7 @@ impl FoldMap { display_text: None, }, &(), + &(), ); } } @@ -393,6 +398,7 @@ impl FoldMap { display_text: None, }, &(), + &(), ); } @@ -465,9 +471,10 @@ struct TransformSummary { } impl sum_tree::Item for Transform { + type Context = (); type Summary = TransformSummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { self.summary.clone() } } @@ -497,9 +504,10 @@ impl Default for Fold { } impl sum_tree::Item for Fold { + type Context = (); type Summary = FoldSummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { FoldSummary { start: self.0.start.clone(), end: self.0.end.clone(), diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index 2c0e234fe98cd30c6d3fa72b210a93c0441b8249..5fd24fbe976dcf8a83fcf8d42394a90743cf1125 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -48,9 +48,10 @@ impl OperationQueue { } impl Item for T { + type Context = (); type Summary = OperationSummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { OperationSummary { key: OperationKey(self.timestamp()), len: 1, diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index e70440f16f0c0bfeb811c8ad1aa6167adc85b253..2cbd928aba5f0714f553051c4a358afa3b067575 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -11,12 +11,13 @@ const TREE_BASE: usize = 2; const TREE_BASE: usize = 6; pub trait Item: Clone + fmt::Debug { + type Context; type Summary: Summary; - fn summary(&self) -> Self::Summary; + fn summary(&self, ctx: &Self::Context) -> Self::Summary; } -pub trait KeyedItem: Item { +pub trait KeyedItem: Item { type Key: for<'a> Dimension<'a, Self::Summary> + Ord; fn key(&self) -> Self::Key; @@ -64,9 +65,13 @@ impl SumTree { })) } - pub fn from_item(item: T, ctx: &::Context) -> Self { + pub fn from_item( + item: T, + item_ctx: &T::Context, + summary_ctx: &::Context, + ) -> Self { let mut tree = Self::new(); - tree.push(item, ctx); + tree.push(item, item_ctx, summary_ctx); tree } @@ -125,47 +130,13 @@ impl SumTree { } } - pub fn extend(&mut self, iter: I, ctx: &::Context) - where - I: IntoIterator, - { - let mut leaf: Option> = None; - - for item in iter { - if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); - } - - if leaf.is_none() { - leaf = Some(Node::Leaf:: { - summary: T::Summary::default(), - items: ArrayVec::new(), - item_summaries: ArrayVec::new(), - }); - } - - if let Some(Node::Leaf { - summary, - items, - item_summaries, - }) = leaf.as_mut() - { - let item_summary = item.summary(); - summary.add_summary(&item_summary, ctx); - items.push(item); - item_summaries.push(item_summary); - } else { - unreachable!() - } - } - - if leaf.is_some() { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); - } - } - - pub fn push(&mut self, item: T, ctx: &::Context) { - let summary = item.summary(); + pub fn push( + &mut self, + item: T, + item_ctx: &T::Context, + summary_ctx: &::Context, + ) { + let summary = item.summary(item_ctx); self.push_tree( SumTree::from_child_trees( vec![SumTree(Arc::new(Node::Leaf { @@ -173,9 +144,9 @@ impl SumTree { items: ArrayVec::from_iter(Some(item)), item_summaries: ArrayVec::from_iter(Some(summary)), }))], - ctx, + summary_ctx, ), - ctx, + summary_ctx, ) } @@ -349,13 +320,54 @@ impl SumTree { } } +impl> SumTree { + pub fn extend(&mut self, iter: I, ctx: &::Context) + where + I: IntoIterator, + { + let mut leaf: Option> = None; + + for item in iter { + if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE { + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); + } + + if leaf.is_none() { + leaf = Some(Node::Leaf:: { + summary: T::Summary::default(), + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + }); + } + + if let Some(Node::Leaf { + summary, + items, + item_summaries, + }) = leaf.as_mut() + { + let item_summary = item.summary(&()); + summary.add_summary(&item_summary, ctx); + items.push(item); + item_summaries.push(item_summary); + } else { + unreachable!() + } + } + + if leaf.is_some() { + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); + } + } +} + impl SumTree { #[allow(unused)] pub fn insert(&mut self, item: T, ctx: &::Context) { *self = { let mut cursor = self.cursor::(); let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx); - new_tree.push(item, ctx); + new_tree.push(item, &(), ctx); new_tree.push_tree(cursor.suffix(ctx), ctx); new_tree }; @@ -863,9 +875,10 @@ mod tests { struct Sum(usize); impl Item for u8 { + type Context = (); type Summary = IntegersSummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { IntegersSummary { count: Count(1), sum: Sum(*self as usize), diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 0f69ea833113b37f87b4658a12b407a757368897..7d7356f2d2fc68219fc85757ec8a605172b5c025 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -538,9 +538,10 @@ impl Entry { } impl sum_tree::Item for Entry { + type Context = (); type Summary = EntrySummary; - fn summary(&self) -> Self::Summary { + fn summary(&self, _: &()) -> Self::Summary { let file_count; let visible_file_count; if self.is_file() { From 7b98fb33dd697cb65a1f0a01a9f12cfcd0818345 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 May 2021 19:24:03 +0200 Subject: [PATCH 02/20] WIP: Get the type checker passing... ...but not the borrow checker! Co-Authored-By: Max Brunsfeld --- zed/src/editor/buffer/mod.rs | 526 +++++++++---------------- zed/src/editor/buffer/text.rs | 54 --- zed/src/editor/display_map/fold_map.rs | 19 +- zed/src/editor/display_map/mod.rs | 2 +- zed/src/operation_queue.rs | 3 +- zed/src/sum_tree/mod.rs | 111 +++--- zed/src/worktree.rs | 19 +- 7 files changed, 240 insertions(+), 494 deletions(-) delete mode 100644 zed/src/editor/buffer/text.rs diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index a2caa6cb380a224f9f9266acc5e30bb7d002a461..735846dd8d3740949edb5b9d1229b31fdfe84e56 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1,15 +1,13 @@ mod anchor; mod point; mod selection; -mod text; pub use anchor::*; pub use point::*; -use ropey::Rope; +use ropey::{Rope, RopeSlice}; use seahash::SeaHasher; pub use selection::*; use similar::{ChangeTag, TextDiff}; -pub use text::*; use crate::{ operation_queue::{self, OperationQueue}, @@ -79,10 +77,6 @@ pub struct Buffer { lamport_clock: time::Lamport, } -pub struct Snapshot { - fragments: SumTree, -} - #[derive(Clone)] struct Transaction { start: time::Global, @@ -243,18 +237,6 @@ impl UndoMap { } } -#[derive(Clone)] -pub struct CharIter<'a> { - fragments_cursor: Cursor<'a, Fragment, usize, usize>, - fragment_chars: str::Chars<'a>, -} - -#[derive(Clone)] -pub struct FragmentIter<'a> { - cursor: Cursor<'a, Fragment, usize, usize>, - started: bool, -} - struct Edits<'a, F: Fn(&FragmentSummary) -> bool> { cursor: FilterCursor<'a, F, Fragment, usize>, undos: &'a UndoMap, @@ -312,21 +294,60 @@ pub struct FragmentSummary { #[derive(Default, Clone, Debug, PartialEq, Eq)] struct FragmentTextSummary { - visible: TextSummary, - deleted: TextSummary, + visible: usize, + deleted: usize, } 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; + self.visible += summary.text.visible; + self.deleted += summary.text.deleted; } } -#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd)] -struct FragmentExtent { - chars: usize, - lines: Point, +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct TextSummary { + pub chars: usize, + pub bytes: usize, + pub lines: Point, +} + +impl<'a> From> 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), + } + } +} + +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; + // } + + self.chars += other.chars; + self.bytes += other.bytes; + self.lines += &other.lines; + } +} + +impl std::ops::AddAssign for TextSummary { + fn add_assign(&mut self, other: Self) { + *self += &other; + } } #[derive(Eq, PartialEq, Clone, Debug)] @@ -455,7 +476,6 @@ impl Buffer { extent: 0, }, &(), - &(), ), ); fragments.push( @@ -464,7 +484,6 @@ impl Buffer { base_insertion.clone(), 0..0, ), - &FragmentContext::default(), &(), ); @@ -480,11 +499,9 @@ impl Buffer { extent: range_in_insertion.end, }, &(), - &(), ); fragments.push( Fragment::new(base_fragment_id, base_insertion, range_in_insertion.clone()), - &FragmentContext::new(base_text, Rope::new(), Default::default()), &(), ); } @@ -511,10 +528,8 @@ impl Buffer { } } - pub fn snapshot(&self) -> Snapshot { - Snapshot { - fragments: self.fragments.clone(), - } + pub fn snapshot(&self) -> Rope { + self.visible_text.clone() } pub fn file(&self) -> Option<&FileHandle> { @@ -625,7 +640,7 @@ impl Buffer { } pub fn text_summary(&self) -> TextSummary { - self.fragments.extent::() + TextSummary::from(self.visible_text.slice(..)) } pub fn text_summary_for_range(&self, range: Range) -> TextSummary { @@ -680,7 +695,7 @@ impl Buffer { } pub fn max_point(&self) -> Point { - TextSummary::from(&self.visible_text).lines + self.text_summary().lines } pub fn line(&self, row: u32) -> Result { @@ -703,13 +718,13 @@ impl Buffer { Ok(self.chars_at(start)?.take(end - start)) } - pub fn chars(&self) -> CharIter { + pub fn chars(&self) -> ropey::iter::Chars { self.chars_at(0).unwrap() } - pub fn chars_at(&self, position: T) -> Result { + pub fn chars_at(&self, position: T) -> Result { let offset = position.to_offset(self)?; - Ok(CharIter::new(&self.fragments, offset)) + Ok(self.visible_text.chars_at(offset)) } pub fn selections_changed_since(&self, since: SelectionsVersion) -> bool { @@ -1081,15 +1096,7 @@ impl Buffer { 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(), - ), - &(), - ); + new_fragments.push(cursor.item().unwrap().clone(), &()); cursor.next(); } @@ -1128,30 +1135,12 @@ impl Buffer { None }; if let Some(fragment) = before_range { - new_fragments.push( - fragment, - &FragmentContext::new( - new_visible_text.clone(), - new_deleted_text.clone(), - new_fragments.summary().text, - ), - &(), - ); + new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - 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, - ), - &(), - ); + new_visible_text + .insert(new_fragments.summary().text.visible, new_text.unwrap()); + new_fragments.push(fragment, &()); } if let Some(mut fragment) = within_range { if fragment.was_visible(&version_in_range, &self.undo_map) { @@ -1159,35 +1148,19 @@ impl Buffer { fragment.visible = false; // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible.chars; + let deleted_start = new_fragments.summary().text.visible; let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( - new_fragments.summary().text.deleted.chars, + new_fragments.summary().text.deleted, &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, - ), - &(), - ); + new_fragments.push(fragment, &()); } if let Some(fragment) = after_range { - new_fragments.push( - fragment, - &FragmentContext::new( - new_visible_text.clone(), - new_deleted_text.clone(), - new_fragments.summary().text, - ), - &(), - ); + new_fragments.push(fragment, &()); } } else { if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp { @@ -1199,16 +1172,8 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text); - new_fragments.push( - fragment, - &FragmentContext::new( - new_visible_text.clone(), - new_deleted_text.clone(), - new_fragments.summary().text, - ), - &(), - ); + new_visible_text.insert(new_fragments.summary().text.visible, new_text); + new_fragments.push(fragment, &()); } if fragment.id < end_fragment_id @@ -1218,24 +1183,16 @@ impl Buffer { fragment.visible = false; // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible.chars; + let deleted_start = new_fragments.summary().text.visible; let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( - new_fragments.summary().text.deleted.chars, + new_fragments.summary().text.deleted, &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, - ), - &(), - ); + new_fragments.push(fragment, &()); } cursor.next(); @@ -1249,16 +1206,8 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text); - new_fragments.push( - fragment, - &FragmentContext::new( - new_visible_text.clone(), - new_deleted_text.clone(), - new_fragments.summary().text, - ), - &(), - ); + new_visible_text.insert(new_fragments.summary().text.visible, new_text); + new_fragments.push(fragment, &()); } new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &()); @@ -1363,32 +1312,24 @@ impl Buffer { // TODO: avoid calling to_string on rope slice. if fragment.visible && !was_visible { - let visible_start = new_fragments.summary().text.deleted.chars; + let visible_start = new_fragments.summary().text.deleted; let visible_range = visible_start..visible_start + fragment.len(); new_visible_text.insert( - new_fragments.summary().text.visible.chars, + new_fragments.summary().text.visible, &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_start = new_fragments.summary().text.visible; let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( - new_fragments.summary().text.deleted.chars, + new_fragments.summary().text.deleted, &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, - ), - &(), - ); + new_fragments.push(fragment, &()); cursor.next(); if let Some(split_id) = insertion_splits.next() { new_fragments.push_tree( @@ -1418,33 +1359,25 @@ impl Buffer { // TODO: avoid calling to_string on rope slice. if fragment.visible && !was_visible { - let visible_start = new_fragments.summary().text.deleted.chars; + let visible_start = new_fragments.summary().text.deleted; let visible_range = visible_start..visible_start + fragment.len(); new_visible_text.insert( - new_fragments.summary().text.visible.chars, + new_fragments.summary().text.visible, &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_start = new_fragments.summary().text.visible; let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( - new_fragments.summary().text.deleted.chars, + new_fragments.summary().text.deleted, &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, - ), - &(), - ); + new_fragments.push(fragment, &()); cursor.next(); } } @@ -1579,17 +1512,13 @@ impl Buffer { prefix.id = FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id); fragment.range_in_insertion.start = prefix.range_in_insertion.end; - new_fragments.push( - prefix.clone(), - &FragmentContext::new(new_visible_text.clone()) & (), - ); + new_fragments.push(prefix.clone(), &()); new_split_tree.push( InsertionSplit { extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start, fragment_id: prefix.id, }, &(), - &(), ); fragment_start = range.start; } @@ -1614,6 +1543,7 @@ impl Buffer { local_timestamp, lamport_timestamp, ); + new_visible_text.insert(new_fragments.summary().text.visible, &new_text); new_fragments.push(new_fragment, &()); } } @@ -1629,6 +1559,15 @@ impl Buffer { if fragment.visible { prefix.deletions.insert(local_timestamp); prefix.visible = false; + + // TODO: avoid calling to_string on rope slice. + let deleted_start = new_fragments.summary().text.visible; + let deleted_range = deleted_start..deleted_start + prefix.len(); + new_deleted_text.insert( + new_fragments.summary().text.deleted, + &new_visible_text.slice(deleted_range).to_string(), + ); + new_visible_text.remove(deleted_range); } fragment.range_in_insertion.start = prefix.range_in_insertion.end; new_fragments.push(prefix.clone(), &()); @@ -1649,6 +1588,15 @@ impl Buffer { if fragment.visible { fragment.deletions.insert(local_timestamp); fragment.visible = false; + + // TODO: avoid calling to_string on rope slice. + let deleted_start = new_fragments.summary().text.visible; + let deleted_range = deleted_start..deleted_start + fragment.len(); + new_deleted_text.insert( + new_fragments.summary().text.deleted, + &new_visible_text.slice(deleted_range).to_string(), + ); + new_visible_text.remove(deleted_range); } } @@ -1712,6 +1660,15 @@ impl Buffer { if new_fragment.visible { new_fragment.deletions.insert(local_timestamp); new_fragment.visible = false; + + // TODO: avoid calling to_string on rope slice. + let deleted_start = new_fragments.summary().text.visible; + let deleted_range = deleted_start..deleted_start + new_fragment.len(); + new_deleted_text.insert( + new_fragments.summary().text.deleted, + &new_visible_text.slice(deleted_range).to_string(), + ); + new_visible_text.remove(deleted_range); } new_fragments.push(new_fragment, &()); cursor.next(); @@ -1775,6 +1732,7 @@ impl Buffer { end_id: last_fragment.insertion.id, end_offset: last_fragment.range_in_insertion.end, version_in_range: time::Global::new(), + // TODO: avoid cloning the String. new_text: new_text.clone(), }, lamport_timestamp, @@ -1784,10 +1742,11 @@ impl Buffer { let new_fragment = self.build_fragment_to_insert( &last_fragment, None, - new_text, + &new_text, local_timestamp, lamport_timestamp, ); + new_visible_text.insert(new_fragments.summary().text.visible, &new_text); new_fragments.push(new_fragment, &()); } } else { @@ -1797,6 +1756,8 @@ impl Buffer { ); } + self.visible_text = new_visible_text; + self.deleted_text = new_deleted_text; self.fragments = new_fragments; ops } @@ -1864,7 +1825,6 @@ impl Buffer { fragment_id: fragment.id.clone(), }, &(), - &(), ); } @@ -1875,7 +1835,6 @@ impl Buffer { fragment_id: fragment.id.clone(), }, &(), - &(), ); } @@ -1886,7 +1845,6 @@ impl Buffer { fragment_id: fragment.id.clone(), }, &(), - &(), ); } @@ -1927,7 +1885,6 @@ impl Buffer { fragment_id: new_fragment_id.clone(), }, &(), - &(), ); self.insertion_splits.insert(insertion_id, split_tree); @@ -2021,7 +1978,7 @@ impl Buffer { fn summary_for_anchor(&self, anchor: &Anchor) -> Result { match anchor { Anchor::Start => Ok(TextSummary::default()), - Anchor::End => Ok(self.fragments.summary().text_summary), + Anchor::End => Ok(self.text_summary()), Anchor::Middle { insertion_id, offset, @@ -2042,37 +1999,29 @@ impl Buffer { .item() .ok_or_else(|| anyhow!("split offset is out of range"))?; - let mut fragments_cursor = self.fragments.cursor::(); + let mut fragments_cursor = self + .fragments + .cursor::(); fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &()); let fragment = fragments_cursor .item() .ok_or_else(|| anyhow!("fragment id does not exist"))?; - let mut summary = fragments_cursor.start().clone(); + let mut ix = fragments_cursor.start().clone().visible; if fragment.visible { - summary += fragment - .text - .slice(..offset - fragment.range_in_insertion.start) - .summary(); + ix += offset - fragment.range_in_insertion.start; } - Ok(summary) + Ok(self.text_summary_for_range(0..ix)) } } } - #[allow(dead_code)] pub fn point_for_offset(&self, offset: usize) -> Result { - let mut fragments_cursor = self.fragments.cursor::(); - fragments_cursor.seek(&offset, SeekBias::Left, &()); - fragments_cursor - .item() - .ok_or_else(|| anyhow!("offset is out of range")) - .map(|fragment| { - let overshoot = fragment - .point_for_offset(offset - &fragments_cursor.start().chars) - .unwrap(); - fragments_cursor.start().lines + &overshoot - }) + if offset <= self.len() { + Ok(self.text_summary_for_range(0..offset).lines) + } else { + Err(anyhow!("offset out of bounds")) + } } } @@ -2080,6 +2029,7 @@ impl Clone for Buffer { fn clone(&self) -> Self { Self { visible_text: self.visible_text.clone(), + deleted_text: self.deleted_text.clone(), fragments: self.fragments.clone(), insertion_splits: self.insertion_splits.clone(), version: self.version.clone(), @@ -2100,16 +2050,6 @@ impl Clone for Buffer { } } -impl Snapshot { - pub fn fragments<'a>(&'a self) -> FragmentIter<'a> { - FragmentIter::new(&self.fragments) - } - - pub fn text_summary(&self) -> TextSummary { - self.fragments.summary().text_summary - } -} - #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { Edited, @@ -2123,81 +2063,6 @@ impl Entity for Buffer { type Event = Event; } -impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point { - fn add_summary(&mut self, summary: &FragmentSummary) { - *self += &summary.text_summary.lines; - } -} - -impl<'a> CharIter<'a> { - fn new(fragments: &'a SumTree, offset: usize) -> Self { - let mut fragments_cursor = fragments.cursor::(); - fragments_cursor.seek(&offset, SeekBias::Right, &()); - let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| { - let offset_in_fragment = offset - fragments_cursor.start(); - fragment.text[offset_in_fragment..].chars() - }); - Self { - fragments_cursor, - fragment_chars, - } - } -} - -impl<'a> Iterator for CharIter<'a> { - type Item = char; - - fn next(&mut self) -> Option { - if let Some(char) = self.fragment_chars.next() { - Some(char) - } else { - loop { - self.fragments_cursor.next(); - if let Some(fragment) = self.fragments_cursor.item() { - if fragment.visible { - self.fragment_chars = fragment.text.as_str().chars(); - return self.fragment_chars.next(); - } - } else { - return None; - } - } - } - } -} - -impl<'a> FragmentIter<'a> { - fn new(fragments: &'a SumTree) -> Self { - let mut cursor = fragments.cursor::(); - cursor.seek(&0, SeekBias::Right, &()); - Self { - cursor, - started: false, - } - } -} - -impl<'a> Iterator for FragmentIter<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option { - loop { - if self.started { - self.cursor.next(); - } else { - self.started = true; - } - if let Some(fragment) = self.cursor.item() { - if fragment.visible { - return Some(fragment.text.as_str()); - } - } else { - return None; - } - } - } -} - impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> { type Item = Edit; @@ -2411,28 +2276,10 @@ impl Fragment { } } -#[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, ctx: &FragmentContext) -> Self::Summary { + fn summary(&self) -> Self::Summary { let mut max_version = time::Global::new(); max_version.observe(self.insertion.id); for deletion in &self.deletions { @@ -2441,21 +2288,19 @@ impl sum_tree::Item for Fragment { max_version.observe_all(&self.max_undos); if self.visible { - let start = ctx.start.visible.chars; FragmentSummary { text: FragmentTextSummary { - visible: TextSummary::from(ctx.visible_text.slice(start..start + self.len())), - deleted: TextSummary::default(), + visible: self.len(), + deleted: 0, }, max_fragment_id: self.id.clone(), max_version, } } else { - let start = ctx.start.deleted.chars; FragmentSummary { text: FragmentTextSummary { - visible: TextSummary::default(), - deleted: TextSummary::from(ctx.deleted_text.slice(start..start + self.len())), + visible: 0, + deleted: self.len(), }, max_fragment_id: self.id.clone(), max_version, @@ -2486,46 +2331,16 @@ impl Default for FragmentSummary { } } -impl<'a> sum_tree::Dimension<'a, FragmentSummary> for TextSummary { - fn add_summary(&mut self, summary: &FragmentSummary) { - *self += &summary.text_summary; - } -} - -impl<'a> AddAssign<&'a FragmentExtent> for FragmentExtent { - fn add_assign(&mut self, other: &Self) { - self.chars += other.chars; - self.lines += &other.lines; - } -} - -impl Default for FragmentExtent { - fn default() -> Self { - FragmentExtent { - lines: Point::zero(), - chars: 0, - } - } -} - -impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentExtent { - fn add_summary(&mut self, summary: &FragmentSummary) { - self.chars += summary.text_summary.chars; - self.lines += &summary.text_summary.lines; - } -} - impl<'a> sum_tree::Dimension<'a, FragmentSummary> for usize { fn add_summary(&mut self, summary: &FragmentSummary) { - *self += summary.text_summary.chars; + *self += summary.text.visible; } } impl sum_tree::Item for InsertionSplit { - type Context = (); type Summary = InsertionSplitSummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { InsertionSplitSummary { extent: self.extent, } @@ -2591,17 +2406,12 @@ pub trait ToOffset { impl ToOffset for Point { fn to_offset(&self, buffer: &Buffer) -> Result { - let mut fragments_cursor = buffer.fragments.cursor::(); - fragments_cursor.seek(self, SeekBias::Left, &()); - fragments_cursor - .item() - .ok_or_else(|| anyhow!("point is out of range")) - .map(|fragment| { - let overshoot = fragment - .offset_for_point(*self - fragments_cursor.start().lines) - .unwrap(); - fragments_cursor.start().chars + overshoot - }) + if *self <= buffer.max_point() { + // TODO: return an error if line is shorter than column. + Ok(buffer.visible_text.line_to_char(self.row as usize) + self.column as usize) + } else { + Err(anyhow!("point is out of bounds")) + } } } @@ -2635,17 +2445,13 @@ impl ToPoint for Anchor { impl ToPoint for usize { fn to_point(&self, buffer: &Buffer) -> Result { - let mut fragments_cursor = buffer.fragments.cursor::(); - fragments_cursor.seek(&self, SeekBias::Left, &()); - fragments_cursor - .item() - .ok_or_else(|| anyhow!("offset is out of range")) - .map(|fragment| { - let overshoot = fragment - .point_for_offset(*self - &fragments_cursor.start().chars) - .unwrap(); - fragments_cursor.start().lines + overshoot - }) + if *self <= buffer.len() { + let row = buffer.visible_text.char_to_line(*self); + let column = *self - buffer.visible_text.line_to_char(row); + Ok(Point::new(row as u32, column as u32)) + } else { + Err(anyhow!("offset is out of bounds")) + } } } @@ -2798,8 +2604,9 @@ mod tests { let (longest_column, longest_rows) = line_lengths.iter().next_back().unwrap(); let range_sum = buffer.text_summary_for_range(start..end); - assert_eq!(range_sum.rightmost_point.column, *longest_column); - assert!(longest_rows.contains(&range_sum.rightmost_point.row)); + // TODO: re-enable when we have rightmost point again. + // assert_eq!(range_sum.rightmost_point.column, *longest_column); + // assert!(longest_rows.contains(&range_sum.rightmost_point.row)); let range_text = &buffer.text()[start..end]; assert_eq!(range_sum.chars, range_text.chars().count()); assert_eq!(range_sum.bytes, range_text.len()); @@ -2878,26 +2685,45 @@ mod tests { fn test_text_summary_for_range(ctx: &mut gpui::MutableAppContext) { ctx.add_model(|ctx| { let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx); - let text = Text::from(buffer.text()); assert_eq!( buffer.text_summary_for_range(1..3), - text.slice(1..3).summary() + TextSummary { + chars: 2, + bytes: 2, + lines: Point::new(1, 0) + } ); assert_eq!( buffer.text_summary_for_range(1..12), - text.slice(1..12).summary() + TextSummary { + chars: 2, + bytes: 2, + lines: Point::new(1, 0) + } ); assert_eq!( buffer.text_summary_for_range(0..20), - text.slice(0..20).summary() + TextSummary { + chars: 2, + bytes: 2, + lines: Point::new(1, 0) + } ); assert_eq!( buffer.text_summary_for_range(0..22), - text.slice(0..22).summary() + TextSummary { + chars: 2, + bytes: 2, + lines: Point::new(1, 0) + } ); assert_eq!( buffer.text_summary_for_range(7..22), - text.slice(7..22).summary() + TextSummary { + chars: 2, + bytes: 2, + lines: Point::new(1, 0) + } ); buffer }); diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs deleted file mode 100644 index 715c6774628f075accb988a4899d94702087ecde..0000000000000000000000000000000000000000 --- a/zed/src/editor/buffer/text.rs +++ /dev/null @@ -1,54 +0,0 @@ -use ropey::{Rope, RopeSlice}; - -use super::Point; - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct TextSummary { - pub chars: usize, - pub bytes: usize, - pub lines: Point, -} - -impl<'a> From> 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), - } - } -} - -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; - // } - - self.chars += other.chars; - self.bytes += other.bytes; - self.lines += &other.lines; - } -} - -impl std::ops::AddAssign for TextSummary { - fn add_assign(&mut self, other: Self) { - *self += &other; - } -} diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index d0cbe60c5817d9c31da3fab2841ba94eeb8fea29..b2e36e0867b248afcd1759e94d726e51240b8dd4 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -1,6 +1,6 @@ use super::{ - buffer::{self, AnchorRangeExt}, - Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset, + buffer::{self, AnchorRangeExt, TextSummary}, + Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset, }; use crate::{ sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree}, @@ -38,7 +38,6 @@ impl FoldMap { display_text: None, }, &(), - &(), )), last_sync: Mutex::new(buffer.version()), } @@ -124,7 +123,7 @@ impl FoldMap { let mut cursor = self.folds.cursor::<_, ()>(); for fold in folds { new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer); - new_tree.push(fold, &(), buffer); + new_tree.push(fold, buffer); } new_tree.push_tree(cursor.suffix(buffer), buffer); new_tree @@ -343,7 +342,6 @@ impl FoldMap { display_text: None, }, &(), - &(), ); } @@ -363,7 +361,6 @@ impl FoldMap { display_text: Some('…'), }, &(), - &(), ); } } @@ -381,7 +378,6 @@ impl FoldMap { display_text: None, }, &(), - &(), ); } } @@ -398,7 +394,6 @@ impl FoldMap { display_text: None, }, &(), - &(), ); } @@ -471,10 +466,9 @@ struct TransformSummary { } impl sum_tree::Item for Transform { - type Context = (); type Summary = TransformSummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { self.summary.clone() } } @@ -504,10 +498,9 @@ impl Default for Fold { } impl sum_tree::Item for Fold { - type Context = (); type Summary = FoldSummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { FoldSummary { start: self.0.start.clone(), end: self.0.end.clone(), @@ -615,7 +608,7 @@ pub struct Chars<'a> { cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>, offset: usize, buffer: &'a Buffer, - buffer_chars: Option>>, + buffer_chars: Option>>, } impl<'a> Iterator for Chars<'a> { diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 608d39dac85161a28b4d9d4756cdea87d5c3d114..7ee540bd2e094522f9f2fd417ed7a2b68f5a9a4f 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -1,6 +1,6 @@ mod fold_map; -use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint}; +use super::{buffer, Anchor, Buffer, Edit, Point, ToOffset, ToPoint}; use anyhow::Result; pub use fold_map::BufferRows; use fold_map::{FoldMap, FoldMapSnapshot}; diff --git a/zed/src/operation_queue.rs b/zed/src/operation_queue.rs index 5fd24fbe976dcf8a83fcf8d42394a90743cf1125..2c0e234fe98cd30c6d3fa72b210a93c0441b8249 100644 --- a/zed/src/operation_queue.rs +++ b/zed/src/operation_queue.rs @@ -48,10 +48,9 @@ impl OperationQueue { } impl Item for T { - type Context = (); type Summary = OperationSummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { OperationSummary { key: OperationKey(self.timestamp()), len: 1, diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index 2cbd928aba5f0714f553051c4a358afa3b067575..e70440f16f0c0bfeb811c8ad1aa6167adc85b253 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -11,13 +11,12 @@ const TREE_BASE: usize = 2; const TREE_BASE: usize = 6; pub trait Item: Clone + fmt::Debug { - type Context; type Summary: Summary; - fn summary(&self, ctx: &Self::Context) -> Self::Summary; + fn summary(&self) -> Self::Summary; } -pub trait KeyedItem: Item { +pub trait KeyedItem: Item { type Key: for<'a> Dimension<'a, Self::Summary> + Ord; fn key(&self) -> Self::Key; @@ -65,13 +64,9 @@ impl SumTree { })) } - pub fn from_item( - item: T, - item_ctx: &T::Context, - summary_ctx: &::Context, - ) -> Self { + pub fn from_item(item: T, ctx: &::Context) -> Self { let mut tree = Self::new(); - tree.push(item, item_ctx, summary_ctx); + tree.push(item, ctx); tree } @@ -130,13 +125,47 @@ impl SumTree { } } - pub fn push( - &mut self, - item: T, - item_ctx: &T::Context, - summary_ctx: &::Context, - ) { - let summary = item.summary(item_ctx); + pub fn extend(&mut self, iter: I, ctx: &::Context) + where + I: IntoIterator, + { + let mut leaf: Option> = None; + + for item in iter { + if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE { + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); + } + + if leaf.is_none() { + leaf = Some(Node::Leaf:: { + summary: T::Summary::default(), + items: ArrayVec::new(), + item_summaries: ArrayVec::new(), + }); + } + + if let Some(Node::Leaf { + summary, + items, + item_summaries, + }) = leaf.as_mut() + { + let item_summary = item.summary(); + summary.add_summary(&item_summary, ctx); + items.push(item); + item_summaries.push(item_summary); + } else { + unreachable!() + } + } + + if leaf.is_some() { + self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); + } + } + + pub fn push(&mut self, item: T, ctx: &::Context) { + let summary = item.summary(); self.push_tree( SumTree::from_child_trees( vec![SumTree(Arc::new(Node::Leaf { @@ -144,9 +173,9 @@ impl SumTree { items: ArrayVec::from_iter(Some(item)), item_summaries: ArrayVec::from_iter(Some(summary)), }))], - summary_ctx, + ctx, ), - summary_ctx, + ctx, ) } @@ -320,54 +349,13 @@ impl SumTree { } } -impl> SumTree { - pub fn extend(&mut self, iter: I, ctx: &::Context) - where - I: IntoIterator, - { - let mut leaf: Option> = None; - - for item in iter { - if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); - } - - if leaf.is_none() { - leaf = Some(Node::Leaf:: { - summary: T::Summary::default(), - items: ArrayVec::new(), - item_summaries: ArrayVec::new(), - }); - } - - if let Some(Node::Leaf { - summary, - items, - item_summaries, - }) = leaf.as_mut() - { - let item_summary = item.summary(&()); - summary.add_summary(&item_summary, ctx); - items.push(item); - item_summaries.push(item_summary); - } else { - unreachable!() - } - } - - if leaf.is_some() { - self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx); - } - } -} - impl SumTree { #[allow(unused)] pub fn insert(&mut self, item: T, ctx: &::Context) { *self = { let mut cursor = self.cursor::(); let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx); - new_tree.push(item, &(), ctx); + new_tree.push(item, ctx); new_tree.push_tree(cursor.suffix(ctx), ctx); new_tree }; @@ -875,10 +863,9 @@ mod tests { struct Sum(usize); impl Item for u8 { - type Context = (); type Summary = IntegersSummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { IntegersSummary { count: Count(1), sum: Sum(*self as usize), diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 7d7356f2d2fc68219fc85757ec8a605172b5c025..29df096a4f2278764351a5e7f8910b2e4585196d 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -3,7 +3,7 @@ mod fuzzy; mod ignore; use crate::{ - editor::{History, Snapshot as BufferSnapshot}, + editor::History, sum_tree::{self, Cursor, Edit, SeekBias, SumTree}, }; use ::ignore::gitignore::Gitignore; @@ -16,6 +16,7 @@ use postage::{ prelude::{Sink, Stream}, watch, }; +use ropey::Rope; use smol::channel::Sender; use std::{ cmp, @@ -198,20 +199,15 @@ impl Worktree { }) } - pub fn save<'a>( - &self, - path: &Path, - content: BufferSnapshot, - ctx: &AppContext, - ) -> Task> { + pub fn save<'a>(&self, path: &Path, content: Rope, ctx: &AppContext) -> Task> { let handles = self.handles.clone(); let path = path.to_path_buf(); let abs_path = self.absolutize(&path); ctx.background_executor().spawn(async move { - let buffer_size = content.text_summary().bytes.min(10 * 1024); + let buffer_size = content.len_bytes().min(10 * 1024); let file = fs::File::create(&abs_path)?; let mut writer = io::BufWriter::with_capacity(buffer_size, &file); - for chunk in content.fragments() { + for chunk in content.chunks() { writer.write(chunk.as_bytes())?; } writer.flush()?; @@ -459,7 +455,7 @@ impl FileHandle { self.worktree.read(ctx).load_history(&self.path(), ctx) } - pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>(&self, content: Rope, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); worktree.save(&self.path(), content, ctx) } @@ -538,10 +534,9 @@ impl Entry { } impl sum_tree::Item for Entry { - type Context = (); type Summary = EntrySummary; - fn summary(&self, _: &()) -> Self::Summary { + fn summary(&self) -> Self::Summary { let file_count; let visible_file_count; if self.is_file() { From 39c56b75eddd5018b2e7cebe9ebeddc6c09f210f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 May 2021 19:48:55 +0200 Subject: [PATCH 03/20] Get local buffer usage working with ropes Randomized tests for remote edits are still not passing. Co-Authored-By: Max Brunsfeld --- zed/src/editor/buffer/mod.rs | 218 ++++++++++------------------------- 1 file changed, 63 insertions(+), 155 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 735846dd8d3740949edb5b9d1229b31fdfe84e56..c99f2302679a71140d38ec0a4d0ef0715014dcb7 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -11,7 +11,7 @@ use similar::{ChangeTag, TextDiff}; use crate::{ operation_queue::{self, OperationQueue}, - sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree}, + sum_tree::{self, FilterCursor, SeekBias, SumTree}, time::{self, ReplicaId}, util::RandomCharIter, worktree::FileHandle, @@ -24,8 +24,7 @@ use std::{ cmp, hash::BuildHasher, iter::{self, Iterator}, - mem, - ops::{AddAssign, Range}, + ops::Range, str, sync::Arc, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, @@ -834,10 +833,11 @@ impl Buffer { .map(|range| Ok(range.start.to_offset(self)?..range.end.to_offset(self)?)) .collect::>>>()?; + let has_new_text = new_text.is_some(); let ops = self.splice_fragments( old_ranges .into_iter() - .filter(|old_range| new_text.is_some() || old_range.end > old_range.start), + .filter(|old_range| has_new_text || old_range.end > old_range.start), new_text.into(), ); @@ -1075,7 +1075,7 @@ impl Buffer { start_offset: usize, end_id: time::Local, end_offset: usize, - new_text: Option<&str>, + mut new_text: Option<&str>, version_in_range: &time::Global, local_timestamp: time::Local, lamport_timestamp: time::Lamport, @@ -1123,7 +1123,7 @@ impl Buffer { &fragment, split_start..split_end, ); - let insertion = if let Some(new_text) = new_text.take() { + let insertion = if let Some(new_text) = new_text { Some(self.build_fragment_to_insert( before_range.as_ref().or(cursor.prev_item()).unwrap(), within_range.as_ref().or(after_range.as_ref()), @@ -1138,8 +1138,10 @@ impl Buffer { new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - new_visible_text - .insert(new_fragments.summary().text.visible, new_text.unwrap()); + new_visible_text.insert( + new_fragments.summary().text.visible, + new_text.take().unwrap(), + ); new_fragments.push(fragment, &()); } if let Some(mut fragment) = within_range { @@ -1152,7 +1154,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1187,7 +1189,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1306,8 +1308,8 @@ impl Buffer { loop { let mut fragment = cursor.item().unwrap().clone(); - let was_visible = - mem::replace(&mut fragment.visible, fragment.is_visible(&self.undo_map)); + let was_visible = fragment.visible; + fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); // TODO: avoid calling to_string on rope slice. @@ -1316,7 +1318,7 @@ impl Buffer { let visible_range = visible_start..visible_start + fragment.len(); new_visible_text.insert( new_fragments.summary().text.visible, - &new_deleted_text.slice(visible_range).to_string(), + &new_deleted_text.slice(visible_range.clone()).to_string(), ); new_deleted_text.remove(visible_range); } else if !fragment.visible && was_visible { @@ -1324,7 +1326,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1351,10 +1353,8 @@ impl Buffer { if edit.version_in_range.observed(fragment.insertion.id) || fragment.insertion.id == undo.edit_id { - let was_visible = mem::replace( - &mut fragment.visible, - fragment.is_visible(&self.undo_map), - ); + let was_visible = fragment.visible; + fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); // TODO: avoid calling to_string on rope slice. @@ -1363,7 +1363,7 @@ impl Buffer { let visible_range = visible_start..visible_start + fragment.len(); new_visible_text.insert( new_fragments.summary().text.visible, - &new_deleted_text.slice(visible_range).to_string(), + &new_deleted_text.slice(visible_range.clone()).to_string(), ); new_deleted_text.remove(visible_range); } else if !fragment.visible && was_visible { @@ -1371,7 +1371,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1385,6 +1385,8 @@ impl Buffer { new_fragments.push_tree(cursor.suffix(&()), &()); drop(cursor); + self.visible_text = new_visible_text; + self.deleted_text = new_deleted_text; self.fragments = new_fragments; Ok(()) @@ -1565,7 +1567,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + prefix.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1594,7 +1596,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -1666,7 +1668,7 @@ impl Buffer { let deleted_range = deleted_start..deleted_start + new_fragment.len(); new_deleted_text.insert( new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range).to_string(), + &new_visible_text.slice(deleted_range.clone()).to_string(), ); new_visible_text.remove(deleted_range); } @@ -2591,9 +2593,9 @@ mod tests { let (longest_column, longest_rows) = line_lengths.iter().next_back().unwrap(); - let rightmost_point = buffer.rightmost_point(); - assert_eq!(rightmost_point.column, *longest_column); - assert!(longest_rows.contains(&rightmost_point.row)); + // let rightmost_point = buffer.rightmost_point(); + // assert_eq!(rightmost_point.column, *longest_column); + // assert!(longest_rows.contains(&rightmost_point.row)); } for _ in 0..5 { @@ -2696,33 +2698,33 @@ mod tests { assert_eq!( buffer.text_summary_for_range(1..12), TextSummary { - chars: 2, - bytes: 2, - lines: Point::new(1, 0) + chars: 11, + bytes: 11, + lines: Point::new(3, 0) } ); assert_eq!( buffer.text_summary_for_range(0..20), TextSummary { - chars: 2, - bytes: 2, - lines: Point::new(1, 0) + chars: 20, + bytes: 20, + lines: Point::new(4, 1) } ); assert_eq!( buffer.text_summary_for_range(0..22), TextSummary { - chars: 2, - bytes: 2, - lines: Point::new(1, 0) + chars: 22, + bytes: 22, + lines: Point::new(4, 3) } ); assert_eq!( buffer.text_summary_for_range(7..22), TextSummary { - chars: 2, - bytes: 2, - lines: Point::new(1, 0) + chars: 15, + bytes: 15, + lines: Point::new(2, 3) } ); buffer @@ -2732,133 +2734,39 @@ mod tests { #[gpui::test] fn test_chars_at(ctx: &mut gpui::MutableAppContext) { ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "", ctx); - buffer.edit(vec![0..0], "abcd\nefgh\nij", None).unwrap(); - buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); - buffer.edit(vec![18..18], "\npqrs", None).unwrap(); - buffer.edit(vec![18..21], "\nPQ", None).unwrap(); + let mut buffer = Buffer::new(0, "", ctx); + buffer.edit(vec![0..0], "abcd\nefgh\nij", None).unwrap(); + buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); + buffer.edit(vec![18..18], "\npqrs", None).unwrap(); + buffer.edit(vec![18..21], "\nPQ", None).unwrap(); - let chars = buffer.chars_at(Point::new(0, 0)).unwrap(); - assert_eq!(chars.collect::(), "abcd\nefgh\nijkl\nmno\nPQrs"); + let chars = buffer.chars_at(Point::new(0, 0)).unwrap(); + assert_eq!(chars.collect::(), "abcd\nefgh\nijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(1, 0)).unwrap(); - assert_eq!(chars.collect::(), "efgh\nijkl\nmno\nPQrs"); + let chars = buffer.chars_at(Point::new(1, 0)).unwrap(); + assert_eq!(chars.collect::(), "efgh\nijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(2, 0)).unwrap(); - assert_eq!(chars.collect::(), "ijkl\nmno\nPQrs"); + let chars = buffer.chars_at(Point::new(2, 0)).unwrap(); + assert_eq!(chars.collect::(), "ijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(3, 0)).unwrap(); - assert_eq!(chars.collect::(), "mno\nPQrs"); + let chars = buffer.chars_at(Point::new(3, 0)).unwrap(); + assert_eq!(chars.collect::(), "mno\nPQrs"); - let chars = buffer.chars_at(Point::new(4, 0)).unwrap(); - assert_eq!(chars.collect::(), "PQrs"); + let chars = buffer.chars_at(Point::new(4, 0)).unwrap(); + assert_eq!(chars.collect::(), "PQrs"); - // Regression test: - let mut buffer = Buffer::new(0, "", ctx); - buffer.edit(vec![0..0], "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n", None).unwrap(); - buffer.edit(vec![60..60], "\n", None).unwrap(); + // Regression test: + let mut buffer = Buffer::new(0, "", ctx); + buffer.edit(vec![0..0], "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n", None).unwrap(); + buffer.edit(vec![60..60], "\n", None).unwrap(); - let chars = buffer.chars_at(Point::new(6, 0)).unwrap(); - assert_eq!(chars.collect::(), " \"xray_wasm\",\n]\n"); + let chars = buffer.chars_at(Point::new(6, 0)).unwrap(); + assert_eq!(chars.collect::(), " \"xray_wasm\",\n]\n"); - buffer - }); + buffer + }); } - // #[test] - // fn test_point_for_offset() -> Result<()> { - // let text = Text::from("abc\ndefgh\nijklm\nopq"); - // assert_eq!(text.point_for_offset(0)?, Point { row: 0, column: 0 }); - // assert_eq!(text.point_for_offset(1)?, Point { row: 0, column: 1 }); - // assert_eq!(text.point_for_offset(2)?, Point { row: 0, column: 2 }); - // assert_eq!(text.point_for_offset(3)?, Point { row: 0, column: 3 }); - // assert_eq!(text.point_for_offset(4)?, Point { row: 1, column: 0 }); - // assert_eq!(text.point_for_offset(5)?, Point { row: 1, column: 1 }); - // assert_eq!(text.point_for_offset(9)?, Point { row: 1, column: 5 }); - // assert_eq!(text.point_for_offset(10)?, Point { row: 2, column: 0 }); - // assert_eq!(text.point_for_offset(14)?, Point { row: 2, column: 4 }); - // assert_eq!(text.point_for_offset(15)?, Point { row: 2, column: 5 }); - // assert_eq!(text.point_for_offset(16)?, Point { row: 3, column: 0 }); - // assert_eq!(text.point_for_offset(17)?, Point { row: 3, column: 1 }); - // assert_eq!(text.point_for_offset(19)?, Point { row: 3, column: 3 }); - // assert!(text.point_for_offset(20).is_err()); - // - // let text = Text::from("abc"); - // assert_eq!(text.point_for_offset(0)?, Point { row: 0, column: 0 }); - // assert_eq!(text.point_for_offset(1)?, Point { row: 0, column: 1 }); - // assert_eq!(text.point_for_offset(2)?, Point { row: 0, column: 2 }); - // assert_eq!(text.point_for_offset(3)?, Point { row: 0, column: 3 }); - // assert!(text.point_for_offset(4).is_err()); - // Ok(()) - // } - - // #[test] - // fn test_offset_for_point() -> Result<()> { - // let text = Text::from("abc\ndefgh"); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 0 })?, 0); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 1 })?, 1); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 2 })?, 2); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 3 })?, 3); - // assert!(text.offset_for_point(Point { row: 0, column: 4 }).is_err()); - // assert_eq!(text.offset_for_point(Point { row: 1, column: 0 })?, 4); - // assert_eq!(text.offset_for_point(Point { row: 1, column: 1 })?, 5); - // assert_eq!(text.offset_for_point(Point { row: 1, column: 5 })?, 9); - // assert!(text.offset_for_point(Point { row: 1, column: 6 }).is_err()); - // - // let text = Text::from("abc"); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 0 })?, 0); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 1 })?, 1); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 2 })?, 2); - // assert_eq!(text.offset_for_point(Point { row: 0, column: 3 })?, 3); - // assert!(text.offset_for_point(Point { row: 0, column: 4 }).is_err()); - // Ok(()) - // } - - // #[test] - // fn test_longest_row_in_range() -> Result<()> { - // for seed in 0..100 { - // println!("{:?}", seed); - // let mut rng = &mut StdRng::seed_from_u64(seed); - // let string_len = rng.gen_range(1, 10); - // let string = RandomCharIter(&mut rng) - // .take(string_len) - // .collect::(); - // let text = Text::from(string.as_ref()); - // - // for _i in 0..10 { - // let end = rng.gen_range(1, string.len() + 1); - // let start = rng.gen_range(0, end); - // - // let mut cur_row = string[0..start].chars().filter(|c| *c == '\n').count() as u32; - // let mut cur_row_len = 0; - // let mut expected_longest_row = cur_row; - // let mut expected_longest_row_len = cur_row_len; - // for ch in string[start..end].chars() { - // if ch == '\n' { - // if cur_row_len > expected_longest_row_len { - // expected_longest_row = cur_row; - // expected_longest_row_len = cur_row_len; - // } - // cur_row += 1; - // cur_row_len = 0; - // } else { - // cur_row_len += 1; - // } - // } - // if cur_row_len > expected_longest_row_len { - // expected_longest_row = cur_row; - // expected_longest_row_len = cur_row_len; - // } - // - // assert_eq!( - // text.longest_row_in_range(start..end)?, - // (expected_longest_row, expected_longest_row_len) - // ); - // } - // } - // Ok(()) - // } - #[test] fn test_fragment_ids() { for seed in 0..10 { From d8f7b35dca84644f77e06b2a613286d6b5de0588 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 May 2021 19:59:01 +0200 Subject: [PATCH 04/20] Fix concurrent editing in buffer after using ropes Co-Authored-By: Max Brunsfeld Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 43 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index c99f2302679a71140d38ec0a4d0ef0715014dcb7..31e35643de6096506d70294635aaeed671434daf 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1095,7 +1095,6 @@ impl Buffer { 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(), &()); cursor.next(); } @@ -1147,16 +1146,17 @@ impl Buffer { 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; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), - ); - new_visible_text.remove(deleted_range); + if fragment.visible { + fragment.visible = false; + // TODO: avoid calling to_string on rope slice. + let deleted_start = new_fragments.summary().text.visible; + let deleted_range = deleted_start..deleted_start + fragment.len(); + new_deleted_text.insert( + new_fragments.summary().text.deleted, + &new_visible_text.slice(deleted_range.clone()).to_string(), + ); + new_visible_text.remove(deleted_range); + } } new_fragments.push(fragment, &()); @@ -1182,16 +1182,17 @@ impl Buffer { && 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; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), - ); - new_visible_text.remove(deleted_range); + if fragment.visible { + fragment.visible = false; + // TODO: avoid calling to_string on rope slice. + let deleted_start = new_fragments.summary().text.visible; + let deleted_range = deleted_start..deleted_start + fragment.len(); + new_deleted_text.insert( + new_fragments.summary().text.deleted, + &new_visible_text.slice(deleted_range.clone()).to_string(), + ); + new_visible_text.remove(deleted_range); + } } new_fragments.push(fragment, &()); From 9aeb35bfab08c1673d91d60879d2ae4c8fa1869c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 13 May 2021 11:54:29 -0700 Subject: [PATCH 05/20] Get tests passing using a slow rightmost_point impl Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 104 +++++++++++++------------ zed/src/editor/display_map/fold_map.rs | 18 +++-- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 31e35643de6096506d70294635aaeed671434daf..23077a25a900adf02cc8901983bac6f183f83130 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -309,33 +309,56 @@ pub struct TextSummary { pub chars: usize, pub bytes: usize, pub lines: Point, + pub first_line_len: u32, + pub rightmost_point: Point, } impl<'a> From> for TextSummary { fn from(slice: RopeSlice<'a>) -> Self { let last_row = slice.len_lines() - 1; let last_column = slice.line(last_row).len_chars(); + + let mut point = Point::default(); + let mut rightmost_point = point; + let mut first_line_len = None; + for (i, c) in slice.chars().enumerate() { + if c == '\n' { + if first_line_len.is_none() { + first_line_len = Some(i as u32); + } + point.row += 1; + point.column = 0; + } else { + point.column += 1; + if point.column > rightmost_point.column { + rightmost_point = point; + } + } + } + Self { chars: slice.len_chars(), bytes: slice.len_bytes(), lines: Point::new(last_row as u32, last_column as u32), + first_line_len: first_line_len.unwrap_or(slice.len_chars() as u32), + rightmost_point, } } } 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; @@ -662,35 +685,11 @@ impl Buffer { } pub fn rightmost_point(&self) -> Point { - todo!() - // self.fragments.summary().text_summary.rightmost_point + self.rightmost_point_in_range(0..self.len()) } pub fn rightmost_point_in_range(&self, range: Range) -> Point { - todo!() - // let mut summary = TextSummary::default(); - - // let mut cursor = self.fragments.cursor::(); - // 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::(&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.rightmost_point + self.text_summary_for_range(range).rightmost_point } pub fn max_point(&self) -> Point { @@ -2594,9 +2593,9 @@ mod tests { let (longest_column, longest_rows) = line_lengths.iter().next_back().unwrap(); - // let rightmost_point = buffer.rightmost_point(); - // assert_eq!(rightmost_point.column, *longest_column); - // assert!(longest_rows.contains(&rightmost_point.row)); + let rightmost_point = buffer.rightmost_point(); + assert_eq!(rightmost_point.column, *longest_column); + assert!(longest_rows.contains(&rightmost_point.row)); } for _ in 0..5 { @@ -2607,9 +2606,8 @@ mod tests { let (longest_column, longest_rows) = line_lengths.iter().next_back().unwrap(); let range_sum = buffer.text_summary_for_range(start..end); - // TODO: re-enable when we have rightmost point again. - // assert_eq!(range_sum.rightmost_point.column, *longest_column); - // assert!(longest_rows.contains(&range_sum.rightmost_point.row)); + assert_eq!(range_sum.rightmost_point.column, *longest_column); + assert!(longest_rows.contains(&range_sum.rightmost_point.row)); let range_text = &buffer.text()[start..end]; assert_eq!(range_sum.chars, range_text.chars().count()); assert_eq!(range_sum.bytes, range_text.len()); @@ -2693,7 +2691,9 @@ mod tests { TextSummary { chars: 2, bytes: 2, - lines: Point::new(1, 0) + lines: Point::new(1, 0), + first_line_len: 1, + rightmost_point: Point::new(0, 1), } ); assert_eq!( @@ -2701,7 +2701,9 @@ mod tests { TextSummary { chars: 11, bytes: 11, - lines: Point::new(3, 0) + lines: Point::new(3, 0), + first_line_len: 1, + rightmost_point: Point::new(2, 4), } ); assert_eq!( @@ -2709,7 +2711,9 @@ mod tests { TextSummary { chars: 20, bytes: 20, - lines: Point::new(4, 1) + lines: Point::new(4, 1), + first_line_len: 2, + rightmost_point: Point::new(3, 6), } ); assert_eq!( @@ -2717,7 +2721,9 @@ mod tests { TextSummary { chars: 22, bytes: 22, - lines: Point::new(4, 3) + lines: Point::new(4, 3), + first_line_len: 2, + rightmost_point: Point::new(3, 6), } ); assert_eq!( @@ -2725,7 +2731,9 @@ mod tests { TextSummary { chars: 15, bytes: 15, - lines: Point::new(2, 3) + lines: Point::new(2, 3), + first_line_len: 4, + rightmost_point: Point::new(1, 6), } ); buffer diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index b2e36e0867b248afcd1759e94d726e51240b8dd4..daf182c2e79ffbb3ab219e8a42c6892d033c5083 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -1,5 +1,5 @@ use super::{ - buffer::{self, AnchorRangeExt, TextSummary}, + buffer::{AnchorRangeExt, TextSummary}, Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset, }; use crate::{ @@ -72,8 +72,7 @@ impl FoldMap { } pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint { - todo!() - // DisplayPoint(self.sync(ctx).summary().display.rightmost_point) + DisplayPoint(self.sync(ctx).summary().display.rightmost_point) } pub fn folds_in_range<'a, T>( @@ -353,8 +352,8 @@ impl FoldMap { chars: 1, bytes: '…'.len_utf8(), lines: Point::new(0, 1), - // first_line_len: 1, - // rightmost_point: Point::new(0, 1), + first_line_len: 1, + rightmost_point: Point::new(0, 1), }, buffer: buffer.text_summary_for_range(fold.start..fold.end), }, @@ -670,8 +669,8 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize { #[cfg(test)] mod tests { use super::*; + use crate::editor::buffer::ToPoint; use crate::test::sample_text; - use buffer::ToPoint; #[gpui::test] fn test_basic_folds(app: &mut gpui::MutableAppContext) { @@ -917,6 +916,7 @@ mod tests { assert_eq!(line_len, line.chars().count() as u32); } + let rightmost_point = map.rightmost_point(app.as_ref()); let mut display_point = DisplayPoint::new(0, 0); let mut display_offset = DisplayOffset(0); for c in expected_text.chars() { @@ -942,6 +942,12 @@ mod tests { *display_point.column_mut() += 1; } display_offset.0 += 1; + if display_point.column() > rightmost_point.column() { + panic!( + "invalid rightmost point {:?}, found point {:?}", + rightmost_point, display_point + ); + } } for _ in 0..5 { From e48973f75a57718015698f4707bacbe2bcc753b5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 11:33:39 +0200 Subject: [PATCH 06/20] Start on a SumTree-based Rope implementation --- zed/src/editor/buffer/mod.rs | 1 + zed/src/editor/buffer/rope.rs | 295 ++++++++++++++++++++++++++++++++++ zed/src/sum_tree/mod.rs | 44 +++++ 3 files changed, 340 insertions(+) create mode 100644 zed/src/editor/buffer/rope.rs diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 23077a25a900adf02cc8901983bac6f183f83130..5cb2e1920d88712380eb6b7f4c5cfc97c0d73b64 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1,5 +1,6 @@ mod anchor; mod point; +mod rope; mod selection; pub use anchor::*; diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs new file mode 100644 index 0000000000000000000000000000000000000000..bb441e96d34797d1af5097fce675977453bce457 --- /dev/null +++ b/zed/src/editor/buffer/rope.rs @@ -0,0 +1,295 @@ +use super::Point; +use crate::sum_tree::{self, SeekBias, SumTree}; +use arrayvec::ArrayString; +use std::{cmp, ops::Range, str}; + +#[cfg(test)] +const CHUNK_BASE: usize = 2; + +#[cfg(not(test))] +const CHUNK_BASE: usize = 8; + +#[derive(Clone, Default, Debug)] +pub struct Rope { + chunks: SumTree, +} + +impl Rope { + pub fn new() -> Self { + Self::default() + } + + pub fn append(&mut self, rope: Rope) { + self.chunks.push_tree(rope.chunks, &()); + } + + pub fn push(&mut self, mut text: &str) { + let mut suffix = ArrayString::<[_; 2 * CHUNK_BASE]>::new(); + self.chunks.with_last_mut( + |chunk| { + if chunk.0.len() + text.len() <= 2 * CHUNK_BASE { + chunk.0.push_str(text); + text = ""; + } else { + let mut append_len = CHUNK_BASE.saturating_sub(chunk.0.len()); + while !text.is_char_boundary(append_len) { + append_len -= 1; + } + + if append_len > 0 { + let split = text.split_at(append_len); + chunk.0.push_str(split.0); + text = split.1; + } else { + let mut take_len = CHUNK_BASE; + while !chunk.0.is_char_boundary(take_len) { + take_len -= 1; + } + + let split = chunk.0.split_at(take_len); + suffix.push_str(split.1); + chunk.0 = ArrayString::from(split.0).unwrap(); + } + } + }, + &(), + ); + + let mut chunks = vec![]; + let mut chunk = ArrayString::new(); + for ch in suffix.chars().chain(text.chars()) { + if chunk.len() + ch.len_utf8() > CHUNK_BASE { + chunks.push(Chunk(chunk)); + chunk = ArrayString::new(); + } + chunk.push(ch); + } + if !chunk.is_empty() { + chunks.push(Chunk(chunk)); + } + self.chunks.extend(chunks, &()); + } + + pub fn slice(&self, range: Range) -> Rope { + let mut slice = Rope::new(); + let mut cursor = self.chunks.cursor::(); + + cursor.slice(&range.start, SeekBias::Left, &()); + if let Some(start_chunk) = cursor.item() { + let start_ix = range.start - cursor.start(); + let end_ix = cmp::min(range.end, cursor.end()) - cursor.start(); + slice.push(&start_chunk.0[start_ix..end_ix]); + } + + if range.end > cursor.end() { + cursor.next(); + slice.append(Rope { + chunks: cursor.slice(&range.end, SeekBias::Left, &()), + }); + if let Some(end_chunk) = cursor.item() { + slice.push(&end_chunk.0[..range.end - cursor.start()]); + } + } + + slice + } + + pub fn summary(&self) -> TextSummary { + self.chunks.summary() + } + + pub fn chars(&self) -> Chars { + self.chars_at(0) + } + + pub fn chars_at(&self, start: usize) -> Chars { + Chars::new(self, start) + } + + fn text(&self) -> String { + let mut text = String::new(); + for chunk in self.chunks.cursor::<(), ()>() { + text.push_str(&chunk.0); + } + text + } +} + +impl<'a> From<&'a str> for Rope { + fn from(text: &'a str) -> Self { + let mut rope = Self::new(); + rope.push(text); + rope + } +} + +#[derive(Clone, Debug, Default)] +struct Chunk(ArrayString<[u8; 2 * CHUNK_BASE]>); + +impl sum_tree::Item for Chunk { + type Summary = TextSummary; + + fn summary(&self) -> Self::Summary { + let mut chars = 0; + let mut bytes = 0; + let mut lines = Point::new(0, 0); + let mut first_line_len = 0; + let mut rightmost_point = Point::new(0, 0); + for c in self.0.chars() { + chars += 1; + bytes += c.len_utf8(); + if c == '\n' { + lines.row += 1; + lines.column = 0; + } else { + lines.column += 1; + if lines.row == 0 { + first_line_len = lines.column; + } + if lines.column > rightmost_point.column { + rightmost_point = lines; + } + } + } + + TextSummary { + chars, + bytes, + lines, + first_line_len, + rightmost_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 = (); + + fn add_summary(&mut self, summary: &Self, _: &Self::Context) { + *self += summary; + } +} + +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; + } + + self.chars += other.chars; + self.bytes += other.bytes; + self.lines += &other.lines; + } +} + +impl std::ops::AddAssign for TextSummary { + fn add_assign(&mut self, other: Self) { + *self += &other; + } +} + +impl<'a> sum_tree::Dimension<'a, TextSummary> for usize { + fn add_summary(&mut self, summary: &'a TextSummary) { + *self += summary.chars; + } +} + +pub struct Chars<'a> { + cursor: sum_tree::Cursor<'a, Chunk, usize, usize>, + chars: str::Chars<'a>, +} + +impl<'a> Chars<'a> { + pub fn new(rope: &'a Rope, start: usize) -> Self { + let mut cursor = rope.chunks.cursor::(); + cursor.slice(&start, SeekBias::Left, &()); + let chunk = cursor.item().expect("invalid index"); + let chars = chunk.0[start - cursor.start()..].chars(); + Self { cursor, chars } + } +} + +impl<'a> Iterator for Chars<'a> { + type Item = char; + + fn next(&mut self) -> Option { + if let Some(ch) = self.chars.next() { + Some(ch) + } else if let Some(chunk) = self.cursor.item() { + self.chars = chunk.0.chars(); + self.cursor.next(); + Some(self.chars.next().unwrap()) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::util::RandomCharIter; + + use super::*; + use rand::prelude::*; + use std::env; + + #[test] + fn test_random() { + let iterations = env::var("ITERATIONS") + .map(|i| i.parse().expect("invalid `ITERATIONS` variable")) + .unwrap_or(100); + let operations = env::var("OPERATIONS") + .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) + .unwrap_or(10); + let seed_range = if let Ok(seed) = env::var("SEED") { + let seed = seed.parse().expect("invalid `SEED` variable"); + seed..seed + 1 + } else { + 0..iterations + }; + + for seed in seed_range { + dbg!(seed); + let mut rng = StdRng::seed_from_u64(seed); + let mut expected = String::new(); + let mut actual = Rope::new(); + for _ in 0..operations { + let end_ix = rng.gen_range(0..=expected.len()); + let start_ix = rng.gen_range(0..=end_ix); + let len = rng.gen_range(0..=5); + let new_text: String = RandomCharIter::new(&mut rng).take(len).collect(); + + let mut new_actual = Rope::new(); + new_actual.append(actual.slice(0..start_ix)); + new_actual.push(&new_text); + new_actual.append(actual.slice(end_ix..actual.summary().chars)); + actual = new_actual; + + let mut new_expected = String::new(); + new_expected.push_str(&expected[..start_ix]); + new_expected.push_str(&new_text); + new_expected.push_str(&expected[end_ix..]); + expected = new_expected; + + assert_eq!(actual.text(), expected); + } + } + } +} diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index e70440f16f0c0bfeb811c8ad1aa6167adc85b253..7511da6fc61ee3042a0806382e5b7533970f1ffe 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -101,6 +101,50 @@ impl SumTree { self.rightmost_leaf().0.items().last() } + pub fn with_last_mut( + &mut self, + f: impl FnOnce(&mut T), + ctx: &::Context, + ) { + self.with_last_mut_recursive(f, ctx); + } + + fn with_last_mut_recursive( + &mut self, + f: impl FnOnce(&mut T), + ctx: &::Context, + ) -> Option { + match Arc::make_mut(&mut self.0) { + Node::Internal { + summary, + child_summaries, + child_trees, + .. + } => { + let last_summary = child_summaries.last_mut().unwrap(); + let last_child = child_trees.last_mut().unwrap(); + *last_summary = last_child.with_last_mut_recursive(f, ctx).unwrap(); + *summary = sum(child_summaries.iter(), ctx); + Some(summary.clone()) + } + Node::Leaf { + summary, + items, + item_summaries, + } => { + if let Some((item, item_summary)) = items.last_mut().zip(item_summaries.last_mut()) + { + (f)(item); + *item_summary = item.summary(); + *summary = sum(item_summaries.iter(), ctx); + Some(summary.clone()) + } else { + None + } + } + } + } + pub fn extent<'a, D: Dimension<'a, T::Summary>>(&'a self) -> D { let mut extent = D::default(); match self.0.as_ref() { From d7cde9f81b473dad63b7c2803d7740314a3e1407 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 12:26:21 +0200 Subject: [PATCH 07/20] Don't underflow Rope chunks on append --- zed/src/editor/buffer/rope.rs | 40 +++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index bb441e96d34797d1af5097fce675977453bce457..c866b37389e67d464f62b28ef9c77f23011e9c1f 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -7,7 +7,7 @@ use std::{cmp, ops::Range, str}; const CHUNK_BASE: usize = 2; #[cfg(not(test))] -const CHUNK_BASE: usize = 8; +const CHUNK_BASE: usize = 16; #[derive(Clone, Default, Debug)] pub struct Rope { @@ -20,7 +20,20 @@ impl Rope { } pub fn append(&mut self, rope: Rope) { - self.chunks.push_tree(rope.chunks, &()); + let mut chunks = rope.chunks.cursor::<(), ()>(); + chunks.next(); + + while let Some((last_chunk, first_chunk)) = self.chunks.last().zip(chunks.item()) { + if last_chunk.0.len() < CHUNK_BASE || first_chunk.0.len() < CHUNK_BASE { + self.push(&first_chunk.0); + chunks.next(); + } else { + break; + } + } + + self.chunks.push_tree(chunks.suffix(&()), &()); + self.check_invariants(); } pub fn push(&mut self, mut text: &str) { @@ -48,7 +61,7 @@ impl Rope { let split = chunk.0.split_at(take_len); suffix.push_str(split.1); - chunk.0 = ArrayString::from(split.0).unwrap(); + chunk.0.truncate(take_len); } } }, @@ -68,6 +81,25 @@ impl Rope { chunks.push(Chunk(chunk)); } self.chunks.extend(chunks, &()); + self.check_invariants(); + } + + fn check_invariants(&self) { + #[cfg(test)] + { + let mut chunks = self.chunks.cursor::<(), ()>().peekable(); + chunks.next(); + while let Some(chunk) = chunks.next() { + if chunks.peek().is_some() { + assert!( + chunk.0.len() >= CHUNK_BASE, + "Underflowing chunk: {:?}\nChunks: {:?}", + chunk, + self.chunks.items() + ); + } + } + } } pub fn slice(&self, range: Range) -> Rope { @@ -90,7 +122,7 @@ impl Rope { slice.push(&end_chunk.0[..range.end - cursor.start()]); } } - + slice.check_invariants(); slice } From ff235e61f7774583da4ec8661e15ae5b9246a5e8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 13:20:20 +0200 Subject: [PATCH 08/20] Increase leaf occupation for rope --- zed/src/editor/buffer/rope.rs | 37 +++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index c866b37389e67d464f62b28ef9c77f23011e9c1f..8f78c94abd65b3631e245e88a3b4c73264019e98 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -37,32 +37,27 @@ impl Rope { } pub fn push(&mut self, mut text: &str) { - let mut suffix = ArrayString::<[_; 2 * CHUNK_BASE]>::new(); + let mut suffix = ArrayString::<[_; CHUNK_BASE]>::new(); self.chunks.with_last_mut( |chunk| { if chunk.0.len() + text.len() <= 2 * CHUNK_BASE { chunk.0.push_str(text); text = ""; - } else { - let mut append_len = CHUNK_BASE.saturating_sub(chunk.0.len()); - while !text.is_char_boundary(append_len) { - append_len -= 1; + } else if chunk.0.len() < CHUNK_BASE { + let mut split_ix = CHUNK_BASE - chunk.0.len(); + while !text.is_char_boundary(split_ix) { + split_ix += 1; } - - if append_len > 0 { - let split = text.split_at(append_len); - chunk.0.push_str(split.0); - text = split.1; - } else { - let mut take_len = CHUNK_BASE; - while !chunk.0.is_char_boundary(take_len) { - take_len -= 1; - } - - let split = chunk.0.split_at(take_len); - suffix.push_str(split.1); - chunk.0.truncate(take_len); + let split = text.split_at(split_ix); + chunk.0.push_str(split.0); + text = split.1; + } else { + let mut split_ix = CHUNK_BASE; + while !chunk.0.is_char_boundary(split_ix) { + split_ix += 1; } + suffix.push_str(&chunk.0[split_ix..]); + chunk.0.truncate(split_ix); } }, &(), @@ -71,7 +66,7 @@ impl Rope { let mut chunks = vec![]; let mut chunk = ArrayString::new(); for ch in suffix.chars().chain(text.chars()) { - if chunk.len() + ch.len_utf8() > CHUNK_BASE { + if chunk.len() + ch.len_utf8() > 2 * CHUNK_BASE { chunks.push(Chunk(chunk)); chunk = ArrayString::new(); } @@ -305,7 +300,7 @@ mod tests { for _ in 0..operations { let end_ix = rng.gen_range(0..=expected.len()); let start_ix = rng.gen_range(0..=end_ix); - let len = rng.gen_range(0..=5); + let len = rng.gen_range(0..=20); let new_text: String = RandomCharIter::new(&mut rng).take(len).collect(); let mut new_actual = Rope::new(); From f303a1d5fe42f07c45715ba45fea208c0284c026 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 14:44:19 +0200 Subject: [PATCH 09/20] Implement `::chars`, `::to_point` and `::to_offset` for Rope --- zed/src/editor/buffer/rope.rs | 104 +++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 8f78c94abd65b3631e245e88a3b4c73264019e98..10c94ef45cdf94063a2e2506a4467c600351bf9c 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -1,5 +1,6 @@ use super::Point; use crate::sum_tree::{self, SeekBias, SumTree}; +use anyhow::{anyhow, Result}; use arrayvec::ArrayString; use std::{cmp, ops::Range, str}; @@ -140,6 +141,28 @@ impl Rope { } text } + + fn to_point(&self, offset: usize) -> Result { + if offset <= self.summary().chars { + let mut cursor = self.chunks.cursor::(); + cursor.seek(&offset, SeekBias::Left, &()); + let overshoot = offset - cursor.start().chars; + Ok(cursor.start().lines + cursor.item().unwrap().to_point(overshoot)) + } else { + Err(anyhow!("offset out of bounds")) + } + } + + fn to_offset(&self, point: Point) -> Result { + if point <= self.summary().lines { + let mut cursor = self.chunks.cursor::(); + cursor.seek(&point, SeekBias::Left, &()); + let overshoot = point - cursor.start().lines; + Ok(cursor.start().chars + cursor.item().unwrap().to_offset(overshoot)) + } else { + Err(anyhow!("offset out of bounds")) + } + } } impl<'a> From<&'a str> for Rope { @@ -153,6 +176,46 @@ impl<'a> From<&'a str> for Rope { #[derive(Clone, Debug, Default)] struct Chunk(ArrayString<[u8; 2 * CHUNK_BASE]>); +impl Chunk { + fn to_point(&self, target: usize) -> Point { + let mut offset = 0; + let mut point = Point::new(0, 0); + for ch in self.0.chars() { + if offset >= target { + break; + } + + if ch == '\n' { + point.row += 1; + point.column = 0; + } else { + point.column += 1; + } + offset += 1; + } + point + } + + fn to_offset(&self, target: Point) -> usize { + let mut offset = 0; + let mut point = Point::new(0, 0); + for ch in self.0.chars() { + if point >= target { + break; + } + + if ch == '\n' { + point.row += 1; + point.column = 0; + } else { + point.column += 1; + } + offset += 1; + } + offset + } +} + impl sum_tree::Item for Chunk { type Summary = TextSummary; @@ -232,12 +295,24 @@ impl std::ops::AddAssign for TextSummary { } } +impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary { + fn add_summary(&mut self, summary: &'a TextSummary) { + *self += summary; + } +} + impl<'a> sum_tree::Dimension<'a, TextSummary> for usize { fn add_summary(&mut self, summary: &'a TextSummary) { *self += summary.chars; } } +impl<'a> sum_tree::Dimension<'a, TextSummary> for Point { + fn add_summary(&mut self, summary: &'a TextSummary) { + *self += &summary.lines; + } +} + pub struct Chars<'a> { cursor: sum_tree::Cursor<'a, Chunk, usize, usize>, chars: str::Chars<'a>, @@ -247,8 +322,14 @@ impl<'a> Chars<'a> { pub fn new(rope: &'a Rope, start: usize) -> Self { let mut cursor = rope.chunks.cursor::(); cursor.slice(&start, SeekBias::Left, &()); - let chunk = cursor.item().expect("invalid index"); - let chars = chunk.0[start - cursor.start()..].chars(); + let chars = if let Some(chunk) = cursor.item() { + let ix = start - cursor.start(); + cursor.next(); + chunk.0[ix..].chars() + } else { + "".chars() + }; + Self { cursor, chars } } } @@ -316,6 +397,25 @@ mod tests { expected = new_expected; assert_eq!(actual.text(), expected); + + for _ in 0..5 { + let ix = rng.gen_range(0..=expected.len()); + assert_eq!(actual.chars_at(ix).collect::(), expected[ix..]); + } + + let mut point = Point::new(0, 0); + let mut offset = 0; + for ch in expected.chars() { + assert_eq!(actual.to_point(offset).unwrap(), point); + assert_eq!(actual.to_offset(point).unwrap(), offset); + if ch == '\n' { + point.row += 1; + point.column = 0 + } else { + point.column += 1; + } + offset += 1; + } } } } From 6e30fdbf5ca8f231608f129d32be7e5f315d9c70 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 15:06:22 +0200 Subject: [PATCH 10/20] Better balancing of chunks on `push` --- zed/src/editor/buffer/rope.rs | 64 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 10c94ef45cdf94063a2e2506a4467c600351bf9c..63ca0ca66bb1df26a6d0a98208f29eaf24f33619 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -2,6 +2,7 @@ use super::Point; use crate::sum_tree::{self, SeekBias, SumTree}; use anyhow::{anyhow, Result}; use arrayvec::ArrayString; +use smallvec::SmallVec; use std::{cmp, ops::Range, str}; #[cfg(test)] @@ -37,36 +38,10 @@ impl Rope { self.check_invariants(); } - pub fn push(&mut self, mut text: &str) { - let mut suffix = ArrayString::<[_; CHUNK_BASE]>::new(); - self.chunks.with_last_mut( - |chunk| { - if chunk.0.len() + text.len() <= 2 * CHUNK_BASE { - chunk.0.push_str(text); - text = ""; - } else if chunk.0.len() < CHUNK_BASE { - let mut split_ix = CHUNK_BASE - chunk.0.len(); - while !text.is_char_boundary(split_ix) { - split_ix += 1; - } - let split = text.split_at(split_ix); - chunk.0.push_str(split.0); - text = split.1; - } else { - let mut split_ix = CHUNK_BASE; - while !chunk.0.is_char_boundary(split_ix) { - split_ix += 1; - } - suffix.push_str(&chunk.0[split_ix..]); - chunk.0.truncate(split_ix); - } - }, - &(), - ); - - let mut chunks = vec![]; + pub fn push(&mut self, text: &str) { + let mut chunks = SmallVec::<[_; 16]>::new(); let mut chunk = ArrayString::new(); - for ch in suffix.chars().chain(text.chars()) { + for ch in text.chars() { if chunk.len() + ch.len_utf8() > 2 * CHUNK_BASE { chunks.push(Chunk(chunk)); chunk = ArrayString::new(); @@ -76,7 +51,36 @@ impl Rope { if !chunk.is_empty() { chunks.push(Chunk(chunk)); } - self.chunks.extend(chunks, &()); + + let mut chunks = chunks.into_iter(); + let mut first_chunk = chunks.next(); + self.chunks.with_last_mut( + |last_chunk| { + if let Some(chunk) = first_chunk.as_mut() { + if last_chunk.0.len() + chunk.0.len() <= 2 * CHUNK_BASE { + last_chunk.0.push_str(&first_chunk.take().unwrap().0); + } else { + let mut text = ArrayString::<[_; 4 * CHUNK_BASE]>::new(); + text.push_str(&last_chunk.0); + text.push_str(&chunk.0); + + let mut midpoint = text.len() / 2; + while !text.is_char_boundary(midpoint) { + midpoint += 1; + } + let (left, right) = text.split_at(midpoint); + last_chunk.0.clear(); + last_chunk.0.push_str(left); + chunk.0.clear(); + chunk.0.push_str(right); + } + } + }, + &(), + ); + + self.chunks + .extend(first_chunk.into_iter().chain(chunks), &()); self.check_invariants(); } From e2c19d3d3f5f84f748ee0ce0d50ff7538405135f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 15:19:03 +0200 Subject: [PATCH 11/20] Add `Rope::chunks` --- zed/src/editor/buffer/rope.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 63ca0ca66bb1df26a6d0a98208f29eaf24f33619..19e4178c394fb90d813bfba0c5e040b11c256cd2 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -138,6 +138,10 @@ impl Rope { Chars::new(self, start) } + pub fn chunks<'a>(&'a self) -> impl Iterator { + self.chunks.cursor::<(), ()>().map(|c| c.0.as_str()) + } + fn text(&self) -> String { let mut text = String::new(); for chunk in self.chunks.cursor::<(), ()>() { From 9f9f339a75d1a5a534108cd4a060a69201c64fd9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 16:00:45 +0200 Subject: [PATCH 12/20] :memo: --- zed/src/editor/buffer/rope.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 19e4178c394fb90d813bfba0c5e040b11c256cd2..bc1d672658c40252f5649b0f27a0958c9ce9c951 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -87,16 +87,11 @@ impl Rope { fn check_invariants(&self) { #[cfg(test)] { + // Ensure all chunks except maybe the last one are not underflowing. let mut chunks = self.chunks.cursor::<(), ()>().peekable(); - chunks.next(); while let Some(chunk) = chunks.next() { if chunks.peek().is_some() { - assert!( - chunk.0.len() >= CHUNK_BASE, - "Underflowing chunk: {:?}\nChunks: {:?}", - chunk, - self.chunks.items() - ); + assert!(chunk.0.len() >= CHUNK_BASE); } } } From 0d50c74caba639fd1ad442a0098f86224ea946e8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 16:07:46 +0200 Subject: [PATCH 13/20] :lipstick: --- zed/src/editor/buffer/rope.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index bc1d672658c40252f5649b0f27a0958c9ce9c951..4bd2297c39272930e4fb556e15c167e7e238ea53 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -24,14 +24,9 @@ impl Rope { pub fn append(&mut self, rope: Rope) { let mut chunks = rope.chunks.cursor::<(), ()>(); chunks.next(); - - while let Some((last_chunk, first_chunk)) = self.chunks.last().zip(chunks.item()) { - if last_chunk.0.len() < CHUNK_BASE || first_chunk.0.len() < CHUNK_BASE { - self.push(&first_chunk.0); - chunks.next(); - } else { - break; - } + if let Some(chunk) = chunks.item() { + self.push(&chunk.0); + chunks.next(); } self.chunks.push_tree(chunks.suffix(&()), &()); From 580fc4fed7db2b6ac3cd28c6cdea96cfbdb3606f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 16:42:46 +0200 Subject: [PATCH 14/20] Clarify variable and method names Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/rope.rs | 36 +++++++++++++++++------------------ zed/src/sum_tree/mod.rs | 12 ++++-------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 4bd2297c39272930e4fb556e15c167e7e238ea53..724beb37d767e35e9f80605006823ad743f7a6a5 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -34,30 +34,30 @@ impl Rope { } pub fn push(&mut self, text: &str) { - let mut chunks = SmallVec::<[_; 16]>::new(); - let mut chunk = ArrayString::new(); + let mut new_chunks = SmallVec::<[_; 16]>::new(); + let mut new_chunk = ArrayString::new(); for ch in text.chars() { - if chunk.len() + ch.len_utf8() > 2 * CHUNK_BASE { - chunks.push(Chunk(chunk)); - chunk = ArrayString::new(); + if new_chunk.len() + ch.len_utf8() > 2 * CHUNK_BASE { + new_chunks.push(Chunk(new_chunk)); + new_chunk = ArrayString::new(); } - chunk.push(ch); + new_chunk.push(ch); } - if !chunk.is_empty() { - chunks.push(Chunk(chunk)); + if !new_chunk.is_empty() { + new_chunks.push(Chunk(new_chunk)); } - let mut chunks = chunks.into_iter(); - let mut first_chunk = chunks.next(); - self.chunks.with_last_mut( + let mut new_chunks = new_chunks.into_iter(); + let mut first_new_chunk = new_chunks.next(); + self.chunks.update_last( |last_chunk| { - if let Some(chunk) = first_chunk.as_mut() { - if last_chunk.0.len() + chunk.0.len() <= 2 * CHUNK_BASE { - last_chunk.0.push_str(&first_chunk.take().unwrap().0); + if let Some(first_new_chunk_ref) = first_new_chunk.as_mut() { + 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(); text.push_str(&last_chunk.0); - text.push_str(&chunk.0); + text.push_str(&first_new_chunk_ref.0); let mut midpoint = text.len() / 2; while !text.is_char_boundary(midpoint) { @@ -66,8 +66,8 @@ impl Rope { let (left, right) = text.split_at(midpoint); last_chunk.0.clear(); last_chunk.0.push_str(left); - chunk.0.clear(); - chunk.0.push_str(right); + first_new_chunk_ref.0.clear(); + first_new_chunk_ref.0.push_str(right); } } }, @@ -75,7 +75,7 @@ impl Rope { ); self.chunks - .extend(first_chunk.into_iter().chain(chunks), &()); + .extend(first_new_chunk.into_iter().chain(new_chunks), &()); self.check_invariants(); } diff --git a/zed/src/sum_tree/mod.rs b/zed/src/sum_tree/mod.rs index 7511da6fc61ee3042a0806382e5b7533970f1ffe..b96b36c49d4444566688c72a470974dffb5dac62 100644 --- a/zed/src/sum_tree/mod.rs +++ b/zed/src/sum_tree/mod.rs @@ -101,15 +101,11 @@ impl SumTree { self.rightmost_leaf().0.items().last() } - pub fn with_last_mut( - &mut self, - f: impl FnOnce(&mut T), - ctx: &::Context, - ) { - self.with_last_mut_recursive(f, ctx); + pub fn update_last(&mut self, f: impl FnOnce(&mut T), ctx: &::Context) { + self.update_last_recursive(f, ctx); } - fn with_last_mut_recursive( + fn update_last_recursive( &mut self, f: impl FnOnce(&mut T), ctx: &::Context, @@ -123,7 +119,7 @@ impl SumTree { } => { let last_summary = child_summaries.last_mut().unwrap(); let last_child = child_trees.last_mut().unwrap(); - *last_summary = last_child.with_last_mut_recursive(f, ctx).unwrap(); + *last_summary = last_child.update_last_recursive(f, ctx).unwrap(); *summary = sum(child_summaries.iter(), ctx); Some(summary.clone()) } From 2f5754db638fe005ab3398db00e80a6665974274 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 17:14:57 +0200 Subject: [PATCH 15/20] Implement a `Cursor` for `Rope` Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/rope.rs | 85 +++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 724beb37d767e35e9f80605006823ad743f7a6a5..ff80fffc0e6d8cc3444ddf7f11e70af823d9790d 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -93,33 +93,17 @@ impl Rope { } pub fn slice(&self, range: Range) -> Rope { - let mut slice = Rope::new(); - let mut cursor = self.chunks.cursor::(); - - cursor.slice(&range.start, SeekBias::Left, &()); - if let Some(start_chunk) = cursor.item() { - let start_ix = range.start - cursor.start(); - let end_ix = cmp::min(range.end, cursor.end()) - cursor.start(); - slice.push(&start_chunk.0[start_ix..end_ix]); - } - - if range.end > cursor.end() { - cursor.next(); - slice.append(Rope { - chunks: cursor.slice(&range.end, SeekBias::Left, &()), - }); - if let Some(end_chunk) = cursor.item() { - slice.push(&end_chunk.0[..range.end - cursor.start()]); - } - } - slice.check_invariants(); - slice + self.cursor(range.start).slice(range.end) } pub fn summary(&self) -> TextSummary { self.chunks.summary() } + pub fn cursor(&self, offset: usize) -> Cursor { + Cursor::new(self, offset) + } + pub fn chars(&self) -> Chars { self.chars_at(0) } @@ -171,6 +155,59 @@ impl<'a> From<&'a str> for Rope { } } +pub struct Cursor<'a> { + rope: &'a Rope, + chunks: sum_tree::Cursor<'a, Chunk, usize, usize>, + offset: usize, +} + +impl<'a> Cursor<'a> { + fn new(rope: &'a Rope, offset: usize) -> Self { + let mut chunks = rope.chunks.cursor(); + chunks.seek(&offset, SeekBias::Right, &()); + Self { + rope, + chunks, + offset, + } + } + + fn seek_forward(&mut self, end_offset: usize) { + debug_assert!(end_offset >= self.offset); + + self.chunks.seek_forward(&end_offset, SeekBias::Right, &()); + self.offset = end_offset; + } + + fn slice(&mut self, end_offset: usize) -> Rope { + debug_assert!(end_offset >= self.offset); + + let mut slice = Rope::new(); + if let Some(start_chunk) = self.chunks.item() { + let start_ix = self.offset - self.chunks.start(); + let end_ix = cmp::min(end_offset, self.chunks.end()) - self.chunks.start(); + slice.push(&start_chunk.0[start_ix..end_ix]); + } + + if end_offset > self.chunks.end() { + self.chunks.next(); + slice.append(Rope { + chunks: self.chunks.slice(&end_offset, SeekBias::Right, &()), + }); + if let Some(end_chunk) = self.chunks.item() { + slice.push(&end_chunk.0[..end_offset - self.chunks.start()]); + } + } + + self.offset = end_offset; + slice + } + + fn suffix(mut self) -> Rope { + self.slice(self.rope.chunks.extent()) + } +} + #[derive(Clone, Debug, Default)] struct Chunk(ArrayString<[u8; 2 * CHUNK_BASE]>); @@ -383,9 +420,11 @@ mod tests { let new_text: String = RandomCharIter::new(&mut rng).take(len).collect(); let mut new_actual = Rope::new(); - new_actual.append(actual.slice(0..start_ix)); + let mut cursor = actual.cursor(0); + new_actual.append(cursor.slice(start_ix)); new_actual.push(&new_text); - new_actual.append(actual.slice(end_ix..actual.summary().chars)); + cursor.seek_forward(end_ix); + new_actual.append(cursor.suffix()); actual = new_actual; let mut new_expected = String::new(); From 2cdf315d956ece44c86c89abf82a2bde43f5910b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 14 May 2021 18:30:47 +0200 Subject: [PATCH 16/20] WIP: Replace ropey with our own Rope Co-Authored-By: Nathan Sobo --- Cargo.lock | 10 - zed/Cargo.toml | 1 - zed/src/editor/buffer/mod.rs | 405 ++++++++++++------------- zed/src/editor/buffer/rope.rs | 39 ++- zed/src/editor/display_map/fold_map.rs | 3 +- zed/src/worktree.rs | 5 +- 6 files changed, 220 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6e0804b04a7f9d441d0d30aebfd2fe4e9817d8c..21a08332c5e08e81c35c0d9d4db343a38983d0d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2188,15 +2188,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" -[[package]] -name = "ropey" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f3ef16589fdbb3e8fbce3dca944c08e61f39c7f16064b21a257d68ea911a83" -dependencies = [ - "smallvec", -] - [[package]] name = "roxmltree" version = "0.14.1" @@ -2987,7 +2978,6 @@ dependencies = [ "parking_lot", "postage", "rand 0.8.3", - "ropey", "rust-embed", "seahash", "serde 1.0.125", diff --git a/zed/Cargo.toml b/zed/Cargo.toml index 370f580484df803ac439fe52942fbea1e3e9257b..2302fc650954c18cb5ee21b902087436575e8006 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -31,7 +31,6 @@ num_cpus = "1.13.0" parking_lot = "0.11.1" postage = {version = "0.4.1", features = ["futures-traits"]} rand = "0.8.3" -ropey = "1.2" rust-embed = "5.9.0" seahash = "4.1" serde = {version = "1", features = ["derive"]} diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 5cb2e1920d88712380eb6b7f4c5cfc97c0d73b64..5538f6474f5ef21d0e4d134537ed33bf120dde24 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1,11 +1,11 @@ mod anchor; mod point; -mod rope; +pub mod rope; mod selection; pub use anchor::*; pub use point::*; -use ropey::{Rope, RopeSlice}; +pub use rope::{Rope, TextSummary}; use seahash::SeaHasher; pub use selection::*; use similar::{ChangeTag, TextDiff}; @@ -57,9 +57,9 @@ type HashMap = std::collections::HashMap; type HashSet = std::collections::HashSet; pub struct Buffer { + fragments: SumTree, visible_text: Rope, deleted_text: Rope, - fragments: SumTree, insertion_splits: HashMap>, pub version: time::Global, saved_version: time::Global, @@ -305,74 +305,6 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary { } } -#[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<'a> From> for TextSummary { - fn from(slice: RopeSlice<'a>) -> Self { - let last_row = slice.len_lines() - 1; - let last_column = slice.line(last_row).len_chars(); - - let mut point = Point::default(); - let mut rightmost_point = point; - let mut first_line_len = None; - for (i, c) in slice.chars().enumerate() { - if c == '\n' { - if first_line_len.is_none() { - first_line_len = Some(i as u32); - } - point.row += 1; - point.column = 0; - } else { - point.column += 1; - if point.column > rightmost_point.column { - rightmost_point = point; - } - } - } - - Self { - chars: slice.len_chars(), - bytes: slice.len_bytes(), - lines: Point::new(last_row as u32, last_column as u32), - first_line_len: first_line_len.unwrap_or(slice.len_chars() as u32), - rightmost_point, - } - } -} - -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; - } - - self.chars += other.chars; - self.bytes += other.bytes; - self.lines += &other.lines; - } -} - -impl std::ops::AddAssign for TextSummary { - fn add_assign(&mut self, other: Self) { - *self += &other; - } -} - #[derive(Eq, PartialEq, Clone, Debug)] struct InsertionSplit { extent: usize, @@ -510,10 +442,10 @@ impl Buffer { &(), ); - if base_text.len_chars() > 0 { + if base_text.len() > 0 { let base_fragment_id = FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value()); - let range_in_insertion = 0..base_text.len_chars(); + let range_in_insertion = 0..base_text.len(); visible_text = base_text.clone(); insertion_splits.get_mut(&base_insertion.id).unwrap().push( @@ -663,11 +595,12 @@ impl Buffer { } pub fn text_summary(&self) -> TextSummary { - TextSummary::from(self.visible_text.slice(..)) + self.visible_text.summary() } pub fn text_summary_for_range(&self, range: Range) -> TextSummary { - TextSummary::from(self.visible_text.slice(range)) + // TODO: Use a dedicated ::summarize method in Rope. + self.visible_text.slice(range).summary() } pub fn len(&self) -> usize { @@ -686,7 +619,7 @@ impl Buffer { } pub fn rightmost_point(&self) -> Point { - self.rightmost_point_in_range(0..self.len()) + self.visible_text.summary().rightmost_point } pub fn rightmost_point_in_range(&self, range: Range) -> Point { @@ -694,7 +627,7 @@ impl Buffer { } pub fn max_point(&self) -> Point { - self.text_summary().lines + self.visible_text.max_point() } pub fn line(&self, row: u32) -> Result { @@ -717,11 +650,11 @@ impl Buffer { Ok(self.chars_at(start)?.take(end - start)) } - pub fn chars(&self) -> ropey::iter::Chars { + pub fn chars(&self) -> rope::Chars { self.chars_at(0).unwrap() } - pub fn chars_at(&self, position: T) -> Result { + pub fn chars_at(&self, position: T) -> Result { let offset = position.to_offset(self)?; Ok(self.visible_text.chars_at(offset)) } @@ -1084,22 +1017,25 @@ impl Buffer { let end_fragment_id = self.resolve_fragment_id(end_id, end_offset)?; let old_fragments = self.fragments.clone(); - let last_id = old_fragments.extent::().0.unwrap(); - let last_id_ref = FragmentIdRef::new(&last_id); + let old_visible_text = self.visible_text.clone(); + let old_deleted_text = self.deleted_text.clone(); + + let mut fragments_cursor = old_fragments.cursor::(); + let mut visible_text_cursor = old_visible_text.cursor(0); + let mut deleted_text_cursor = old_deleted_text.cursor(0); - let mut cursor = old_fragments.cursor::(); 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(); + fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); + let mut new_visible_text = visible_text_cursor.slice(fragments_cursor.start().visible); + let mut new_deleted_text = deleted_text_cursor.slice(fragments_cursor.start().deleted); - let start_fragment = cursor.item().unwrap(); + let start_fragment = fragments_cursor.item().unwrap(); if start_offset == start_fragment.range_in_insertion.end { - new_fragments.push(cursor.item().unwrap().clone(), &()); - cursor.next(); + new_fragments.push(fragments_cursor.item().unwrap().clone(), &()); + fragments_cursor.next(); } - while let Some(fragment) = cursor.item() { + while let Some(fragment) = fragments_cursor.item() { if new_text.is_none() && fragment.id > end_fragment_id { break; } @@ -1118,13 +1054,14 @@ impl Buffer { fragment.range_in_insertion.end }; let (before_range, within_range, after_range) = self.split_fragment( - cursor.prev_item().as_ref().unwrap(), + fragments_cursor.prev_item().as_ref().unwrap(), &fragment, split_start..split_end, ); let insertion = if let Some(new_text) = new_text { + let prev_fragment = fragments_cursor.prev_item(); Some(self.build_fragment_to_insert( - before_range.as_ref().or(cursor.prev_item()).unwrap(), + before_range.as_ref().or(prev_fragment).unwrap(), within_range.as_ref().or(after_range.as_ref()), new_text, local_timestamp, @@ -1137,25 +1074,26 @@ impl Buffer { new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - new_visible_text.insert( - new_fragments.summary().text.visible, - new_text.take().unwrap(), - ); + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); new_fragments.push(fragment, &()); + new_visible_text.push(new_text.take().unwrap()); } if let Some(mut fragment) = within_range { if fragment.was_visible(&version_in_range, &self.undo_map) { fragment.deletions.insert(local_timestamp); if fragment.visible { fragment.visible = false; - // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + fragment.len()), ); - new_visible_text.remove(deleted_range); } } @@ -1168,14 +1106,16 @@ impl Buffer { 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(), + fragments_cursor.prev_item().as_ref().unwrap(), Some(&fragment), new_text, local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible, new_text); + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); new_fragments.push(fragment, &()); + new_visible_text.push(new_text); } if fragment.id < end_fragment_id @@ -1184,39 +1124,46 @@ impl Buffer { fragment.deletions.insert(local_timestamp); if fragment.visible { fragment.visible = false; - // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + fragment.len()), ); - new_visible_text.remove(deleted_range); } } new_fragments.push(fragment, &()); } - cursor.next(); + fragments_cursor.next(); } if let Some(new_text) = new_text { let fragment = self.build_fragment_to_insert( - cursor.prev_item().as_ref().unwrap(), + fragments_cursor.prev_item().as_ref().unwrap(), None, new_text, local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible, new_text); + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); new_fragments.push(fragment, &()); + new_visible_text.push(new_text); } - new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &()); + new_fragments.push_tree(fragments_cursor.suffix(&()), &()); + new_visible_text.append(visible_text_cursor.suffix()); + new_deleted_text.append(deleted_text_cursor.suffix()); + + self.fragments = new_fragments; 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); Ok(()) @@ -1291,52 +1238,60 @@ 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(); + let mut new_visible_text = Rope::new(); + let mut new_deleted_text = Rope::new(); self.undo_map.insert(undo); let edit = &self.history.ops[&undo.edit_id]; let start_fragment_id = self.resolve_fragment_id(edit.start_id, edit.start_offset)?; let end_fragment_id = self.resolve_fragment_id(edit.end_id, edit.end_offset)?; - let mut cursor = self.fragments.cursor::(); + + let mut fragments_cursor = self.fragments.cursor::(); + let mut visible_text_cursor = self.visible_text.cursor(0); + let mut deleted_text_cursor = self.deleted_text.cursor(0); if edit.start_id == edit.end_id && edit.start_offset == edit.end_offset { let splits = &self.insertion_splits[&undo.edit_id]; let mut insertion_splits = splits.cursor::<(), ()>().map(|s| &s.fragment_id).peekable(); let first_split_id = insertion_splits.next().unwrap(); - new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); + new_fragments = + fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_deleted_text + .append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); loop { - let mut fragment = cursor.item().unwrap().clone(); + let mut fragment = fragments_cursor.item().unwrap().clone(); let was_visible = fragment.visible; 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 { + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_deleted_text + .append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); + } + if fragment.visible && !was_visible { - let visible_start = new_fragments.summary().text.deleted; - let visible_range = visible_start..visible_start + fragment.len(); - new_visible_text.insert( - new_fragments.summary().text.visible, - &new_deleted_text.slice(visible_range.clone()).to_string(), + new_visible_text.append( + deleted_text_cursor + .slice(new_fragments.summary().text.deleted + fragment.len()), ); - new_deleted_text.remove(visible_range); } else if !fragment.visible && was_visible { - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + fragment.len()), ); - new_visible_text.remove(deleted_range); } new_fragments.push(fragment, &()); - cursor.next(); + fragments_cursor.next(); if let Some(split_id) = insertion_splits.next() { new_fragments.push_tree( - cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()), + fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()), &(), ); } else { @@ -1344,9 +1299,12 @@ impl Buffer { } } } else { - new_fragments = - cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); - while let Some(fragment) = cursor.item() { + new_fragments = fragments_cursor.slice( + &FragmentIdRef::new(&start_fragment_id), + SeekBias::Left, + &(), + ); + while let Some(fragment) = fragments_cursor.item() { if fragment.id > end_fragment_id { break; } else { @@ -1358,37 +1316,42 @@ impl Buffer { 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 { + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + } + if fragment.visible && !was_visible { - let visible_start = new_fragments.summary().text.deleted; - let visible_range = visible_start..visible_start + fragment.len(); - new_visible_text.insert( - new_fragments.summary().text.visible, - &new_deleted_text.slice(visible_range.clone()).to_string(), + new_visible_text.append( + deleted_text_cursor + .slice(new_fragments.summary().text.deleted + fragment.len()), ); - new_deleted_text.remove(visible_range); } else if !fragment.visible && was_visible { - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + fragment.len()), ); - new_visible_text.remove(deleted_range); } } new_fragments.push(fragment, &()); - cursor.next(); + fragments_cursor.next(); } } } - new_fragments.push_tree(cursor.suffix(&()), &()); - drop(cursor); + new_fragments.push_tree(fragments_cursor.suffix(&()), &()); + new_visible_text.append(visible_text_cursor.suffix()); + new_deleted_text.append(deleted_text_cursor.suffix()); + drop(fragments_cursor); + + self.fragments = new_fragments; self.visible_text = new_visible_text; self.deleted_text = new_deleted_text; - self.fragments = new_fragments; Ok(()) } @@ -1470,15 +1433,23 @@ impl Buffer { let mut ops = Vec::with_capacity(old_ranges.size_hint().0); let old_fragments = self.fragments.clone(); - let mut cursor = old_fragments.cursor::(); + let old_visible_text = self.visible_text.clone(); + let old_deleted_text = self.deleted_text.clone(); + + let mut fragments_cursor = old_fragments.cursor::(); + let mut visible_text_cursor = old_visible_text.cursor(0); + let mut deleted_text_cursor = old_deleted_text.cursor(0); + let mut new_fragments = SumTree::new(); - let mut new_visible_text = self.visible_text.clone(); - let mut new_deleted_text = self.deleted_text.clone(); + let mut new_visible_text = Rope::new(); + let mut new_deleted_text = Rope::new(); new_fragments.push_tree( - cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), + fragments_cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), &(), ); + new_visible_text.append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_deleted_text.append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); let mut start_id = None; let mut start_offset = None; @@ -1489,10 +1460,10 @@ impl Buffer { let mut local_timestamp = self.local_clock.tick(); let mut lamport_timestamp = self.lamport_clock.tick(); - while cur_range.is_some() && cursor.item().is_some() { - let mut fragment = cursor.item().unwrap().clone(); - let fragment_summary = cursor.item_summary().unwrap(); - let mut fragment_start = *cursor.start(); + while cur_range.is_some() && fragments_cursor.item().is_some() { + let mut fragment = fragments_cursor.item().unwrap().clone(); + let fragment_summary = fragments_cursor.item_summary().unwrap(); + let mut fragment_start = *fragments_cursor.start(); let mut fragment_end = fragment_start + fragment.visible_len(); let old_split_tree = self @@ -1546,8 +1517,12 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible, &new_text); + + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); new_fragments.push(new_fragment, &()); + new_visible_text.push(&new_text); } } @@ -1559,18 +1534,20 @@ impl Buffer { prefix.id = FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id); version_in_range.observe_all(&fragment_summary.max_version); - if fragment.visible { + if prefix.visible { prefix.deletions.insert(local_timestamp); prefix.visible = false; - // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + prefix.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + prefix.len()), ); - new_visible_text.remove(deleted_range); } fragment.range_in_insertion.start = prefix.range_in_insertion.end; new_fragments.push(prefix.clone(), &()); @@ -1592,14 +1569,16 @@ 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; - let deleted_range = deleted_start..deleted_start + fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + new_deleted_text.append( + visible_text_cursor + .slice(new_fragments.summary().text.visible + fragment.len()), ); - new_visible_text.remove(deleted_range); } } @@ -1651,11 +1630,11 @@ impl Buffer { new_fragments.push(fragment, &()); // Scan forward until we find a fragment that is not fully contained by the current splice. - cursor.next(); + fragments_cursor.next(); if let Some(range) = cur_range.clone() { - while let Some(fragment) = cursor.item() { - let fragment_summary = cursor.item_summary().unwrap(); - fragment_start = *cursor.start(); + while let Some(fragment) = fragments_cursor.item() { + let fragment_summary = fragments_cursor.item_summary().unwrap(); + fragment_start = *fragments_cursor.start(); fragment_end = fragment_start + fragment.visible_len(); if range.start < fragment_start && range.end >= fragment_end { let mut new_fragment = fragment.clone(); @@ -1664,17 +1643,20 @@ impl Buffer { new_fragment.deletions.insert(local_timestamp); new_fragment.visible = false; - // TODO: avoid calling to_string on rope slice. - let deleted_start = new_fragments.summary().text.visible; - let deleted_range = deleted_start..deleted_start + new_fragment.len(); - new_deleted_text.insert( - new_fragments.summary().text.deleted, - &new_visible_text.slice(deleted_range.clone()).to_string(), + new_visible_text.append( + visible_text_cursor.slice(new_fragments.summary().text.visible), + ); + new_deleted_text.append( + deleted_text_cursor.slice(new_fragments.summary().text.deleted), + ); + new_deleted_text.append( + visible_text_cursor.slice( + new_fragments.summary().text.visible + new_fragment.len(), + ), ); - new_visible_text.remove(deleted_range); } new_fragments.push(new_fragment, &()); - cursor.next(); + fragments_cursor.next(); if range.end == fragment_end { end_id = Some(fragment.insertion.id); @@ -1715,7 +1697,11 @@ impl Buffer { // and push all the fragments in between into the new tree. if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) { new_fragments.push_tree( - cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), + fragments_cursor.slice( + &cur_range.as_ref().unwrap().start, + SeekBias::Right, + &(), + ), &(), ); } @@ -1749,19 +1735,21 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text.insert(new_fragments.summary().text.visible, &new_text); + + new_visible_text + .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); new_fragments.push(new_fragment, &()); + new_visible_text.push(&new_text); } - } else { - new_fragments.push_tree( - cursor.slice(&old_fragments.extent::(), SeekBias::Right, &()), - &(), - ); } + new_fragments.push_tree(fragments_cursor.suffix(&()), &()); + new_visible_text.append(visible_text_cursor.suffix()); + new_deleted_text.append(deleted_text_cursor.suffix()); + + self.fragments = new_fragments; self.visible_text = new_visible_text; self.deleted_text = new_deleted_text; - self.fragments = new_fragments; ops } @@ -2031,9 +2019,9 @@ impl Buffer { impl Clone for Buffer { fn clone(&self) -> Self { Self { + fragments: self.fragments.clone(), visible_text: self.visible_text.clone(), deleted_text: self.deleted_text.clone(), - fragments: self.fragments.clone(), insertion_splits: self.insertion_splits.clone(), version: self.version.clone(), saved_version: self.saved_version.clone(), @@ -2409,12 +2397,7 @@ pub trait ToOffset { impl ToOffset for Point { fn to_offset(&self, buffer: &Buffer) -> Result { - if *self <= buffer.max_point() { - // TODO: return an error if line is shorter than column. - Ok(buffer.visible_text.line_to_char(self.row as usize) + self.column as usize) - } else { - Err(anyhow!("point is out of bounds")) - } + buffer.visible_text.to_offset(*self) } } @@ -2448,13 +2431,7 @@ impl ToPoint for Anchor { impl ToPoint for usize { fn to_point(&self, buffer: &Buffer) -> Result { - if *self <= buffer.len() { - let row = buffer.visible_text.char_to_line(*self); - let column = *self - buffer.visible_text.line_to_char(row); - Ok(Point::new(row as u32, column as u32)) - } else { - Err(anyhow!("offset is out of bounds")) - } + buffer.visible_text.to_point(*self) } } diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index ff80fffc0e6d8cc3444ddf7f11e70af823d9790d..c08733f62512019ef5c68f187cf8651e04f25130 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -100,6 +100,14 @@ impl Rope { self.chunks.summary() } + pub fn len(&self) -> usize { + self.chunks.extent() + } + + pub fn max_point(&self) -> Point { + self.chunks.extent() + } + pub fn cursor(&self, offset: usize) -> Cursor { Cursor::new(self, offset) } @@ -116,15 +124,7 @@ impl Rope { self.chunks.cursor::<(), ()>().map(|c| c.0.as_str()) } - fn text(&self) -> String { - let mut text = String::new(); - for chunk in self.chunks.cursor::<(), ()>() { - text.push_str(&chunk.0); - } - text - } - - fn to_point(&self, offset: usize) -> Result { + pub fn to_point(&self, offset: usize) -> Result { if offset <= self.summary().chars { let mut cursor = self.chunks.cursor::(); cursor.seek(&offset, SeekBias::Left, &()); @@ -135,7 +135,8 @@ impl Rope { } } - fn to_offset(&self, point: Point) -> Result { + pub fn to_offset(&self, point: Point) -> Result { + // TODO: Verify the point actually exists. if point <= self.summary().lines { let mut cursor = self.chunks.cursor::(); cursor.seek(&point, SeekBias::Left, &()); @@ -162,7 +163,7 @@ pub struct Cursor<'a> { } impl<'a> Cursor<'a> { - fn new(rope: &'a Rope, offset: usize) -> Self { + pub fn new(rope: &'a Rope, offset: usize) -> Self { let mut chunks = rope.chunks.cursor(); chunks.seek(&offset, SeekBias::Right, &()); Self { @@ -172,14 +173,14 @@ impl<'a> Cursor<'a> { } } - fn seek_forward(&mut self, end_offset: usize) { + pub fn seek_forward(&mut self, end_offset: usize) { debug_assert!(end_offset >= self.offset); self.chunks.seek_forward(&end_offset, SeekBias::Right, &()); self.offset = end_offset; } - fn slice(&mut self, end_offset: usize) -> Rope { + pub fn slice(&mut self, end_offset: usize) -> Rope { debug_assert!(end_offset >= self.offset); let mut slice = Rope::new(); @@ -203,7 +204,7 @@ impl<'a> Cursor<'a> { slice } - fn suffix(mut self) -> Rope { + pub fn suffix(mut self) -> Rope { self.slice(self.rope.chunks.extent()) } } @@ -456,4 +457,14 @@ mod tests { } } } + + impl Rope { + fn text(&self) -> String { + let mut text = String::new(); + for chunk in self.chunks.cursor::<(), ()>() { + text.push_str(&chunk.0); + } + text + } + } } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index daf182c2e79ffbb3ab219e8a42c6892d033c5083..b404bcb3dc766fd1a8688c867ed9ae140eff2b98 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -3,6 +3,7 @@ use super::{ Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset, }; use crate::{ + editor::rope, sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree}, time, }; @@ -607,7 +608,7 @@ pub struct Chars<'a> { cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>, offset: usize, buffer: &'a Buffer, - buffer_chars: Option>>, + buffer_chars: Option>>, } impl<'a> Iterator for Chars<'a> { diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 29df096a4f2278764351a5e7f8910b2e4585196d..2a924cf58636d2788514d40195906fd8ea9897b5 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -3,7 +3,7 @@ mod fuzzy; mod ignore; use crate::{ - editor::History, + editor::{History, Rope}, sum_tree::{self, Cursor, Edit, SeekBias, SumTree}, }; use ::ignore::gitignore::Gitignore; @@ -16,7 +16,6 @@ use postage::{ prelude::{Sink, Stream}, watch, }; -use ropey::Rope; use smol::channel::Sender; use std::{ cmp, @@ -204,7 +203,7 @@ impl Worktree { let path = path.to_path_buf(); let abs_path = self.absolutize(&path); ctx.background_executor().spawn(async move { - let buffer_size = content.len_bytes().min(10 * 1024); + let buffer_size = content.summary().bytes.min(10 * 1024); let file = fs::File::create(&abs_path)?; let mut writer = io::BufWriter::with_capacity(buffer_size, &file); for chunk in content.chunks() { From cba86033350742b61e15768ea567c167d7fc0b3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 14 May 2021 10:58:02 -0600 Subject: [PATCH 17/20] WIP --- zed/src/editor/buffer/mod.rs | 75 ++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 5538f6474f5ef21d0e4d134537ed33bf120dde24..633d0e8a525ab1972518d174eb7475030fc915a6 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1020,14 +1020,16 @@ impl Buffer { let old_visible_text = self.visible_text.clone(); let old_deleted_text = self.deleted_text.clone(); + let mut builder = RopeBuilder::new(old_visible_text.cursor(0), new_visible_text.cursor(0)); let mut fragments_cursor = old_fragments.cursor::(); - let mut visible_text_cursor = old_visible_text.cursor(0); - let mut deleted_text_cursor = old_deleted_text.cursor(0); let mut new_fragments = fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); - let mut new_visible_text = visible_text_cursor.slice(fragments_cursor.start().visible); - let mut new_deleted_text = deleted_text_cursor.slice(fragments_cursor.start().deleted); + + builder.keep_to( + new_fragments.summary().text.visible, + new_fragments.summary().text.deleted, + ); let start_fragment = fragments_cursor.item().unwrap(); if start_offset == start_fragment.range_in_insertion.end { @@ -3509,3 +3511,68 @@ mod tests { lengths } } + +struct RopeBuilder<'a> { + visible_delta: isize, + deleted_delta: isize, + old_visible_cursor: rope::Cursor<'a>, + old_deleted_cursor: rope::Cursor<'a>, + new_visible: Rope, + new_deleted: Rope, +} + +impl<'a> RopeBuilder<'a> { + fn new(old_visible_cursor: rope::Cursor<'a>, old_deleted_cursor: rope::Cursor<'a>) -> Self { + Self { + visible_delta: 0, + deleted_delta: 0, + old_visible_cursor, + old_deleted_cursor, + new_visible: Rope::new(), + new_deleted: Rope::new(), + } + } + + fn keep_to(&mut self, sum: FragmentTextSummary) { + self.new_visible.append( + self.old_visible_cursor + .slice((sum.visible as isize + self.visible_delta) as usize), + ); + self.new_deleted.append( + self.old_deleted_cursor + .slice((sum.deleted as isize + self.deleted_delta) as usize), + ); + } + + fn delete_to(&mut self, offset: usize) { + let deleted = self + .old_visible_cursor + .slice((offset as isize + self.visible_delta) as usize); + let deleted_len = deleted.len(); + self.new_deleted.append(deleted); + self.visible_delta += deleted_len as isize; + self.deleted_delta -= deleted_len as isize; + } + + fn restore_to(&mut self, offset: usize) { + let restored = self + .old_deleted_cursor + .slice((offset as isize + self.deleted_delta) as usize); + let restored_len = restored.len(); + self.new_visible.append(restored); + self.visible_delta -= restored_len as isize; + self.deleted_delta += restored_len as isize; + } + + fn insert(&mut self, text: &str) { + let old_len = self.new_visible.len(); + self.new_visible.push(text); + self.visible_delta -= (self.new_visible.len() - old_len) as isize; + } + + fn finish(self) -> (Rope, Rope) { + self.new_visible.append(self.old_visible_cursor.suffix()); + self.new_deleted.append(self.old_deleted_cursor.suffix()); + (self.new_visible, self.new_deleted) + } +} From 79c91de2f48ae58aca4f1b98b81e0bb5a0192286 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 May 2021 12:47:19 -0700 Subject: [PATCH 18/20] Get tests passing with RopeBuilder Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 394 +++++++++++++++------------------- zed/src/editor/buffer/rope.rs | 11 +- 2 files changed, 179 insertions(+), 226 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 633d0e8a525ab1972518d174eb7475030fc915a6..3b5c657f1fa2268530700ad69a1928a31961efd3 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -12,7 +12,7 @@ use similar::{ChangeTag, TextDiff}; use crate::{ operation_queue::{self, OperationQueue}, - sum_tree::{self, FilterCursor, SeekBias, SumTree}, + sum_tree::{self, FilterCursor, Item, SeekBias, SumTree}, time::{self, ReplicaId}, util::RandomCharIter, worktree::FileHandle, @@ -25,6 +25,7 @@ use std::{ cmp, hash::BuildHasher, iter::{self, Iterator}, + mem, ops::Range, str, sync::Arc, @@ -1016,24 +1017,26 @@ impl Buffer { let start_fragment_id = self.resolve_fragment_id(start_id, start_offset)?; let end_fragment_id = self.resolve_fragment_id(end_id, end_offset)?; - let old_fragments = self.fragments.clone(); - let old_visible_text = self.visible_text.clone(); - let old_deleted_text = self.deleted_text.clone(); + let mut old_visible_text = Rope::new(); + let mut old_deleted_text = Rope::new(); + let mut old_fragments = SumTree::new(); + mem::swap(&mut old_visible_text, &mut self.visible_text); + mem::swap(&mut old_deleted_text, &mut self.deleted_text); + mem::swap(&mut old_fragments, &mut self.fragments); - let mut builder = RopeBuilder::new(old_visible_text.cursor(0), new_visible_text.cursor(0)); + let mut new_ropes = + RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); let mut fragments_cursor = old_fragments.cursor::(); let mut new_fragments = fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); - - builder.keep_to( - new_fragments.summary().text.visible, - new_fragments.summary().text.deleted, - ); + new_ropes.keep(new_fragments.summary().text); let start_fragment = fragments_cursor.item().unwrap(); if start_offset == start_fragment.range_in_insertion.end { - new_fragments.push(fragments_cursor.item().unwrap().clone(), &()); + let fragment = fragments_cursor.item().unwrap().clone(); + new_ropes.keep(fragment.summary().text); + new_fragments.push(fragment, &()); fragments_cursor.next(); } @@ -1073,35 +1076,31 @@ impl Buffer { None }; if let Some(fragment) = before_range { + new_ropes.keep(fragment.summary().text); new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_ropes.insert(new_text.take().unwrap()); new_fragments.push(fragment, &()); - new_visible_text.push(new_text.take().unwrap()); } if let Some(mut fragment) = within_range { + let fragment_was_visible = fragment.visible; if fragment.was_visible(&version_in_range, &self.undo_map) { fragment.deletions.insert(local_timestamp); if fragment.visible { fragment.visible = false; - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + fragment.len()), - ); } } + if fragment_was_visible && !fragment.visible { + new_ropes.delete(fragment.len()); + } else { + new_ropes.keep(fragment.summary().text); + } new_fragments.push(fragment, &()); } if let Some(fragment) = after_range { + new_ropes.keep(fragment.summary().text); new_fragments.push(fragment, &()); } } else { @@ -1114,31 +1113,25 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_ropes.insert(new_text); new_fragments.push(fragment, &()); - new_visible_text.push(new_text); } + let fragment_was_visible = fragment.visible; if fragment.id < end_fragment_id && fragment.was_visible(&version_in_range, &self.undo_map) { fragment.deletions.insert(local_timestamp); if fragment.visible { fragment.visible = false; - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + fragment.len()), - ); } } + if fragment_was_visible && !fragment.visible { + new_ropes.delete(fragment.len()); + } else { + new_ropes.keep(fragment.summary().text); + } new_fragments.push(fragment, &()); } @@ -1153,19 +1146,16 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_ropes.insert(new_text); new_fragments.push(fragment, &()); - new_visible_text.push(new_text); } + let (visible_text, deleted_text) = new_ropes.finish(); new_fragments.push_tree(fragments_cursor.suffix(&()), &()); - new_visible_text.append(visible_text_cursor.suffix()); - new_deleted_text.append(deleted_text_cursor.suffix()); self.fragments = new_fragments; - self.visible_text = new_visible_text; - self.deleted_text = new_deleted_text; + self.visible_text = visible_text; + self.deleted_text = deleted_text; self.local_clock.observe(local_timestamp); self.lamport_clock.observe(lamport_timestamp); Ok(()) @@ -1240,8 +1230,12 @@ impl Buffer { fn apply_undo(&mut self, undo: UndoOperation) -> Result<()> { let mut new_fragments; - let mut new_visible_text = Rope::new(); - let mut new_deleted_text = Rope::new(); + let mut old_visible_text = Rope::new(); + let mut old_deleted_text = Rope::new(); + mem::swap(&mut old_visible_text, &mut self.visible_text); + mem::swap(&mut old_deleted_text, &mut self.deleted_text); + let mut new_ropes = + RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); self.undo_map.insert(undo); let edit = &self.history.ops[&undo.edit_id]; @@ -1249,8 +1243,6 @@ impl Buffer { let end_fragment_id = self.resolve_fragment_id(edit.end_id, edit.end_offset)?; let mut fragments_cursor = self.fragments.cursor::(); - let mut visible_text_cursor = self.visible_text.cursor(0); - let mut deleted_text_cursor = self.deleted_text.cursor(0); if edit.start_id == edit.end_id && edit.start_offset == edit.end_offset { let splits = &self.insertion_splits[&undo.edit_id]; @@ -1259,10 +1251,8 @@ impl Buffer { let first_split_id = insertion_splits.next().unwrap(); new_fragments = fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); - new_deleted_text - .append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); + new_ropes.keep(new_fragments.summary().text); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); loop { let mut fragment = fragments_cursor.item().unwrap().clone(); @@ -1270,32 +1260,23 @@ impl Buffer { fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); - if fragment.visible != was_visible { - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); - new_deleted_text - .append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); - } - if fragment.visible && !was_visible { - new_visible_text.append( - deleted_text_cursor - .slice(new_fragments.summary().text.deleted + fragment.len()), - ); + new_ropes.restore(fragment.len()); } else if !fragment.visible && was_visible { - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + fragment.len()), - ); + new_ropes.delete(fragment.len()); + } else { + new_ropes.keep(fragment.summary().text); } - new_fragments.push(fragment, &()); + new_fragments.push(fragment.clone(), &()); + assert_eq!(new_ropes.summary(), new_fragments.summary().text,); + fragments_cursor.next(); if let Some(split_id) = insertion_splits.next() { - new_fragments.push_tree( - fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()), - &(), - ); + let slice = + fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()); + new_ropes.keep(slice.summary().text); + new_fragments.push_tree(slice, &()); } else { break; } @@ -1306,6 +1287,9 @@ impl Buffer { SeekBias::Left, &(), ); + new_ropes.keep(new_fragments.summary().text); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); + while let Some(fragment) = fragments_cursor.item() { if fragment.id > end_fragment_id { break; @@ -1318,42 +1302,32 @@ impl Buffer { fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); - if fragment.visible != was_visible { - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - } - if fragment.visible && !was_visible { - new_visible_text.append( - deleted_text_cursor - .slice(new_fragments.summary().text.deleted + fragment.len()), - ); + new_ropes.restore(fragment.len()); } else if !fragment.visible && was_visible { - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + fragment.len()), - ); + new_ropes.delete(fragment.len()); + } else { + new_ropes.keep(fragment.summary().text); } + } else { + new_ropes.keep(fragment.summary().text); } new_fragments.push(fragment, &()); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); + fragments_cursor.next(); } } } new_fragments.push_tree(fragments_cursor.suffix(&()), &()); - new_visible_text.append(visible_text_cursor.suffix()); - new_deleted_text.append(deleted_text_cursor.suffix()); + let (visible_text, deleted_text) = new_ropes.finish(); drop(fragments_cursor); self.fragments = new_fragments; - self.visible_text = new_visible_text; - self.deleted_text = new_deleted_text; + self.visible_text = visible_text; + self.deleted_text = deleted_text; Ok(()) } @@ -1434,24 +1408,21 @@ impl Buffer { let mut ops = Vec::with_capacity(old_ranges.size_hint().0); - let old_fragments = self.fragments.clone(); - let old_visible_text = self.visible_text.clone(); - let old_deleted_text = self.deleted_text.clone(); + let mut old_fragments = SumTree::new(); + let mut old_visible_text = Rope::new(); + let mut old_deleted_text = Rope::new(); + mem::swap(&mut old_visible_text, &mut self.visible_text); + mem::swap(&mut old_deleted_text, &mut self.deleted_text); + mem::swap(&mut old_fragments, &mut self.fragments); let mut fragments_cursor = old_fragments.cursor::(); - let mut visible_text_cursor = old_visible_text.cursor(0); - let mut deleted_text_cursor = old_deleted_text.cursor(0); - - let mut new_fragments = SumTree::new(); - let mut new_visible_text = Rope::new(); - let mut new_deleted_text = Rope::new(); + let mut new_fragments = + fragments_cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()); - new_fragments.push_tree( - fragments_cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()), - &(), - ); - new_visible_text.append(visible_text_cursor.slice(new_fragments.summary().text.visible)); - new_deleted_text.append(deleted_text_cursor.slice(new_fragments.summary().text.deleted)); + let mut new_ropes = + RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); + new_ropes.keep(new_fragments.summary().text); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); let mut start_id = None; let mut start_offset = None; @@ -1467,6 +1438,7 @@ impl Buffer { let fragment_summary = fragments_cursor.item_summary().unwrap(); let mut fragment_start = *fragments_cursor.start(); let mut fragment_end = fragment_start + fragment.visible_len(); + let fragment_was_visible = fragment.visible; let old_split_tree = self .insertion_splits @@ -1488,7 +1460,11 @@ impl Buffer { prefix.id = FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id); fragment.range_in_insertion.start = prefix.range_in_insertion.end; + + new_ropes.keep(prefix.summary().text); new_fragments.push(prefix.clone(), &()); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); + new_split_tree.push( InsertionSplit { extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start, @@ -1520,11 +1496,9 @@ impl Buffer { lamport_timestamp, ); - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); + new_ropes.insert(&new_text); new_fragments.push(new_fragment, &()); - new_visible_text.push(&new_text); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); } } @@ -1539,20 +1513,14 @@ impl Buffer { if prefix.visible { prefix.deletions.insert(local_timestamp); prefix.visible = false; - - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + prefix.len()), - ); + new_ropes.delete(prefix.len()); + } else { + new_ropes.keep(prefix.summary().text); } fragment.range_in_insertion.start = prefix.range_in_insertion.end; new_fragments.push(prefix.clone(), &()); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); + new_split_tree.push( InsertionSplit { extent: prefix.range_in_insertion.end @@ -1570,17 +1538,6 @@ impl Buffer { if fragment.visible { fragment.deletions.insert(local_timestamp); fragment.visible = false; - - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - new_deleted_text.append( - visible_text_cursor - .slice(new_fragments.summary().text.visible + fragment.len()), - ); } } @@ -1629,7 +1586,14 @@ impl Buffer { ); self.insertion_splits .insert(fragment.insertion.id, new_split_tree); + + if fragment_was_visible && !fragment.visible { + new_ropes.delete(fragment.len()); + } else { + new_ropes.keep(fragment.summary().text); + } new_fragments.push(fragment, &()); + assert_eq!(new_ropes.summary(), new_fragments.summary().text); // Scan forward until we find a fragment that is not fully contained by the current splice. fragments_cursor.next(); @@ -1644,18 +1608,9 @@ impl Buffer { if new_fragment.visible { new_fragment.deletions.insert(local_timestamp); new_fragment.visible = false; - - new_visible_text.append( - visible_text_cursor.slice(new_fragments.summary().text.visible), - ); - new_deleted_text.append( - deleted_text_cursor.slice(new_fragments.summary().text.deleted), - ); - new_deleted_text.append( - visible_text_cursor.slice( - new_fragments.summary().text.visible + new_fragment.len(), - ), - ); + new_ropes.delete(new_fragment.len()); + } else { + new_ropes.keep(new_fragment.summary().text); } new_fragments.push(new_fragment, &()); fragments_cursor.next(); @@ -1698,14 +1653,13 @@ impl Buffer { // that the cursor is parked at, we should seek to the next splice's start range // and push all the fragments in between into the new tree. if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) { - new_fragments.push_tree( - fragments_cursor.slice( - &cur_range.as_ref().unwrap().start, - SeekBias::Right, - &(), - ), + let slice = fragments_cursor.slice( + &cur_range.as_ref().unwrap().start, + SeekBias::Right, &(), ); + new_ropes.keep(slice.summary().text); + new_fragments.push_tree(slice, &()); } } } @@ -1738,20 +1692,17 @@ impl Buffer { lamport_timestamp, ); - new_visible_text - .append(visible_text_cursor.slice(new_fragments.summary().text.visible)); + new_ropes.insert(&new_text); new_fragments.push(new_fragment, &()); - new_visible_text.push(&new_text); } } new_fragments.push_tree(fragments_cursor.suffix(&()), &()); - new_visible_text.append(visible_text_cursor.suffix()); - new_deleted_text.append(deleted_text_cursor.suffix()); + let (visible_text, deleted_text) = new_ropes.finish(); self.fragments = new_fragments; - self.visible_text = new_visible_text; - self.deleted_text = new_deleted_text; + self.visible_text = visible_text; + self.deleted_text = deleted_text; ops } @@ -2043,6 +1994,66 @@ impl Clone for Buffer { } } +struct RopeBuilder<'a> { + old_visible_cursor: rope::Cursor<'a>, + old_deleted_cursor: rope::Cursor<'a>, + new_visible: Rope, + new_deleted: Rope, +} + +impl<'a> RopeBuilder<'a> { + fn new(old_visible_cursor: rope::Cursor<'a>, old_deleted_cursor: rope::Cursor<'a>) -> Self { + Self { + old_visible_cursor, + old_deleted_cursor, + new_visible: Rope::new(), + new_deleted: Rope::new(), + } + } + + fn keep(&mut self, len: FragmentTextSummary) { + let visible_text = self + .old_visible_cursor + .slice(self.old_visible_cursor.offset() + len.visible); + let deleted_text = self + .old_deleted_cursor + .slice(self.old_deleted_cursor.offset() + len.deleted); + self.new_visible.append(visible_text); + self.new_deleted.append(deleted_text); + } + + fn delete(&mut self, deleted_len: usize) { + let deleted = self + .old_visible_cursor + .slice(self.old_visible_cursor.offset() + deleted_len); + self.new_deleted.append(deleted); + } + + fn restore(&mut self, restored_len: usize) { + let restored = self + .old_deleted_cursor + .slice(self.old_deleted_cursor.offset() + restored_len); + self.new_visible.append(restored); + } + + fn insert(&mut self, text: &str) { + self.new_visible.push(text); + } + + fn summary(&self) -> FragmentTextSummary { + FragmentTextSummary { + visible: self.new_visible.len(), + deleted: self.new_deleted.len(), + } + } + + fn finish(mut self) -> (Rope, Rope) { + self.new_visible.append(self.old_visible_cursor.suffix()); + self.new_deleted.append(self.old_deleted_cursor.suffix()); + (self.new_visible, self.new_deleted) + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { Edited, @@ -3511,68 +3522,3 @@ mod tests { lengths } } - -struct RopeBuilder<'a> { - visible_delta: isize, - deleted_delta: isize, - old_visible_cursor: rope::Cursor<'a>, - old_deleted_cursor: rope::Cursor<'a>, - new_visible: Rope, - new_deleted: Rope, -} - -impl<'a> RopeBuilder<'a> { - fn new(old_visible_cursor: rope::Cursor<'a>, old_deleted_cursor: rope::Cursor<'a>) -> Self { - Self { - visible_delta: 0, - deleted_delta: 0, - old_visible_cursor, - old_deleted_cursor, - new_visible: Rope::new(), - new_deleted: Rope::new(), - } - } - - fn keep_to(&mut self, sum: FragmentTextSummary) { - self.new_visible.append( - self.old_visible_cursor - .slice((sum.visible as isize + self.visible_delta) as usize), - ); - self.new_deleted.append( - self.old_deleted_cursor - .slice((sum.deleted as isize + self.deleted_delta) as usize), - ); - } - - fn delete_to(&mut self, offset: usize) { - let deleted = self - .old_visible_cursor - .slice((offset as isize + self.visible_delta) as usize); - let deleted_len = deleted.len(); - self.new_deleted.append(deleted); - self.visible_delta += deleted_len as isize; - self.deleted_delta -= deleted_len as isize; - } - - fn restore_to(&mut self, offset: usize) { - let restored = self - .old_deleted_cursor - .slice((offset as isize + self.deleted_delta) as usize); - let restored_len = restored.len(); - self.new_visible.append(restored); - self.visible_delta -= restored_len as isize; - self.deleted_delta += restored_len as isize; - } - - fn insert(&mut self, text: &str) { - let old_len = self.new_visible.len(); - self.new_visible.push(text); - self.visible_delta -= (self.new_visible.len() - old_len) as isize; - } - - fn finish(self) -> (Rope, Rope) { - self.new_visible.append(self.old_visible_cursor.suffix()); - self.new_deleted.append(self.old_deleted_cursor.suffix()); - (self.new_visible, self.new_deleted) - } -} diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index c08733f62512019ef5c68f187cf8651e04f25130..74a680286bc0ad2a0430094308d3040f8499f966 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -129,7 +129,10 @@ impl Rope { let mut cursor = self.chunks.cursor::(); cursor.seek(&offset, SeekBias::Left, &()); let overshoot = offset - cursor.start().chars; - Ok(cursor.start().lines + cursor.item().unwrap().to_point(overshoot)) + Ok(cursor.start().lines + + cursor + .item() + .map_or(Point::zero(), |chunk| chunk.to_point(overshoot))) } else { Err(anyhow!("offset out of bounds")) } @@ -141,7 +144,7 @@ impl Rope { let mut cursor = self.chunks.cursor::(); cursor.seek(&point, SeekBias::Left, &()); let overshoot = point - cursor.start().lines; - Ok(cursor.start().chars + cursor.item().unwrap().to_offset(overshoot)) + Ok(cursor.start().chars + cursor.item().map_or(0, |chunk| chunk.to_offset(overshoot))) } else { Err(anyhow!("offset out of bounds")) } @@ -207,6 +210,10 @@ impl<'a> Cursor<'a> { pub fn suffix(mut self) -> Rope { self.slice(self.rope.chunks.extent()) } + + pub fn offset(&self) -> usize { + self.offset + } } #[derive(Clone, Debug, Default)] From fc2533555c8411ae8667bf681a57dbbbbda4144a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 May 2021 14:07:25 -0700 Subject: [PATCH 19/20] Remove inline assertions about RopeBuilder invariants --- zed/src/editor/buffer/mod.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 3b5c657f1fa2268530700ad69a1928a31961efd3..f235deafefed4ac85b65eaee9e3e7301a1b6ded0 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1252,7 +1252,6 @@ impl Buffer { new_fragments = fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); new_ropes.keep(new_fragments.summary().text); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); loop { let mut fragment = fragments_cursor.item().unwrap().clone(); @@ -1269,7 +1268,6 @@ impl Buffer { } new_fragments.push(fragment.clone(), &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text,); fragments_cursor.next(); if let Some(split_id) = insertion_splits.next() { @@ -1288,7 +1286,6 @@ impl Buffer { &(), ); new_ropes.keep(new_fragments.summary().text); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); while let Some(fragment) = fragments_cursor.item() { if fragment.id > end_fragment_id { @@ -1314,8 +1311,6 @@ impl Buffer { } new_fragments.push(fragment, &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); - fragments_cursor.next(); } } @@ -1422,7 +1417,6 @@ impl Buffer { let mut new_ropes = RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); new_ropes.keep(new_fragments.summary().text); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); let mut start_id = None; let mut start_offset = None; @@ -1463,8 +1457,6 @@ impl Buffer { new_ropes.keep(prefix.summary().text); new_fragments.push(prefix.clone(), &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); - new_split_tree.push( InsertionSplit { extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start, @@ -1498,7 +1490,6 @@ impl Buffer { new_ropes.insert(&new_text); new_fragments.push(new_fragment, &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); } } @@ -1519,8 +1510,6 @@ impl Buffer { } fragment.range_in_insertion.start = prefix.range_in_insertion.end; new_fragments.push(prefix.clone(), &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); - new_split_tree.push( InsertionSplit { extent: prefix.range_in_insertion.end @@ -1593,7 +1582,6 @@ impl Buffer { new_ropes.keep(fragment.summary().text); } new_fragments.push(fragment, &()); - assert_eq!(new_ropes.summary(), new_fragments.summary().text); // Scan forward until we find a fragment that is not fully contained by the current splice. fragments_cursor.next(); From 614e96b95706c1463c1dc4a95129f05136600aa9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 May 2021 14:48:19 -0700 Subject: [PATCH 20/20] Clarify how the ropes are kept consistent with the fragments --- zed/src/editor/buffer/mod.rs | 132 +++++++++++++---------------------- 1 file changed, 47 insertions(+), 85 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index f235deafefed4ac85b65eaee9e3e7301a1b6ded0..78c7c2f2af4cbe6ac77a736a0e18282803f1e67c 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -12,7 +12,7 @@ use similar::{ChangeTag, TextDiff}; use crate::{ operation_queue::{self, OperationQueue}, - sum_tree::{self, FilterCursor, Item, SeekBias, SumTree}, + sum_tree::{self, FilterCursor, SeekBias, SumTree}, time::{self, ReplicaId}, util::RandomCharIter, worktree::FileHandle, @@ -1024,18 +1024,18 @@ impl Buffer { mem::swap(&mut old_deleted_text, &mut self.deleted_text); mem::swap(&mut old_fragments, &mut self.fragments); - let mut new_ropes = - RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); let mut fragments_cursor = old_fragments.cursor::(); let mut new_fragments = fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &()); - new_ropes.keep(new_fragments.summary().text); + let mut new_ropes = + RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); + new_ropes.push_tree(new_fragments.summary().text); let start_fragment = fragments_cursor.item().unwrap(); if start_offset == start_fragment.range_in_insertion.end { let fragment = fragments_cursor.item().unwrap().clone(); - new_ropes.keep(fragment.summary().text); + new_ropes.push_fragment(&fragment, fragment.visible); new_fragments.push(fragment, &()); fragments_cursor.next(); } @@ -1076,11 +1076,11 @@ impl Buffer { None }; if let Some(fragment) = before_range { - new_ropes.keep(fragment.summary().text); + new_ropes.push_fragment(&fragment, fragment.visible); new_fragments.push(fragment, &()); } if let Some(fragment) = insertion { - new_ropes.insert(new_text.take().unwrap()); + new_ropes.push_str(new_text.take().unwrap()); new_fragments.push(fragment, &()); } if let Some(mut fragment) = within_range { @@ -1092,15 +1092,11 @@ impl Buffer { } } - if fragment_was_visible && !fragment.visible { - new_ropes.delete(fragment.len()); - } else { - new_ropes.keep(fragment.summary().text); - } + new_ropes.push_fragment(&fragment, fragment_was_visible); new_fragments.push(fragment, &()); } if let Some(fragment) = after_range { - new_ropes.keep(fragment.summary().text); + new_ropes.push_fragment(&fragment, fragment.visible); new_fragments.push(fragment, &()); } } else { @@ -1113,7 +1109,7 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_ropes.insert(new_text); + new_ropes.push_str(new_text); new_fragments.push(fragment, &()); } @@ -1127,11 +1123,7 @@ impl Buffer { } } - if fragment_was_visible && !fragment.visible { - new_ropes.delete(fragment.len()); - } else { - new_ropes.keep(fragment.summary().text); - } + new_ropes.push_fragment(&fragment, fragment_was_visible); new_fragments.push(fragment, &()); } @@ -1146,7 +1138,7 @@ impl Buffer { local_timestamp, lamport_timestamp, ); - new_ropes.insert(new_text); + new_ropes.push_str(new_text); new_fragments.push(fragment, &()); } @@ -1251,7 +1243,7 @@ impl Buffer { let first_split_id = insertion_splits.next().unwrap(); new_fragments = fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &()); - new_ropes.keep(new_fragments.summary().text); + new_ropes.push_tree(new_fragments.summary().text); loop { let mut fragment = fragments_cursor.item().unwrap().clone(); @@ -1259,21 +1251,14 @@ impl Buffer { fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); - if fragment.visible && !was_visible { - new_ropes.restore(fragment.len()); - } else if !fragment.visible && was_visible { - new_ropes.delete(fragment.len()); - } else { - new_ropes.keep(fragment.summary().text); - } - + new_ropes.push_fragment(&fragment, was_visible); new_fragments.push(fragment.clone(), &()); fragments_cursor.next(); if let Some(split_id) = insertion_splits.next() { let slice = fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()); - new_ropes.keep(slice.summary().text); + new_ropes.push_tree(slice.summary().text); new_fragments.push_tree(slice, &()); } else { break; @@ -1285,31 +1270,22 @@ impl Buffer { SeekBias::Left, &(), ); - new_ropes.keep(new_fragments.summary().text); + new_ropes.push_tree(new_fragments.summary().text); while let Some(fragment) = fragments_cursor.item() { if fragment.id > end_fragment_id { break; } else { let mut fragment = fragment.clone(); + let fragment_was_visible = fragment.visible; if edit.version_in_range.observed(fragment.insertion.id) || fragment.insertion.id == undo.edit_id { - let was_visible = fragment.visible; fragment.visible = fragment.is_visible(&self.undo_map); fragment.max_undos.observe(undo.id); - - if fragment.visible && !was_visible { - new_ropes.restore(fragment.len()); - } else if !fragment.visible && was_visible { - new_ropes.delete(fragment.len()); - } else { - new_ropes.keep(fragment.summary().text); - } - } else { - new_ropes.keep(fragment.summary().text); } + new_ropes.push_fragment(&fragment, fragment_was_visible); new_fragments.push(fragment, &()); fragments_cursor.next(); } @@ -1416,7 +1392,7 @@ impl Buffer { let mut new_ropes = RopeBuilder::new(old_visible_text.cursor(0), old_deleted_text.cursor(0)); - new_ropes.keep(new_fragments.summary().text); + new_ropes.push_tree(new_fragments.summary().text); let mut start_id = None; let mut start_offset = None; @@ -1455,7 +1431,7 @@ impl Buffer { FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id); fragment.range_in_insertion.start = prefix.range_in_insertion.end; - new_ropes.keep(prefix.summary().text); + new_ropes.push_fragment(&prefix, prefix.visible); new_fragments.push(prefix.clone(), &()); new_split_tree.push( InsertionSplit { @@ -1488,7 +1464,7 @@ impl Buffer { lamport_timestamp, ); - new_ropes.insert(&new_text); + new_ropes.push_str(&new_text); new_fragments.push(new_fragment, &()); } } @@ -1504,11 +1480,9 @@ impl Buffer { if prefix.visible { prefix.deletions.insert(local_timestamp); prefix.visible = false; - new_ropes.delete(prefix.len()); - } else { - new_ropes.keep(prefix.summary().text); } fragment.range_in_insertion.start = prefix.range_in_insertion.end; + new_ropes.push_fragment(&prefix, fragment_was_visible); new_fragments.push(prefix.clone(), &()); new_split_tree.push( InsertionSplit { @@ -1576,11 +1550,7 @@ impl Buffer { self.insertion_splits .insert(fragment.insertion.id, new_split_tree); - if fragment_was_visible && !fragment.visible { - new_ropes.delete(fragment.len()); - } else { - new_ropes.keep(fragment.summary().text); - } + new_ropes.push_fragment(&fragment, fragment_was_visible); new_fragments.push(fragment, &()); // Scan forward until we find a fragment that is not fully contained by the current splice. @@ -1588,6 +1558,7 @@ impl Buffer { if let Some(range) = cur_range.clone() { while let Some(fragment) = fragments_cursor.item() { let fragment_summary = fragments_cursor.item_summary().unwrap(); + let fragment_was_visible = fragment.visible; fragment_start = *fragments_cursor.start(); fragment_end = fragment_start + fragment.visible_len(); if range.start < fragment_start && range.end >= fragment_end { @@ -1596,10 +1567,9 @@ impl Buffer { if new_fragment.visible { new_fragment.deletions.insert(local_timestamp); new_fragment.visible = false; - new_ropes.delete(new_fragment.len()); - } else { - new_ropes.keep(new_fragment.summary().text); } + + new_ropes.push_fragment(&new_fragment, fragment_was_visible); new_fragments.push(new_fragment, &()); fragments_cursor.next(); @@ -1646,7 +1616,7 @@ impl Buffer { SeekBias::Right, &(), ); - new_ropes.keep(slice.summary().text); + new_ropes.push_tree(slice.summary().text); new_fragments.push_tree(slice, &()); } } @@ -1680,7 +1650,7 @@ impl Buffer { lamport_timestamp, ); - new_ropes.insert(&new_text); + new_ropes.push_str(&new_text); new_fragments.push(new_fragment, &()); } } @@ -1999,42 +1969,34 @@ impl<'a> RopeBuilder<'a> { } } - fn keep(&mut self, len: FragmentTextSummary) { - let visible_text = self - .old_visible_cursor - .slice(self.old_visible_cursor.offset() + len.visible); - let deleted_text = self - .old_deleted_cursor - .slice(self.old_deleted_cursor.offset() + len.deleted); - self.new_visible.append(visible_text); - self.new_deleted.append(deleted_text); + fn push_tree(&mut self, len: FragmentTextSummary) { + self.push(len.visible, true, true); + self.push(len.deleted, false, false); } - fn delete(&mut self, deleted_len: usize) { - let deleted = self - .old_visible_cursor - .slice(self.old_visible_cursor.offset() + deleted_len); - self.new_deleted.append(deleted); + fn push_fragment(&mut self, fragment: &Fragment, was_visible: bool) { + self.push(fragment.len(), was_visible, fragment.visible) } - fn restore(&mut self, restored_len: usize) { - let restored = self - .old_deleted_cursor - .slice(self.old_deleted_cursor.offset() + restored_len); - self.new_visible.append(restored); + fn push(&mut self, len: usize, was_visible: bool, is_visible: bool) { + let text = if was_visible { + self.old_visible_cursor + .slice(self.old_visible_cursor.offset() + len) + } else { + self.old_deleted_cursor + .slice(self.old_deleted_cursor.offset() + len) + }; + if is_visible { + self.new_visible.append(text); + } else { + self.new_deleted.append(text); + } } - fn insert(&mut self, text: &str) { + fn push_str(&mut self, text: &str) { self.new_visible.push(text); } - fn summary(&self) -> FragmentTextSummary { - FragmentTextSummary { - visible: self.new_visible.len(), - deleted: self.new_deleted.len(), - } - } - fn finish(mut self) -> (Rope, Rope) { self.new_visible.append(self.old_visible_cursor.suffix()); self.new_deleted.append(self.old_deleted_cursor.suffix());