From 472ff1621f6186b38b0900a3bb07b7d9cbd3b12f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 9 Apr 2021 17:17:36 +0200 Subject: [PATCH] Merge `UndoHistory` and `History`, storing also operations in the latter --- zed/src/editor/buffer/mod.rs | 144 +++++++++++++++++----------------- zed/src/editor/buffer/text.rs | 20 +++-- zed/src/worktree/worktree.rs | 4 +- 3 files changed, 87 insertions(+), 81 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 074d4917e24b573eb82616db88c34979ad2ec498..dd873620040f3c896ae39eead099b2e63f15f9da 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -63,12 +63,11 @@ pub struct Buffer { file: Option, fragments: SumTree, insertion_splits: HashMap>, - edit_ops: HashMap, pub version: time::Global, saved_version: time::Global, last_edit: time::Local, undo_map: UndoMap, - undo_history: UndoHistory, + history: History, selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, @@ -82,54 +81,6 @@ pub struct Snapshot { fragments: SumTree, } -#[derive(Clone)] -pub struct History { - pub base_text: String, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Selection { - pub start: Anchor, - pub end: Anchor, - pub reversed: bool, -} - -#[derive(Clone, Default, Debug)] -struct UndoMap(HashMap>); - -impl UndoMap { - fn insert(&mut self, undo: UndoOperation) { - self.0.entry(undo.edit_id).or_default().push(undo); - } - - fn is_undone(&self, edit_id: time::Local) -> bool { - self.undo_count(edit_id) % 2 == 1 - } - - fn was_undone(&self, edit_id: time::Local, version: &time::Global) -> bool { - let undo_count = self - .0 - .get(&edit_id) - .unwrap_or(&Vec::new()) - .iter() - .filter(|undo| version.observed(undo.id)) - .map(|undo| undo.count) - .max() - .unwrap_or(0); - undo_count % 2 == 1 - } - - fn undo_count(&self, edit_id: time::Local) -> u32 { - self.0 - .get(&edit_id) - .unwrap_or(&Vec::new()) - .iter() - .map(|undo| undo.count) - .max() - .unwrap_or(0) - } -} - #[derive(Clone)] struct EditGroup { edits: Vec, @@ -137,22 +88,30 @@ struct EditGroup { } #[derive(Clone)] -struct UndoHistory { - group_interval: Duration, +pub struct History { + pub base_text: Arc, + ops: HashMap, undo_stack: Vec, redo_stack: Vec, + group_interval: Duration, } -impl UndoHistory { - fn new(group_interval: Duration) -> Self { +impl History { + pub fn new(base_text: Arc) -> Self { Self { - group_interval, + base_text, + ops: Default::default(), undo_stack: Vec::new(), redo_stack: Vec::new(), + group_interval: UNDO_GROUP_INTERVAL, } } - fn push(&mut self, edit_id: time::Local, now: Instant) { + fn push(&mut self, op: EditOperation) { + self.ops.insert(op.id, op); + } + + fn push_undo(&mut self, edit_id: time::Local, now: Instant) { if let Some(edit_group) = self.undo_stack.last_mut() { if now - edit_group.last_edit_at <= self.group_interval { edit_group.edits.push(edit_id); @@ -190,6 +149,49 @@ impl UndoHistory { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Selection { + pub start: Anchor, + pub end: Anchor, + pub reversed: bool, +} + +#[derive(Clone, Default, Debug)] +struct UndoMap(HashMap>); + +impl UndoMap { + fn insert(&mut self, undo: UndoOperation) { + self.0.entry(undo.edit_id).or_default().push(undo); + } + + fn is_undone(&self, edit_id: time::Local) -> bool { + self.undo_count(edit_id) % 2 == 1 + } + + fn was_undone(&self, edit_id: time::Local, version: &time::Global) -> bool { + let undo_count = self + .0 + .get(&edit_id) + .unwrap_or(&Vec::new()) + .iter() + .filter(|undo| version.observed(undo.id)) + .map(|undo| undo.count) + .max() + .unwrap_or(0); + undo_count % 2 == 1 + } + + fn undo_count(&self, edit_id: time::Local) -> u32 { + self.0 + .get(&edit_id) + .unwrap_or(&Vec::new()) + .iter() + .map(|undo| undo.count) + .max() + .unwrap_or(0) + } +} + #[derive(Clone)] pub struct CharIter<'a> { fragments_cursor: Cursor<'a, Fragment, usize, usize>, @@ -305,15 +307,15 @@ pub struct UndoOperation { } impl Buffer { - pub fn new>(replica_id: ReplicaId, base_text: T) -> Self { - Self::build(replica_id, None, base_text.into()) + pub fn new>>(replica_id: ReplicaId, base_text: T) -> Self { + Self::build(replica_id, None, History::new(base_text.into())) } pub fn from_history(replica_id: ReplicaId, file: FileHandle, history: History) -> Self { - Self::build(replica_id, Some(file), history.base_text) + Self::build(replica_id, Some(file), history) } - fn build(replica_id: ReplicaId, file: Option, base_text: String) -> Self { + fn build(replica_id: ReplicaId, file: Option, history: History) -> Self { let mut insertion_splits = HashMap::default(); let mut fragments = SumTree::new(); @@ -321,7 +323,7 @@ impl Buffer { id: time::Local::default(), parent_id: time::Local::default(), offset_in_parent: 0, - text: base_text.into(), + text: history.base_text.clone().into(), lamport_timestamp: time::Lamport::default(), }; @@ -366,12 +368,11 @@ impl Buffer { file, fragments, insertion_splits, - edit_ops: HashMap::default(), version: time::Global::new(), saved_version: time::Global::new(), last_edit: time::Local::default(), undo_map: Default::default(), - undo_history: UndoHistory::new(UNDO_GROUP_INTERVAL), + history, selections: HashMap::default(), selections_last_update: 0, deferred_ops: OperationQueue::new(), @@ -602,8 +603,8 @@ impl Buffer { for op in &ops { if let Operation::Edit { edit, .. } = op { - self.edit_ops.insert(edit.id, edit.clone()); - self.undo_history.push(edit.id, now); + self.history.push(edit.clone()); + self.history.push_undo(edit.id, now); } } @@ -864,7 +865,7 @@ impl Buffer { lamport_timestamp, )?; self.version.observe(edit.id); - self.edit_ops.insert(edit.id, edit); + self.history.push(edit); } } Operation::Undo { @@ -1017,7 +1018,7 @@ impl Buffer { let old_version = self.version.clone(); let mut ops = Vec::new(); - if let Some(edit_group) = self.undo_history.pop_undo() { + if let Some(edit_group) = self.history.pop_undo() { for edit_id in edit_group.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } @@ -1039,7 +1040,7 @@ impl Buffer { let old_version = self.version.clone(); let mut ops = Vec::new(); - if let Some(edit_group) = self.undo_history.pop_redo() { + if let Some(edit_group) = self.history.pop_redo() { for edit_id in edit_group.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } @@ -1075,7 +1076,7 @@ impl Buffer { let mut new_fragments; self.undo_map.insert(undo); - let edit = &self.edit_ops[&undo.edit_id]; + 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::(); @@ -1700,12 +1701,11 @@ impl Clone for Buffer { file: self.file.clone(), fragments: self.fragments.clone(), insertion_splits: self.insertion_splits.clone(), - edit_ops: self.edit_ops.clone(), version: self.version.clone(), saved_version: self.saved_version.clone(), last_edit: self.last_edit.clone(), undo_map: self.undo_map.clone(), - undo_history: self.undo_history.clone(), + history: self.history.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), @@ -3076,7 +3076,7 @@ mod tests { pub fn randomly_undo_redo(&mut self, rng: &mut impl Rng) -> Vec { let mut ops = Vec::new(); for _ in 0..rng.gen_range(1..5) { - if let Some(edit_id) = self.edit_ops.keys().choose(rng).copied() { + if let Some(edit_id) = self.history.ops.keys().choose(rng).copied() { ops.push(self.undo_or_redo(edit_id).unwrap()); } } diff --git a/zed/src/editor/buffer/text.rs b/zed/src/editor/buffer/text.rs index 147ffdd86ab2967738e9e731a1fa130ed10f3656..0fff5c81d18af79877e33a9f3add8a7dcf301a61 100644 --- a/zed/src/editor/buffer/text.rs +++ b/zed/src/editor/buffer/text.rs @@ -117,6 +117,18 @@ pub struct Text { 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; @@ -147,19 +159,13 @@ impl From for Text { let mut tree = SumTree::new(); tree.extend(runs); Text { - text: text.into(), + text, runs: tree, range: 0..chars_len, } } } -impl<'a> From<&'a str> for Text { - fn from(text: &'a str) -> Self { - Self::from(String::from(text)) - } -} - impl Debug for Text { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Text").field(&self.as_str()).finish() diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index bbb264df625806bda0e08be83f5d6cffa9746bcf..3072ce2904c5d7fe779c1f611027a0cf0f0f5ea7 100644 --- a/zed/src/worktree/worktree.rs +++ b/zed/src/worktree/worktree.rs @@ -345,7 +345,7 @@ impl Worktree { let mut file = smol::fs::File::open(&path).await?; let mut base_text = String::new(); file.read_to_string(&mut base_text).await?; - let history = History { base_text }; + let history = History::new(Arc::from(base_text)); tree.0.write().histories.insert(entry_id, history.clone()); Ok(history) } @@ -717,7 +717,7 @@ mod test { .await .unwrap(); - assert_eq!(history.base_text, buffer.text()); + assert_eq!(history.base_text.as_ref(), buffer.text()); }) } }