From e534fe9112a91f850bacaf0d4f433cdccc914ac8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 9 Apr 2021 14:13:25 +0200 Subject: [PATCH 01/13] Add initial support for undo/redo stack We're still not capturing selections/anchors, that's right up next. --- zed/src/editor/buffer/mod.rs | 127 ++++++++++++++++++++++++++++++++++ zed/src/editor/buffer_view.rs | 14 ++++ 2 files changed, 141 insertions(+) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 827da6be9d..7523179af3 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -28,8 +28,11 @@ use std::{ path::PathBuf, str, sync::Arc, + time::{Duration, Instant}, }; +const UNDO_GROUP_INTERVAL: Duration = Duration::from_millis(300); + pub type SelectionSetId = time::Lamport; pub type SelectionsVersion = usize; @@ -65,6 +68,7 @@ pub struct Buffer { saved_version: time::Global, last_edit: time::Local, undo_map: UndoMap, + undo_history: UndoHistory, selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, @@ -126,6 +130,67 @@ impl UndoMap { } } +#[derive(Clone)] +struct EditGroup { + edits: Vec, + last_edit_at: Instant, +} + +#[derive(Clone)] +struct UndoHistory { + group_interval: Duration, + undo_stack: Vec, + redo_stack: Vec, +} + +impl UndoHistory { + fn new(group_interval: Duration) -> Self { + Self { + group_interval, + undo_stack: Vec::new(), + redo_stack: Vec::new(), + } + } + + fn push(&mut self, edit_id: time::Local, now: Instant) { + self.redo_stack.clear(); + 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); + edit_group.last_edit_at = now; + } else { + self.undo_stack.push(EditGroup { + edits: vec![edit_id], + last_edit_at: now, + }); + } + } else { + self.undo_stack.push(EditGroup { + edits: vec![edit_id], + last_edit_at: now, + }); + } + } + + fn pop_undo(&mut self) -> Option<&EditGroup> { + if let Some(edit_group) = self.undo_stack.pop() { + self.redo_stack.push(edit_group); + self.redo_stack.last() + } else { + None + } + } + + fn pop_redo(&mut self) -> Option<&EditGroup> { + if let Some(edit_group) = self.redo_stack.pop() { + self.undo_stack.push(edit_group); + self.undo_stack.last() + } else { + None + } + } +} + #[derive(Clone)] pub struct CharIter<'a> { fragments_cursor: Cursor<'a, Fragment, usize, usize>, @@ -307,6 +372,7 @@ impl Buffer { saved_version: time::Global::new(), last_edit: time::Local::default(), undo_map: Default::default(), + undo_history: UndoHistory::new(UNDO_GROUP_INTERVAL), selections: HashMap::default(), selections_last_update: 0, deferred_ops: OperationQueue::new(), @@ -494,6 +560,21 @@ impl Buffer { new_text: T, ctx: Option<&mut ModelContext>, ) -> Result> + where + I: IntoIterator>, + S: ToOffset, + T: Into, + { + self.edit_at(old_ranges, new_text, Instant::now(), ctx) + } + + fn edit_at( + &mut self, + old_ranges: I, + new_text: T, + now: Instant, + ctx: Option<&mut ModelContext>, + ) -> Result> where I: IntoIterator>, S: ToOffset, @@ -523,6 +604,7 @@ 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); } } @@ -931,6 +1013,50 @@ impl Buffer { Ok(()) } + pub fn undo(&mut self, ctx: Option<&mut ModelContext>) -> Vec { + let was_dirty = self.is_dirty(); + let old_version = self.version.clone(); + + let mut ops = Vec::new(); + if let Some(edit_group) = self.undo_history.pop_undo() { + for edit_id in edit_group.edits.clone() { + ops.push(self.undo_or_redo(edit_id).unwrap()); + } + } + + if let Some(ctx) = ctx { + ctx.notify(); + let changes = self.edits_since(old_version).collect::>(); + if !changes.is_empty() { + self.did_edit(changes, was_dirty, ctx); + } + } + + ops + } + + pub fn redo(&mut self, ctx: Option<&mut ModelContext>) -> Vec { + let was_dirty = self.is_dirty(); + let old_version = self.version.clone(); + + let mut ops = Vec::new(); + if let Some(edit_group) = self.undo_history.pop_redo() { + for edit_id in edit_group.edits.clone() { + ops.push(self.undo_or_redo(edit_id).unwrap()); + } + } + + if let Some(ctx) = ctx { + ctx.notify(); + let changes = self.edits_since(old_version).collect::>(); + if !changes.is_empty() { + self.did_edit(changes, was_dirty, ctx); + } + } + + ops + } + fn undo_or_redo(&mut self, edit_id: time::Local) -> Result { let undo = UndoOperation { id: self.local_clock.tick(), @@ -1580,6 +1706,7 @@ impl Clone for Buffer { saved_version: self.saved_version.clone(), last_edit: self.last_edit.clone(), undo_map: self.undo_map.clone(), + undo_history: self.undo_history.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 6f3d6f108e..15be98dad3 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -28,6 +28,8 @@ pub fn init(app: &mut App) { app.add_bindings(vec![ Binding::new("backspace", "buffer:backspace", Some("BufferView")), Binding::new("enter", "buffer:newline", Some("BufferView")), + Binding::new("cmd-z", "buffer:undo", Some("BufferView")), + Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")), Binding::new("up", "buffer:move_up", Some("BufferView")), Binding::new("down", "buffer:move_down", Some("BufferView")), Binding::new("left", "buffer:move_left", Some("BufferView")), @@ -52,6 +54,8 @@ pub fn init(app: &mut App) { app.add_action("buffer:insert", BufferView::insert); app.add_action("buffer:newline", BufferView::newline); app.add_action("buffer:backspace", BufferView::backspace); + app.add_action("buffer:undo", BufferView::undo); + app.add_action("buffer:redo", BufferView::redo); app.add_action("buffer:move_up", BufferView::move_up); app.add_action("buffer:move_down", BufferView::move_down); app.add_action("buffer:move_left", BufferView::move_left); @@ -435,6 +439,16 @@ impl BufferView { self.insert(&String::new(), ctx); } + pub fn undo(&mut self, _: &(), ctx: &mut ViewContext) { + self.buffer + .update(ctx, |buffer, ctx| buffer.undo(Some(ctx))); + } + + pub fn redo(&mut self, _: &(), ctx: &mut ViewContext) { + self.buffer + .update(ctx, |buffer, ctx| buffer.redo(Some(ctx))); + } + pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext) { { let app = ctx.app(); From d0b06a2a1d07092d07691e8c802ebad01732a34c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 9 Apr 2021 16:50:06 +0200 Subject: [PATCH 02/13] Don't clear redo stack when editing Co-Authored-By: Nathan Sobo --- zed/src/editor/buffer/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 7523179af3..074d4917e2 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -153,7 +153,6 @@ impl UndoHistory { } fn push(&mut self, edit_id: time::Local, now: Instant) { - self.redo_stack.clear(); 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); From 472ff1621f6186b38b0900a3bb07b7d9cbd3b12f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 9 Apr 2021 17:17:36 +0200 Subject: [PATCH 03/13] Merge `UndoHistory` and `History`, storing also operations in the latter --- zed/src/editor/buffer/mod.rs | 158 +++++++++++++++++----------------- zed/src/editor/buffer/text.rs | 20 +++-- zed/src/worktree/worktree.rs | 4 +- 3 files changed, 94 insertions(+), 88 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 074d4917e2..dd87362004 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,9 +81,72 @@ pub struct Snapshot { fragments: SumTree, } +#[derive(Clone)] +struct EditGroup { + edits: Vec, + last_edit_at: Instant, +} + #[derive(Clone)] pub struct History { - pub base_text: String, + pub base_text: Arc, + ops: HashMap, + undo_stack: Vec, + redo_stack: Vec, + group_interval: Duration, +} + +impl History { + pub fn new(base_text: Arc) -> Self { + Self { + base_text, + ops: Default::default(), + undo_stack: Vec::new(), + redo_stack: Vec::new(), + group_interval: UNDO_GROUP_INTERVAL, + } + } + + 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); + edit_group.last_edit_at = now; + } else { + self.undo_stack.push(EditGroup { + edits: vec![edit_id], + last_edit_at: now, + }); + } + } else { + self.undo_stack.push(EditGroup { + edits: vec![edit_id], + last_edit_at: now, + }); + } + } + + fn pop_undo(&mut self) -> Option<&EditGroup> { + if let Some(edit_group) = self.undo_stack.pop() { + self.redo_stack.push(edit_group); + self.redo_stack.last() + } else { + None + } + } + + fn pop_redo(&mut self) -> Option<&EditGroup> { + if let Some(edit_group) = self.redo_stack.pop() { + self.undo_stack.push(edit_group); + self.undo_stack.last() + } else { + None + } + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -130,66 +192,6 @@ impl UndoMap { } } -#[derive(Clone)] -struct EditGroup { - edits: Vec, - last_edit_at: Instant, -} - -#[derive(Clone)] -struct UndoHistory { - group_interval: Duration, - undo_stack: Vec, - redo_stack: Vec, -} - -impl UndoHistory { - fn new(group_interval: Duration) -> Self { - Self { - group_interval, - undo_stack: Vec::new(), - redo_stack: Vec::new(), - } - } - - fn push(&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); - edit_group.last_edit_at = now; - } else { - self.undo_stack.push(EditGroup { - edits: vec![edit_id], - last_edit_at: now, - }); - } - } else { - self.undo_stack.push(EditGroup { - edits: vec![edit_id], - last_edit_at: now, - }); - } - } - - fn pop_undo(&mut self) -> Option<&EditGroup> { - if let Some(edit_group) = self.undo_stack.pop() { - self.redo_stack.push(edit_group); - self.redo_stack.last() - } else { - None - } - } - - fn pop_redo(&mut self) -> Option<&EditGroup> { - if let Some(edit_group) = self.redo_stack.pop() { - self.undo_stack.push(edit_group); - self.undo_stack.last() - } else { - None - } - } -} - #[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 147ffdd86a..0fff5c81d1 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 bbb264df62..3072ce2904 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()); }) } } From 551c86f87a218c40ef7e80b9aa0d923cf5637020 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 9 Apr 2021 18:45:13 +0200 Subject: [PATCH 04/13] WIP: Start on pushing selection management down into `Buffer` --- zed/src/editor/buffer/mod.rs | 205 ++++++--------- zed/src/editor/buffer/selection.rs | 75 ++++++ zed/src/editor/buffer_view.rs | 403 +++++++++++++---------------- 3 files changed, 336 insertions(+), 347 deletions(-) create mode 100644 zed/src/editor/buffer/selection.rs diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index dd87362004..f35bf41400 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1,11 +1,13 @@ mod anchor; mod point; +mod selection; mod text; pub use anchor::*; use futures_core::future::LocalBoxFuture; pub use point::*; use seahash::SeaHasher; +pub use selection::*; pub use text::*; use crate::{ @@ -23,7 +25,6 @@ use std::{ cmp::{self, Ordering}, hash::BuildHasher, iter::{self, Iterator}, - mem, ops::{AddAssign, Range}, path::PathBuf, str, @@ -33,9 +34,6 @@ use std::{ const UNDO_GROUP_INTERVAL: Duration = Duration::from_millis(300); -pub type SelectionSetId = time::Lamport; -pub type SelectionsVersion = usize; - #[derive(Clone, Default)] struct DeterministicState; @@ -149,13 +147,6 @@ impl History { } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Selection { - pub start: Anchor, - pub end: Anchor, - pub reversed: bool, -} - #[derive(Clone, Default, Debug)] struct UndoMap(HashMap>); @@ -679,45 +670,46 @@ impl Buffer { (old_ranges, new_text, operations) } - pub fn add_selection_set(&mut self, ranges: I) -> Result<(SelectionSetId, Operation)> - where - I: IntoIterator>, - { - let selections = self.selections_from_ranges(ranges)?; + pub fn add_selection_set( + &mut self, + selections: Vec, + ctx: Option<&mut ModelContext>, + ) -> (SelectionSetId, Operation) { let lamport_timestamp = self.lamport_clock.tick(); self.selections .insert(lamport_timestamp, selections.clone()); self.selections_last_update += 1; - Ok(( + if let Some(ctx) = ctx { + ctx.notify(); + } + + ( lamport_timestamp, Operation::UpdateSelections { set_id: lamport_timestamp, selections: Some(selections), lamport_timestamp, }, - )) + ) } - pub fn replace_selection_set( + pub fn update_selection_set( &mut self, set_id: SelectionSetId, - ranges: I, - ) -> Result - where - I: IntoIterator>, - { - self.selections - .remove(&set_id) - .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?; - - let mut selections = self.selections_from_ranges(ranges)?; + mut selections: Vec, + ctx: Option<&mut ModelContext>, + ) -> Result { self.merge_selections(&mut selections); self.selections.insert(set_id, selections.clone()); let lamport_timestamp = self.lamport_clock.tick(); self.selections_last_update += 1; + if let Some(ctx) = ctx { + ctx.notify(); + } + Ok(Operation::UpdateSelections { set_id, selections: Some(selections), @@ -725,12 +717,21 @@ impl Buffer { }) } - pub fn remove_selection_set(&mut self, set_id: SelectionSetId) -> Result { + pub fn remove_selection_set( + &mut self, + set_id: SelectionSetId, + ctx: Option<&mut ModelContext>, + ) -> Result { self.selections .remove(&set_id) .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?; let lamport_timestamp = self.lamport_clock.tick(); self.selections_last_update += 1; + + if let Some(ctx) = ctx { + ctx.notify(); + } + Ok(Operation::UpdateSelections { set_id, selections: None, @@ -738,15 +739,18 @@ impl Buffer { }) } + pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> { + self.selections + .get(&set_id) + .map(|s| s.as_slice()) + .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) + } + pub fn selection_ranges<'a>( &'a self, set_id: SelectionSetId, ) -> Result> + 'a> { - let selections = self - .selections - .get(&set_id) - .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?; - Ok(selections.iter().map(move |selection| { + Ok(self.selections(set_id)?.iter().map(move |selection| { let start = selection.start.to_point(self).unwrap(); let end = selection.end.to_point(self).unwrap(); if selection.reversed { @@ -770,49 +774,25 @@ impl Buffer { } fn merge_selections(&mut self, selections: &mut Vec) { - let mut new_selections = Vec::with_capacity(selections.len()); - { - let mut old_selections = selections.drain(..); - if let Some(mut prev_selection) = old_selections.next() { - for selection in old_selections { - if prev_selection.end.cmp(&selection.start, self).unwrap() >= Ordering::Equal { - if selection.end.cmp(&prev_selection.end, self).unwrap() > Ordering::Equal { - prev_selection.end = selection.end; - } - } else { - new_selections.push(mem::replace(&mut prev_selection, selection)); - } + let mut i = 1; + while i < selections.len() { + if selections[i - 1] + .end + .cmp(&selections[i].start, self) + .unwrap() + >= Ordering::Equal + { + let removed = selections.remove(i); + if removed.start.cmp(&selections[i - 1].start, self).unwrap() < Ordering::Equal { + selections[i - 1].start = removed.start; + } + if removed.end.cmp(&selections[i - 1].end, self).unwrap() > Ordering::Equal { + selections[i - 1].end = removed.end; } - new_selections.push(prev_selection); - } - } - *selections = new_selections; - } - - fn selections_from_ranges(&self, ranges: I) -> Result> - where - I: IntoIterator>, - { - let mut ranges = ranges.into_iter().collect::>(); - ranges.sort_unstable_by_key(|range| range.start); - - let mut selections = Vec::with_capacity(ranges.len()); - for range in ranges { - if range.start > range.end { - selections.push(Selection { - start: self.anchor_before(range.end)?, - end: self.anchor_before(range.start)?, - reversed: true, - }); } else { - selections.push(Selection { - start: self.anchor_after(range.start)?, - end: self.anchor_before(range.end)?, - reversed: false, - }); + i += 1; } } - Ok(selections) } pub fn apply_ops>( @@ -1927,48 +1907,6 @@ impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> { // collector.into_inner().changes // } -impl Selection { - pub fn head(&self) -> &Anchor { - if self.reversed { - &self.start - } else { - &self.end - } - } - - pub fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) { - if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal { - if !self.reversed { - mem::swap(&mut self.start, &mut self.end); - self.reversed = true; - } - self.start = cursor; - } else { - if self.reversed { - mem::swap(&mut self.start, &mut self.end); - self.reversed = false; - } - self.end = cursor; - } - } - - pub fn tail(&self) -> &Anchor { - if self.reversed { - &self.end - } else { - &self.start - } - } - - pub fn is_empty(&self, buffer: &Buffer) -> bool { - self.start.to_offset(buffer).unwrap() == self.end.to_offset(buffer).unwrap() - } - - pub fn anchor_range(&self) -> Range { - self.start.clone()..self.end.clone() - } -} - #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)] struct FragmentId(Arc<[u16]>); @@ -3049,7 +2987,7 @@ mod tests { .collect::>(); let set_id = replica_selection_sets.choose(rng); if set_id.is_some() && rng.gen_bool(1.0 / 6.0) { - let op = self.remove_selection_set(*set_id.unwrap()).unwrap(); + let op = self.remove_selection_set(*set_id.unwrap(), None).unwrap(); operations.push(op); } else { let mut ranges = Vec::new(); @@ -3060,11 +2998,12 @@ mod tests { let end_point = self.point_for_offset(end).unwrap(); ranges.push(start_point..end_point); } + let new_selections = self.selections_from_ranges(ranges).unwrap(); let op = if set_id.is_none() || rng.gen_bool(1.0 / 5.0) { - self.add_selection_set(ranges).unwrap().1 + self.add_selection_set(new_selections, None).1 } else { - self.replace_selection_set(*set_id.unwrap(), ranges) + self.update_selection_set(*set_id.unwrap(), new_selections, None) .unwrap() }; operations.push(op); @@ -3082,6 +3021,34 @@ mod tests { } ops } + + fn selections_from_ranges(&self, ranges: I) -> Result> + where + I: IntoIterator>, + { + let mut ranges = ranges.into_iter().collect::>(); + ranges.sort_unstable_by_key(|range| range.start); + + let mut selections = Vec::with_capacity(ranges.len()); + for range in ranges { + if range.start > range.end { + selections.push(Selection { + start: self.anchor_before(range.end)?, + end: self.anchor_before(range.start)?, + reversed: true, + goal_column: None, + }); + } else { + selections.push(Selection { + start: self.anchor_after(range.start)?, + end: self.anchor_before(range.end)?, + reversed: false, + goal_column: None, + }); + } + } + Ok(selections) + } } impl Operation { diff --git a/zed/src/editor/buffer/selection.rs b/zed/src/editor/buffer/selection.rs new file mode 100644 index 0000000000..99c71e0a44 --- /dev/null +++ b/zed/src/editor/buffer/selection.rs @@ -0,0 +1,75 @@ +use crate::{ + editor::{ + buffer::{Anchor, Buffer, Point, ToPoint}, + display_map::DisplayMap, + DisplayPoint, + }, + time, +}; +use gpui::AppContext; +use std::{cmp::Ordering, mem, ops::Range}; + +pub type SelectionSetId = time::Lamport; +pub type SelectionsVersion = usize; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Selection { + pub start: Anchor, + pub end: Anchor, + pub reversed: bool, + pub goal_column: Option, +} + +impl Selection { + pub fn head(&self) -> &Anchor { + if self.reversed { + &self.start + } else { + &self.end + } + } + + pub fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) { + if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal { + if !self.reversed { + mem::swap(&mut self.start, &mut self.end); + self.reversed = true; + } + self.start = cursor; + } else { + if self.reversed { + mem::swap(&mut self.start, &mut self.end); + self.reversed = false; + } + self.end = cursor; + } + } + + pub fn tail(&self) -> &Anchor { + if self.reversed { + &self.end + } else { + &self.start + } + } + + pub fn range(&self, buffer: &Buffer) -> Range { + let start = self.start.to_point(buffer).unwrap(); + let end = self.end.to_point(buffer).unwrap(); + if self.reversed { + end..start + } else { + start..end + } + } + + pub fn display_range(&self, map: &DisplayMap, app: &AppContext) -> Range { + let start = self.start.to_display_point(map, app).unwrap(); + let end = self.end.to_display_point(map, app).unwrap(); + if self.reversed { + end..start + } else { + start..end + } + } +} diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 15be98dad3..ebcd3a93d4 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1,6 +1,6 @@ use super::{ buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point, - ToOffset, ToPoint, + Selection, SelectionSetId, ToOffset, }; use crate::{settings::Settings, watch, workspace}; use anyhow::Result; @@ -16,7 +16,6 @@ use smol::Timer; use std::{ cmp::{self, Ordering}, fmt::Write, - mem, ops::Range, sync::Arc, time::Duration, @@ -90,7 +89,7 @@ pub struct BufferView { handle: WeakViewHandle, buffer: ModelHandle, display_map: ModelHandle, - selections: Vec, + selection_set_id: SelectionSetId, pending_selection: Option, scroll_position: Mutex, autoscroll_requested: Mutex, @@ -128,17 +127,22 @@ impl BufferView { }); ctx.observe(&display_map, Self::on_display_map_changed); - let buffer_ref = buffer.as_ref(ctx); + let (selection_set_id, ops) = buffer.update(ctx, |buffer, ctx| { + buffer.add_selection_set( + vec![Selection { + start: buffer.anchor_before(0).unwrap(), + end: buffer.anchor_before(0).unwrap(), + reversed: false, + goal_column: None, + }], + Some(ctx), + ) + }); Self { handle: ctx.handle().downgrade(), buffer, display_map, - selections: vec![Selection { - start: buffer_ref.anchor_before(0).unwrap(), - end: buffer_ref.anchor_before(0).unwrap(), - reversed: false, - goal_column: None, - }], + selection_set_id, pending_selection: None, scroll_position: Mutex::new(Vector2F::zero()), autoscroll_requested: Mutex::new(false), @@ -194,7 +198,7 @@ impl BufferView { let map = self.display_map.as_ref(app); let visible_lines = viewport_height / line_height; let first_cursor_top = self - .selections + .selections(app) .first() .unwrap() .head() @@ -202,7 +206,7 @@ impl BufferView { .unwrap() .row() as f32; let last_cursor_bottom = self - .selections + .selections(app) .last() .unwrap() .head() @@ -245,7 +249,7 @@ impl BufferView { let mut target_left = std::f32::INFINITY; let mut target_right = 0.0_f32; - for selection in &self.selections { + for selection in self.selections(app) { let head = selection.head().to_display_point(map, app).unwrap(); let start_column = head.column().saturating_sub(3); let end_column = cmp::min(map.line_len(head.row(), app).unwrap(), head.column() + 3); @@ -302,7 +306,7 @@ impl BufferView { }; if !add { - self.selections.clear(); + self.update_selections(Vec::new(), ctx); } self.pending_selection = Some(selection); @@ -333,9 +337,9 @@ impl BufferView { fn end_selection(&mut self, ctx: &mut ViewContext) { if let Some(selection) = self.pending_selection.take() { let ix = self.selection_insertion_index(&selection.start, ctx.app()); - self.selections.insert(ix, selection); - self.merge_selections(ctx.app()); - ctx.notify(); + let mut selections = self.selections(ctx.app()).to_vec(); + selections.insert(ix, selection); + self.update_selections(selections, ctx); } else { log::error!("end_selection dispatched with no pending selection"); } @@ -350,7 +354,6 @@ impl BufferView { where T: IntoIterator>, { - let buffer = self.buffer.as_ref(ctx); let map = self.display_map.as_ref(ctx); let mut selections = Vec::new(); for range in ranges { @@ -361,51 +364,50 @@ impl BufferView { goal_column: None, }); } - selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer).unwrap()); - self.selections = selections; - self.merge_selections(ctx.app()); - ctx.notify(); + self.update_selections(selections, ctx); Ok(()) } fn insert(&mut self, text: &String, ctx: &mut ViewContext) { - let buffer = self.buffer.as_ref(ctx); let mut offset_ranges = SmallVec::<[Range; 32]>::new(); - for selection in &self.selections { - let start = selection.start.to_offset(buffer).unwrap(); - let end = selection.end.to_offset(buffer).unwrap(); - offset_ranges.push(start..end); + { + let buffer = self.buffer.as_ref(ctx); + for selection in self.selections(ctx.app()) { + let start = selection.start.to_offset(buffer).unwrap(); + let end = selection.end.to_offset(buffer).unwrap(); + offset_ranges.push(start..end); + } } + let mut new_selections = Vec::new(); self.buffer.update(ctx, |buffer, ctx| { if let Err(error) = buffer.edit(offset_ranges.iter().cloned(), text.as_str(), Some(ctx)) { log::error!("error inserting text: {}", error); }; + let char_count = text.chars().count() as isize; + let mut delta = 0_isize; + new_selections = offset_ranges + .into_iter() + .map(|range| { + let start = range.start as isize; + let end = range.end as isize; + let anchor = buffer + .anchor_before((start + delta + char_count) as usize) + .unwrap(); + let deleted_count = end - start; + delta += char_count - deleted_count; + Selection { + start: anchor.clone(), + end: anchor, + reversed: false, + goal_column: None, + } + }) + .collect(); }); - let buffer = self.buffer.as_ref(ctx); - let char_count = text.chars().count() as isize; - let mut delta = 0_isize; - self.selections = offset_ranges - .into_iter() - .map(|range| { - let start = range.start as isize; - let end = range.end as isize; - let anchor = buffer - .anchor_before((start + delta + char_count) as usize) - .unwrap(); - let deleted_count = end - start; - delta += char_count - deleted_count; - Selection { - start: anchor.clone(), - end: anchor, - reversed: false, - goal_column: None, - } - }) - .collect(); - + self.update_selections(new_selections, ctx); self.pause_cursor_blinking(ctx); *self.autoscroll_requested.lock() = true; } @@ -419,22 +421,27 @@ impl BufferView { } pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext) { - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); - for selection in &mut self.selections { - if selection.range(buffer).is_empty() { - let head = selection.head().to_display_point(map, ctx.app()).unwrap(); - let cursor = map - .anchor_before( - movement::left(map, head, ctx.app()).unwrap(), - Bias::Left, - ctx.app(), - ) - .unwrap(); - selection.set_head(&buffer, cursor); - selection.goal_column = None; + let mut selections = self.selections(ctx.app()).to_vec(); + { + let buffer = self.buffer.as_ref(ctx); + let map = self.display_map.as_ref(ctx); + for selection in &mut selections { + if selection.range(buffer).is_empty() { + let head = selection.head().to_display_point(map, ctx.app()).unwrap(); + let cursor = map + .anchor_before( + movement::left(map, head, ctx.app()).unwrap(), + Bias::Left, + ctx.app(), + ) + .unwrap(); + selection.set_head(&buffer, cursor); + selection.goal_column = None; + } } } + + self.update_selections(selections, ctx); self.changed_selections(ctx); self.insert(&String::new(), ctx); } @@ -450,10 +457,11 @@ impl BufferView { } pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext) { + let mut selections = self.selections(ctx.app()).to_vec(); { let app = ctx.app(); - let map = self.display_map.as_ref(ctx); - for selection in &mut self.selections { + let map = self.display_map.as_ref(app); + for selection in &mut selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -470,14 +478,16 @@ impl BufferView { selection.goal_column = None; } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } pub fn select_left(&mut self, _: &(), ctx: &mut ViewContext) { + let mut selections = self.selections(ctx.app()).to_vec(); { let buffer = self.buffer.as_ref(ctx); let map = self.display_map.as_ref(ctx); - for selection in &mut self.selections { + for selection in &mut selections { let head = selection.head().to_display_point(map, ctx.app()).unwrap(); let cursor = map .anchor_before( @@ -490,14 +500,16 @@ impl BufferView { selection.goal_column = None; } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } pub fn move_right(&mut self, _: &(), ctx: &mut ViewContext) { + let mut selections = self.selections(ctx.app()).to_vec(); { let app = ctx.app(); let map = self.display_map.as_ref(app); - for selection in &mut self.selections { + for selection in &mut selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -514,15 +526,17 @@ impl BufferView { selection.goal_column = None; } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } pub fn select_right(&mut self, _: &(), ctx: &mut ViewContext) { + let mut selections = self.selections(ctx.app()).to_vec(); { - let buffer = self.buffer.as_ref(ctx); let app = ctx.app(); + let buffer = self.buffer.as_ref(app); let map = self.display_map.as_ref(app); - for selection in &mut self.selections { + for selection in &mut selections { let head = selection.head().to_display_point(map, ctx.app()).unwrap(); let cursor = map .anchor_before(movement::right(map, head, app).unwrap(), Bias::Right, app) @@ -531,6 +545,7 @@ impl BufferView { selection.goal_column = None; } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } @@ -538,23 +553,27 @@ impl BufferView { if self.single_line { ctx.propagate_action(); } else { - let app = ctx.app(); - let map = self.display_map.as_ref(app); - for selection in &mut self.selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); - if start != end { - selection.goal_column = None; - } + let mut selections = self.selections(ctx.app()).to_vec(); + { + let app = ctx.app(); + let map = self.display_map.as_ref(app); + for selection in &mut selections { + let start = selection.start.to_display_point(map, app).unwrap(); + let end = selection.end.to_display_point(map, app).unwrap(); + if start != end { + selection.goal_column = None; + } - let (start, goal_column) = - movement::up(map, start, selection.goal_column, app).unwrap(); - let cursor = map.anchor_before(start, Bias::Left, app).unwrap(); - selection.start = cursor.clone(); - selection.end = cursor; - selection.goal_column = goal_column; - selection.reversed = false; + let (start, goal_column) = + movement::up(map, start, selection.goal_column, app).unwrap(); + let cursor = map.anchor_before(start, Bias::Left, app).unwrap(); + selection.start = cursor.clone(); + selection.end = cursor; + selection.goal_column = goal_column; + selection.reversed = false; + } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } } @@ -563,16 +582,20 @@ impl BufferView { if self.single_line { ctx.propagate_action(); } else { - let app = ctx.app(); - let buffer = self.buffer.as_ref(app); - let map = self.display_map.as_ref(app); - for selection in &mut self.selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let (head, goal_column) = - movement::up(map, head, selection.goal_column, app).unwrap(); - selection.set_head(&buffer, map.anchor_before(head, Bias::Left, app).unwrap()); - selection.goal_column = goal_column; + let mut selections = self.selections(ctx.app()).to_vec(); + { + let app = ctx.app(); + let buffer = self.buffer.as_ref(app); + let map = self.display_map.as_ref(app); + for selection in &mut selections { + let head = selection.head().to_display_point(map, app).unwrap(); + let (head, goal_column) = + movement::up(map, head, selection.goal_column, app).unwrap(); + selection.set_head(&buffer, map.anchor_before(head, Bias::Left, app).unwrap()); + selection.goal_column = goal_column; + } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } } @@ -581,23 +604,27 @@ impl BufferView { if self.single_line { ctx.propagate_action(); } else { - let app = ctx.app(); - let map = self.display_map.as_ref(app); - for selection in &mut self.selections { - let start = selection.start.to_display_point(map, app).unwrap(); - let end = selection.end.to_display_point(map, app).unwrap(); - if start != end { - selection.goal_column = None; - } + let mut selections = self.selections(ctx.app()).to_vec(); + { + let app = ctx.app(); + let map = self.display_map.as_ref(app); + for selection in &mut selections { + let start = selection.start.to_display_point(map, app).unwrap(); + let end = selection.end.to_display_point(map, app).unwrap(); + if start != end { + selection.goal_column = None; + } - let (start, goal_column) = - movement::down(map, end, selection.goal_column, app).unwrap(); - let cursor = map.anchor_before(start, Bias::Right, app).unwrap(); - selection.start = cursor.clone(); - selection.end = cursor; - selection.goal_column = goal_column; - selection.reversed = false; + let (start, goal_column) = + movement::down(map, end, selection.goal_column, app).unwrap(); + let cursor = map.anchor_before(start, Bias::Right, app).unwrap(); + selection.start = cursor.clone(); + selection.end = cursor; + selection.goal_column = goal_column; + selection.reversed = false; + } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } } @@ -606,69 +633,39 @@ impl BufferView { if self.single_line { ctx.propagate_action(); } else { - let app = ctx.app(); - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); - for selection in &mut self.selections { - let head = selection.head().to_display_point(map, app).unwrap(); - let (head, goal_column) = - movement::down(map, head, selection.goal_column, app).unwrap(); - selection.set_head(&buffer, map.anchor_before(head, Bias::Right, app).unwrap()); - selection.goal_column = goal_column; + let mut selections = self.selections(ctx.app()).to_vec(); + { + let app = ctx.app(); + let buffer = self.buffer.as_ref(app); + let map = self.display_map.as_ref(app); + for selection in &mut selections { + let head = selection.head().to_display_point(map, app).unwrap(); + let (head, goal_column) = + movement::down(map, head, selection.goal_column, app).unwrap(); + selection.set_head(&buffer, map.anchor_before(head, Bias::Right, app).unwrap()); + selection.goal_column = goal_column; + } } + self.update_selections(selections, ctx); self.changed_selections(ctx); } } pub fn changed_selections(&mut self, ctx: &mut ViewContext) { - self.merge_selections(ctx.app()); self.pause_cursor_blinking(ctx); *self.autoscroll_requested.lock() = true; ctx.notify(); } - fn merge_selections(&mut self, ctx: &AppContext) { - let buffer = self.buffer.as_ref(ctx); - let mut i = 1; - while i < self.selections.len() { - if self.selections[i - 1] - .end - .cmp(&self.selections[i].start, buffer) - .unwrap() - >= Ordering::Equal - { - let removed = self.selections.remove(i); - if removed - .start - .cmp(&self.selections[i - 1].start, buffer) - .unwrap() - < Ordering::Equal - { - self.selections[i - 1].start = removed.start; - } - if removed - .end - .cmp(&self.selections[i - 1].end, buffer) - .unwrap() - > Ordering::Equal - { - self.selections[i - 1].end = removed.end; - } - } else { - i += 1; - } - } - } - pub fn first_selection(&self, app: &AppContext) -> Range { - self.selections + self.selections(app) .first() .unwrap() .display_range(self.display_map.as_ref(app), app) } pub fn last_selection(&self, app: &AppContext) -> Range { - self.selections + self.selections(app) .last() .unwrap() .display_range(self.display_map.as_ref(app), app) @@ -691,7 +688,7 @@ impl BufferView { None } }); - self.selections[start_index..] + self.selections(app)[start_index..] .iter() .map(move |s| s.display_range(map, app)) .take_while(move |r| r.start <= range.end || r.end <= range.end) @@ -700,16 +697,12 @@ impl BufferView { fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize { let buffer = self.buffer.as_ref(app); - - match self - .selections - .binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap()) - { + let selections = self.selections(app); + match selections.binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap()) { Ok(index) => index, Err(index) => { if index > 0 - && self.selections[index - 1].end.cmp(&start, buffer).unwrap() - == Ordering::Greater + && selections[index - 1].end.cmp(&start, buffer).unwrap() == Ordering::Greater { index - 1 } else { @@ -719,6 +712,21 @@ impl BufferView { } } + fn selections<'a>(&self, app: &'a AppContext) -> &'a [Selection] { + self.buffer + .as_ref(app) + .selections(self.selection_set_id) + .unwrap() + } + + fn update_selections<'a>(&self, selections: Vec, ctx: &mut ViewContext) { + let op = self.buffer.update(ctx, |buffer, ctx| { + buffer + .update_selection_set(self.selection_set_id, selections, Some(ctx)) + .unwrap() + }); + } + pub fn page_up(&mut self, _: &(), _: &mut ViewContext) { log::info!("BufferView::page_up"); } @@ -734,7 +742,7 @@ impl BufferView { let app = ctx.app(); let map = self.display_map.as_ref(app); - for selection in &self.selections { + for selection in self.selections(app) { let (start, end) = selection.display_range(map, app).sorted(); let buffer_start_row = start.to_buffer_point(map, Bias::Left, app).unwrap().row; @@ -766,7 +774,7 @@ impl BufferView { let map = self.display_map.as_ref(app); let buffer = self.buffer.as_ref(app); let ranges = self - .selections + .selections(app) .iter() .map(|s| { let (start, end) = s.display_range(map, app).sorted(); @@ -846,7 +854,7 @@ impl BufferView { self.display_map.update(ctx, |map, ctx| { let buffer = self.buffer.as_ref(ctx); let ranges = self - .selections + .selections(ctx.app()) .iter() .map(|s| s.range(buffer)) .collect::>(); @@ -1099,13 +1107,6 @@ impl BufferView { } } -struct Selection { - start: Anchor, - end: Anchor, - reversed: bool, - goal_column: Option, -} - pub enum Event { Activate, Edited, @@ -1194,60 +1195,6 @@ impl workspace::ItemView for BufferView { } } -impl Selection { - fn head(&self) -> &Anchor { - if self.reversed { - &self.start - } else { - &self.end - } - } - - fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) { - if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal { - if !self.reversed { - mem::swap(&mut self.start, &mut self.end); - self.reversed = true; - } - self.start = cursor; - } else { - if self.reversed { - mem::swap(&mut self.start, &mut self.end); - self.reversed = false; - } - self.end = cursor; - } - } - - fn tail(&self) -> &Anchor { - if self.reversed { - &self.end - } else { - &self.start - } - } - - fn range(&self, buffer: &Buffer) -> Range { - let start = self.start.to_point(buffer).unwrap(); - let end = self.end.to_point(buffer).unwrap(); - if self.reversed { - end..start - } else { - start..end - } - } - - fn display_range(&self, map: &DisplayMap, app: &AppContext) -> Range { - let start = self.start.to_display_point(map, app).unwrap(); - let end = self.end.to_display_point(map, app).unwrap(); - if self.reversed { - end..start - } else { - start..end - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -1492,12 +1439,12 @@ mod tests { view.update(&mut app, |view, ctx| { view.move_down(&(), ctx); assert_eq!( - view.selections(ctx.app()), + view.selection_ranges(ctx.app()), &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); view.move_right(&(), ctx); assert_eq!( - view.selections(ctx.app()), + view.selection_ranges(ctx.app()), &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] ); Ok::<(), Error>(()) @@ -1543,7 +1490,7 @@ mod tests { } impl BufferView { - fn selections(&self, app: &AppContext) -> Vec> { + fn selection_ranges(&self, app: &AppContext) -> Vec> { self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app) .collect::>() } From 40bfdd38aed52c9e2da8e05028b5e3063b77bba3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 10 Apr 2021 13:03:45 +0200 Subject: [PATCH 05/13] :lipstick: --- zed/src/editor/buffer_view.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index ebcd3a93d4..1a6112ef1d 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -127,7 +127,7 @@ impl BufferView { }); ctx.observe(&display_map, Self::on_display_map_changed); - let (selection_set_id, ops) = buffer.update(ctx, |buffer, ctx| { + let (selection_set_id, op) = buffer.update(ctx, |buffer, ctx| { buffer.add_selection_set( vec![Selection { start: buffer.anchor_before(0).unwrap(), @@ -719,7 +719,7 @@ impl BufferView { .unwrap() } - fn update_selections<'a>(&self, selections: Vec, ctx: &mut ViewContext) { + fn update_selections(&self, selections: Vec, ctx: &mut ViewContext) { let op = self.buffer.update(ctx, |buffer, ctx| { buffer .update_selection_set(self.selection_set_id, selections, Some(ctx)) From ab14b99a7357474d724be08108b45a8709a053c4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 15:15:27 +0200 Subject: [PATCH 06/13] Introduce transactional edits and allow snapshotting of selections --- zed/src/editor/buffer/mod.rs | 225 +++++++++++++++++++++++++++------- zed/src/editor/buffer_view.rs | 22 +++- 2 files changed, 203 insertions(+), 44 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index f35bf41400..da37ac1835 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -66,7 +66,8 @@ pub struct Buffer { last_edit: time::Local, undo_map: UndoMap, history: History, - selections: HashMap>, + pending_transaction: Option, + selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, deferred_replicas: HashSet, @@ -80,8 +81,22 @@ pub struct Snapshot { } #[derive(Clone)] -struct EditGroup { +struct SelectionSetSnapshot { + set_id: SelectionSetId, + before_transaction: Arc<[Selection]>, + after_transaction: Arc<[Selection]>, +} + +#[derive(Clone)] +struct PendingTransaction { + start: time::Global, + buffer_was_dirty: bool, +} + +#[derive(Clone)] +struct Transaction { edits: Vec, + selections: Option, last_edit_at: Instant, } @@ -89,8 +104,9 @@ struct EditGroup { pub struct History { pub base_text: Arc, ops: HashMap, - undo_stack: Vec, - redo_stack: Vec, + undo_stack: Vec, + redo_stack: Vec, + transaction_depth: usize, group_interval: Duration, } @@ -101,6 +117,7 @@ impl History { ops: Default::default(), undo_stack: Vec::new(), redo_stack: Vec::new(), + transaction_depth: 0, group_interval: UNDO_GROUP_INTERVAL, } } @@ -109,37 +126,85 @@ impl History { 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); - edit_group.last_edit_at = now; + fn start_transaction( + &mut self, + selections: Option<(SelectionSetId, Arc<[Selection]>)>, + now: Instant, + ) -> bool { + self.transaction_depth += 1; + if self.transaction_depth == 1 { + if let Some(transaction) = self.undo_stack.last_mut() { + if now - transaction.last_edit_at <= self.group_interval { + transaction.last_edit_at = now; + } else { + self.undo_stack.push(Transaction { + edits: Vec::new(), + selections: selections.map(|(set_id, selections)| SelectionSetSnapshot { + set_id, + before_transaction: selections.clone(), + after_transaction: selections, + }), + last_edit_at: now, + }); + } } else { - self.undo_stack.push(EditGroup { - edits: vec![edit_id], + self.undo_stack.push(Transaction { + edits: Vec::new(), + selections: selections.map(|(set_id, selections)| SelectionSetSnapshot { + set_id, + before_transaction: selections.clone(), + after_transaction: selections, + }), last_edit_at: now, }); } + true } else { - self.undo_stack.push(EditGroup { - edits: vec![edit_id], - last_edit_at: now, - }); + false } } - fn pop_undo(&mut self) -> Option<&EditGroup> { - if let Some(edit_group) = self.undo_stack.pop() { - self.redo_stack.push(edit_group); + fn end_transaction( + &mut self, + selections: Option<(SelectionSetId, Arc<[Selection]>)>, + now: Instant, + ) -> bool { + assert_ne!(self.transaction_depth, 0); + self.transaction_depth -= 1; + if self.transaction_depth == 0 { + let transaction = self.undo_stack.last_mut().unwrap(); + if let Some((set_id, selections)) = selections { + if let Some(transaction_selections) = &mut transaction.selections { + assert_eq!(set_id, transaction_selections.set_id); + transaction_selections.after_transaction = selections; + } + } + transaction.last_edit_at = now; + true + } else { + false + } + } + + fn push_undo(&mut self, edit_id: time::Local) { + assert_ne!(self.transaction_depth, 0); + self.undo_stack.last_mut().unwrap().edits.push(edit_id); + } + + fn pop_undo(&mut self) -> Option<&Transaction> { + assert_eq!(self.transaction_depth, 0); + if let Some(transaction) = self.undo_stack.pop() { + self.redo_stack.push(transaction); self.redo_stack.last() } else { None } } - fn pop_redo(&mut self) -> Option<&EditGroup> { - if let Some(edit_group) = self.redo_stack.pop() { - self.undo_stack.push(edit_group); + fn pop_redo(&mut self) -> Option<&Transaction> { + assert_eq!(self.transaction_depth, 0); + if let Some(transaction) = self.redo_stack.pop() { + self.undo_stack.push(transaction); self.undo_stack.last() } else { None @@ -274,7 +339,7 @@ pub enum Operation { }, UpdateSelections { set_id: SelectionSetId, - selections: Option>, + selections: Option>, lamport_timestamp: time::Lamport, }, } @@ -364,6 +429,7 @@ impl Buffer { last_edit: time::Local::default(), undo_map: Default::default(), history, + pending_transaction: None, selections: HashMap::default(), selections_last_update: 0, deferred_ops: OperationQueue::new(), @@ -545,6 +611,68 @@ impl Buffer { self.deferred_ops.len() } + pub fn start_transaction(&mut self, set_id: Option) -> Result<()> { + self.start_transaction_at(set_id, Instant::now()) + } + + fn start_transaction_at(&mut self, set_id: Option, now: Instant) -> Result<()> { + let selections = if let Some(set_id) = set_id { + let selections = self + .selections + .get(&set_id) + .ok_or_else(|| anyhow!("invalid selection set {:?}", set_id))?; + Some((set_id, selections.clone())) + } else { + None + }; + if self.history.start_transaction(selections, now) { + self.pending_transaction = Some(PendingTransaction { + start: self.version.clone(), + buffer_was_dirty: self.is_dirty(), + }); + } + Ok(()) + } + + pub fn end_transaction( + &mut self, + set_id: Option, + ctx: Option<&mut ModelContext>, + ) -> Result<()> { + self.end_transaction_at(set_id, Instant::now(), ctx) + } + + fn end_transaction_at( + &mut self, + set_id: Option, + now: Instant, + ctx: Option<&mut ModelContext>, + ) -> Result<()> { + let selections = if let Some(set_id) = set_id { + let selections = self + .selections + .get(&set_id) + .ok_or_else(|| anyhow!("invalid selection set {:?}", set_id))?; + Some((set_id, selections.clone())) + } else { + None + }; + + if self.history.end_transaction(selections, now) { + if let Some(ctx) = ctx { + ctx.notify(); + + let transaction = self.pending_transaction.take().unwrap(); + let changes = self.edits_since(transaction.start).collect::>(); + if !changes.is_empty() { + self.did_edit(changes, transaction.buffer_was_dirty, ctx); + } + } + } + + Ok(()) + } + pub fn edit( &mut self, old_ranges: I, @@ -571,6 +699,8 @@ impl Buffer { S: ToOffset, T: Into, { + self.start_transaction_at(None, now)?; + let new_text = new_text.into(); let new_text = if new_text.len() > 0 { Some(new_text) @@ -578,8 +708,6 @@ impl Buffer { None }; - let was_dirty = self.is_dirty(); - let old_version = self.version.clone(); let old_ranges = old_ranges .into_iter() .map(|range| Ok(range.start.to_offset(self)?..range.end.to_offset(self)?)) @@ -595,19 +723,11 @@ impl Buffer { for op in &ops { if let Operation::Edit { edit, .. } = op { self.history.push(edit.clone()); - self.history.push_undo(edit.id, now); + self.history.push_undo(edit.id); } } if let Some(op) = ops.last() { - if let Some(ctx) = ctx { - ctx.notify(); - let changes = self.edits_since(old_version).collect::>(); - if !changes.is_empty() { - self.did_edit(changes, was_dirty, ctx); - } - } - if let Operation::Edit { edit, .. } = op { self.last_edit = edit.id; self.version.observe(edit.id); @@ -616,6 +736,8 @@ impl Buffer { } } + self.end_transaction_at(None, now, ctx)?; + Ok(ops) } @@ -675,9 +797,10 @@ impl Buffer { selections: Vec, ctx: Option<&mut ModelContext>, ) -> (SelectionSetId, Operation) { + let selections = Arc::from(selections); let lamport_timestamp = self.lamport_clock.tick(); self.selections - .insert(lamport_timestamp, selections.clone()); + .insert(lamport_timestamp, Arc::clone(&selections)); self.selections_last_update += 1; if let Some(ctx) = ctx { @@ -701,7 +824,8 @@ impl Buffer { ctx: Option<&mut ModelContext>, ) -> Result { self.merge_selections(&mut selections); - self.selections.insert(set_id, selections.clone()); + let selections = Arc::from(selections); + self.selections.insert(set_id, Arc::clone(&selections)); let lamport_timestamp = self.lamport_clock.tick(); self.selections_last_update += 1; @@ -742,7 +866,7 @@ impl Buffer { pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> { self.selections .get(&set_id) - .map(|s| s.as_slice()) + .map(|s| s.as_ref()) .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } @@ -761,8 +885,10 @@ impl Buffer { })) } - pub fn all_selections(&self) -> impl Iterator)> { - self.selections.iter() + pub fn all_selections(&self) -> impl Iterator { + self.selections + .iter() + .map(|(set_id, selections)| (set_id, selections.as_ref())) } pub fn all_selection_ranges<'a>( @@ -998,10 +1124,17 @@ impl Buffer { let old_version = self.version.clone(); let mut ops = Vec::new(); - if let Some(edit_group) = self.history.pop_undo() { - for edit_id in edit_group.edits.clone() { + if let Some(transaction) = self.history.pop_undo() { + let transaction_selections = transaction.selections.clone(); + for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } + + if let Some(transaction_selections) = transaction_selections { + if let Some(selections) = self.selections.get_mut(&transaction_selections.set_id) { + *selections = transaction_selections.before_transaction; + } + } } if let Some(ctx) = ctx { @@ -1020,10 +1153,17 @@ impl Buffer { let old_version = self.version.clone(); let mut ops = Vec::new(); - if let Some(edit_group) = self.history.pop_redo() { - for edit_id in edit_group.edits.clone() { + if let Some(transaction) = self.history.pop_redo() { + let transaction_selections = transaction.selections.clone(); + for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } + + if let Some(transaction_selections) = transaction_selections { + if let Some(selections) = self.selections.get_mut(&transaction_selections.set_id) { + *selections = transaction_selections.after_transaction; + } + } } if let Some(ctx) = ctx { @@ -1686,6 +1826,7 @@ impl Clone for Buffer { last_edit: self.last_edit.clone(), undo_map: self.undo_map.clone(), history: self.history.clone(), + pending_transaction: self.pending_transaction.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 1a6112ef1d..a4d6f15b2c 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -379,6 +379,7 @@ impl BufferView { } } + self.start_transaction(ctx); let mut new_selections = Vec::new(); self.buffer.update(ctx, |buffer, ctx| { if let Err(error) = buffer.edit(offset_ranges.iter().cloned(), text.as_str(), Some(ctx)) @@ -408,8 +409,7 @@ impl BufferView { }); self.update_selections(new_selections, ctx); - self.pause_cursor_blinking(ctx); - *self.autoscroll_requested.lock() = true; + self.end_transaction(ctx); } fn newline(&mut self, _: &(), ctx: &mut ViewContext) { @@ -421,6 +421,7 @@ impl BufferView { } pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext) { + self.start_transaction(ctx); let mut selections = self.selections(ctx.app()).to_vec(); { let buffer = self.buffer.as_ref(ctx); @@ -444,6 +445,7 @@ impl BufferView { self.update_selections(selections, ctx); self.changed_selections(ctx); self.insert(&String::new(), ctx); + self.end_transaction(ctx); } pub fn undo(&mut self, _: &(), ctx: &mut ViewContext) { @@ -727,6 +729,22 @@ impl BufferView { }); } + fn start_transaction(&self, ctx: &mut ViewContext) { + self.buffer.update(ctx, |buffer, _| { + buffer + .start_transaction(Some(self.selection_set_id)) + .unwrap() + }); + } + + fn end_transaction(&self, ctx: &mut ViewContext) { + self.buffer.update(ctx, |buffer, ctx| { + buffer + .end_transaction(Some(self.selection_set_id), Some(ctx)) + .unwrap() + }); + } + pub fn page_up(&mut self, _: &(), _: &mut ViewContext) { log::info!("BufferView::page_up"); } From 606e6bed563c904fa962df0515ce5ed6982779bb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 15:32:45 +0200 Subject: [PATCH 07/13] Simplify buffer history management --- zed/src/editor/buffer/mod.rs | 131 ++++++++++++++++------------------- 1 file changed, 59 insertions(+), 72 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index da37ac1835..2956a2ad8c 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -66,7 +66,6 @@ pub struct Buffer { last_edit: time::Local, undo_map: UndoMap, history: History, - pending_transaction: Option, selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, @@ -81,22 +80,13 @@ pub struct Snapshot { } #[derive(Clone)] -struct SelectionSetSnapshot { - set_id: SelectionSetId, - before_transaction: Arc<[Selection]>, - after_transaction: Arc<[Selection]>, -} - -#[derive(Clone)] -struct PendingTransaction { +struct Transaction { start: time::Global, buffer_was_dirty: bool, -} - -#[derive(Clone)] -struct Transaction { edits: Vec, - selections: Option, + selections_before: Option<(SelectionSetId, Arc<[Selection]>)>, + selections_after: Option<(SelectionSetId, Arc<[Selection]>)>, + first_edit_at: Instant, last_edit_at: Instant, } @@ -128,39 +118,22 @@ impl History { fn start_transaction( &mut self, + start: time::Global, + buffer_was_dirty: bool, selections: Option<(SelectionSetId, Arc<[Selection]>)>, now: Instant, - ) -> bool { + ) { self.transaction_depth += 1; if self.transaction_depth == 1 { - if let Some(transaction) = self.undo_stack.last_mut() { - if now - transaction.last_edit_at <= self.group_interval { - transaction.last_edit_at = now; - } else { - self.undo_stack.push(Transaction { - edits: Vec::new(), - selections: selections.map(|(set_id, selections)| SelectionSetSnapshot { - set_id, - before_transaction: selections.clone(), - after_transaction: selections, - }), - last_edit_at: now, - }); - } - } else { - self.undo_stack.push(Transaction { - edits: Vec::new(), - selections: selections.map(|(set_id, selections)| SelectionSetSnapshot { - set_id, - before_transaction: selections.clone(), - after_transaction: selections, - }), - last_edit_at: now, - }); - } - true - } else { - false + self.undo_stack.push(Transaction { + start, + buffer_was_dirty, + edits: Vec::new(), + selections_before: selections, + selections_after: None, + first_edit_at: now, + last_edit_at: now, + }); } } @@ -168,22 +141,39 @@ impl History { &mut self, selections: Option<(SelectionSetId, Arc<[Selection]>)>, now: Instant, - ) -> bool { + ) -> Option<&Transaction> { assert_ne!(self.transaction_depth, 0); self.transaction_depth -= 1; if self.transaction_depth == 0 { let transaction = self.undo_stack.last_mut().unwrap(); - if let Some((set_id, selections)) = selections { - if let Some(transaction_selections) = &mut transaction.selections { - assert_eq!(set_id, transaction_selections.set_id); - transaction_selections.after_transaction = selections; + transaction.selections_after = selections; + transaction.last_edit_at = now; + Some(transaction) + } else { + None + } + } + + fn group(&mut self) { + let mut new_len = self.undo_stack.len(); + let mut transactions = self.undo_stack.iter_mut(); + + if let Some(mut transaction) = transactions.next_back() { + for prev_transaction in transactions.next_back() { + if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval + { + prev_transaction.edits.append(&mut transaction.edits); + prev_transaction.last_edit_at = transaction.last_edit_at; + prev_transaction.selections_after = transaction.selections_after.take(); + transaction = prev_transaction; + new_len -= 1; + } else { + break; } } - transaction.last_edit_at = now; - true - } else { - false } + + self.undo_stack.truncate(new_len); } fn push_undo(&mut self, edit_id: time::Local) { @@ -429,7 +419,6 @@ impl Buffer { last_edit: time::Local::default(), undo_map: Default::default(), history, - pending_transaction: None, selections: HashMap::default(), selections_last_update: 0, deferred_ops: OperationQueue::new(), @@ -625,12 +614,8 @@ impl Buffer { } else { None }; - if self.history.start_transaction(selections, now) { - self.pending_transaction = Some(PendingTransaction { - start: self.version.clone(), - buffer_was_dirty: self.is_dirty(), - }); - } + self.history + .start_transaction(self.version.clone(), self.is_dirty(), selections, now); Ok(()) } @@ -658,14 +643,17 @@ impl Buffer { None }; - if self.history.end_transaction(selections, now) { + if let Some(transaction) = self.history.end_transaction(selections, now) { + let since = transaction.start.clone(); + let was_dirty = transaction.buffer_was_dirty; + self.history.group(); + if let Some(ctx) = ctx { ctx.notify(); - let transaction = self.pending_transaction.take().unwrap(); - let changes = self.edits_since(transaction.start).collect::>(); + let changes = self.edits_since(since).collect::>(); if !changes.is_empty() { - self.did_edit(changes, transaction.buffer_was_dirty, ctx); + self.did_edit(changes, was_dirty, ctx); } } } @@ -1125,14 +1113,14 @@ impl Buffer { let mut ops = Vec::new(); if let Some(transaction) = self.history.pop_undo() { - let transaction_selections = transaction.selections.clone(); + let transaction_selections = transaction.selections_before.clone(); for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } - if let Some(transaction_selections) = transaction_selections { - if let Some(selections) = self.selections.get_mut(&transaction_selections.set_id) { - *selections = transaction_selections.before_transaction; + if let Some((set_id, transaction_selections)) = transaction_selections { + if let Some(selections) = self.selections.get_mut(&set_id) { + *selections = transaction_selections; } } } @@ -1154,14 +1142,14 @@ impl Buffer { let mut ops = Vec::new(); if let Some(transaction) = self.history.pop_redo() { - let transaction_selections = transaction.selections.clone(); + let transaction_selections = transaction.selections_after.clone(); for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } - if let Some(transaction_selections) = transaction_selections { - if let Some(selections) = self.selections.get_mut(&transaction_selections.set_id) { - *selections = transaction_selections.after_transaction; + if let Some((set_id, transaction_selections)) = transaction_selections { + if let Some(selections) = self.selections.get_mut(&set_id) { + *selections = transaction_selections; } } } @@ -1826,7 +1814,6 @@ impl Clone for Buffer { last_edit: self.last_edit.clone(), undo_map: self.undo_map.clone(), history: self.history.clone(), - pending_transaction: self.pending_transaction.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), From be68af43fc8915f818e5c1e916f1843ddc5b8ce6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 16:00:24 +0200 Subject: [PATCH 08/13] Add unit test for history manipulation (undo, redo, grouping) --- zed/src/editor/buffer/mod.rs | 118 +++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 2956a2ad8c..01c186250c 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -858,35 +858,6 @@ impl Buffer { .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } - pub fn selection_ranges<'a>( - &'a self, - set_id: SelectionSetId, - ) -> Result> + 'a> { - Ok(self.selections(set_id)?.iter().map(move |selection| { - let start = selection.start.to_point(self).unwrap(); - let end = selection.end.to_point(self).unwrap(); - if selection.reversed { - end..start - } else { - start..end - } - })) - } - - pub fn all_selections(&self) -> impl Iterator { - self.selections - .iter() - .map(|(set_id, selections)| (set_id, selections.as_ref())) - } - - pub fn all_selection_ranges<'a>( - &'a self, - ) -> impl 'a + Iterator>)> { - self.selections - .keys() - .map(move |set_id| (*set_id, self.selection_ranges(*set_id).unwrap().collect())) - } - fn merge_selections(&mut self, selections: &mut Vec) { let mut i = 1; while i < selections.len() { @@ -3026,6 +2997,59 @@ mod tests { Ok(()) } + #[test] + fn test_history() -> Result<()> { + let mut now = Instant::now(); + let mut buffer = Buffer::new(0, "123456"); + + let (set_id, _) = + buffer.add_selection_set(buffer.selections_from_ranges(vec![4..4])?, None); + buffer.start_transaction_at(Some(set_id), now)?; + buffer.edit(vec![2..4], "cd", None)?; + buffer.end_transaction_at(Some(set_id), now, None)?; + assert_eq!(buffer.text(), "12cd56"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![4..4]); + + buffer.start_transaction_at(Some(set_id), now)?; + buffer.update_selection_set(set_id, buffer.selections_from_ranges(vec![1..3])?, None)?; + buffer.edit_at(vec![4..5], "e", now, None)?; + buffer.end_transaction_at(Some(set_id), now, None)?; + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![1..3]); + + now += UNDO_GROUP_INTERVAL + Duration::from_millis(1); + buffer.start_transaction_at(Some(set_id), now)?; + buffer.update_selection_set(set_id, buffer.selections_from_ranges(vec![2..2])?, None)?; + buffer.edit_at(vec![0..1], "a", now, None)?; + buffer.end_transaction_at(Some(set_id), now, None)?; + assert_eq!(buffer.text(), "a2cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![2..2]); + + // Last transaction happened past the group interval, undo it on its + // own. + buffer.undo(None); + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![1..3]); + + // First two transactions happened within the group interval, undo them + // together. + buffer.undo(None); + assert_eq!(buffer.text(), "123456"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![4..4]); + + // Redo the first two transactions together. + buffer.redo(None); + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![1..3]); + + // Redo the last transaction on its own. + buffer.redo(None); + assert_eq!(buffer.text(), "a2cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![2..2]); + + Ok(()) + } + #[test] fn test_random_concurrent_edits() { use crate::test::Network; @@ -3121,10 +3145,8 @@ mod tests { let mut ranges = Vec::new(); for _ in 0..5 { let start = rng.gen_range(0..self.len() + 1); - let start_point = self.point_for_offset(start).unwrap(); let end = rng.gen_range(0..self.len() + 1); - let end_point = self.point_for_offset(end).unwrap(); - ranges.push(start_point..end_point); + ranges.push(start..end); } let new_selections = self.selections_from_ranges(ranges).unwrap(); @@ -3152,7 +3174,7 @@ mod tests { fn selections_from_ranges(&self, ranges: I) -> Result> where - I: IntoIterator>, + I: IntoIterator>, { let mut ranges = ranges.into_iter().collect::>(); ranges.sort_unstable_by_key(|range| range.start); @@ -3177,6 +3199,36 @@ mod tests { } Ok(selections) } + + pub fn selection_ranges<'a>(&'a self, set_id: SelectionSetId) -> Result>> { + Ok(self + .selections(set_id)? + .iter() + .map(move |selection| { + let start = selection.start.to_offset(self).unwrap(); + let end = selection.end.to_offset(self).unwrap(); + if selection.reversed { + end..start + } else { + start..end + } + }) + .collect()) + } + + pub fn all_selections(&self) -> impl Iterator { + self.selections + .iter() + .map(|(set_id, selections)| (set_id, selections.as_ref())) + } + + pub fn all_selection_ranges<'a>( + &'a self, + ) -> impl 'a + Iterator>)> { + self.selections + .keys() + .map(move |set_id| (*set_id, self.selection_ranges(*set_id).unwrap())) + } } impl Operation { From 59a9f0102f999b34c831a07ce4e8d24ae2cf3d3c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 17:35:50 +0200 Subject: [PATCH 09/13] Remove unnecessary edit_at method --- zed/src/editor/buffer/mod.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 01c186250c..c2a6eacad6 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -672,22 +672,7 @@ impl Buffer { S: ToOffset, T: Into, { - self.edit_at(old_ranges, new_text, Instant::now(), ctx) - } - - fn edit_at( - &mut self, - old_ranges: I, - new_text: T, - now: Instant, - ctx: Option<&mut ModelContext>, - ) -> Result> - where - I: IntoIterator>, - S: ToOffset, - T: Into, - { - self.start_transaction_at(None, now)?; + self.start_transaction_at(None, Instant::now())?; let new_text = new_text.into(); let new_text = if new_text.len() > 0 { @@ -724,7 +709,7 @@ impl Buffer { } } - self.end_transaction_at(None, now, ctx)?; + self.end_transaction_at(None, Instant::now(), ctx)?; Ok(ops) } @@ -3012,7 +2997,7 @@ mod tests { buffer.start_transaction_at(Some(set_id), now)?; buffer.update_selection_set(set_id, buffer.selections_from_ranges(vec![1..3])?, None)?; - buffer.edit_at(vec![4..5], "e", now, None)?; + buffer.edit(vec![4..5], "e", None)?; buffer.end_transaction_at(Some(set_id), now, None)?; assert_eq!(buffer.text(), "12cde6"); assert_eq!(buffer.selection_ranges(set_id)?, vec![1..3]); @@ -3020,7 +3005,7 @@ mod tests { now += UNDO_GROUP_INTERVAL + Duration::from_millis(1); buffer.start_transaction_at(Some(set_id), now)?; buffer.update_selection_set(set_id, buffer.selections_from_ranges(vec![2..2])?, None)?; - buffer.edit_at(vec![0..1], "a", now, None)?; + buffer.edit(vec![0..1], "a", None)?; buffer.end_transaction_at(Some(set_id), now, None)?; assert_eq!(buffer.text(), "a2cde6"); assert_eq!(buffer.selection_ranges(set_id)?, vec![2..2]); From 3a33fab091cb752959728a8f60bbec849775f8b8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 17:47:19 +0200 Subject: [PATCH 10/13] Generate operation when updating selection set after undo/redo --- zed/src/editor/buffer/mod.rs | 55 +++++++++-------------------------- zed/src/editor/buffer_view.rs | 23 ++++++++++++++- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index c2a6eacad6..b143a4bf33 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -22,7 +22,7 @@ use gpui::{AppContext, Entity, ModelContext}; use lazy_static::lazy_static; use rand::prelude::*; use std::{ - cmp::{self, Ordering}, + cmp, hash::BuildHasher, iter::{self, Iterator}, ops::{AddAssign, Range}, @@ -767,10 +767,10 @@ impl Buffer { pub fn add_selection_set( &mut self, - selections: Vec, + selections: impl Into>, ctx: Option<&mut ModelContext>, ) -> (SelectionSetId, Operation) { - let selections = Arc::from(selections); + let selections = selections.into(); let lamport_timestamp = self.lamport_clock.tick(); self.selections .insert(lamport_timestamp, Arc::clone(&selections)); @@ -793,12 +793,11 @@ impl Buffer { pub fn update_selection_set( &mut self, set_id: SelectionSetId, - mut selections: Vec, + selections: impl Into>, ctx: Option<&mut ModelContext>, ) -> Result { - self.merge_selections(&mut selections); - let selections = Arc::from(selections); - self.selections.insert(set_id, Arc::clone(&selections)); + let selections = selections.into(); + self.selections.insert(set_id, selections.clone()); let lamport_timestamp = self.lamport_clock.tick(); self.selections_last_update += 1; @@ -843,28 +842,6 @@ impl Buffer { .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } - fn merge_selections(&mut self, selections: &mut Vec) { - let mut i = 1; - while i < selections.len() { - if selections[i - 1] - .end - .cmp(&selections[i].start, self) - .unwrap() - >= Ordering::Equal - { - let removed = selections.remove(i); - if removed.start.cmp(&selections[i - 1].start, self).unwrap() < Ordering::Equal { - selections[i - 1].start = removed.start; - } - if removed.end.cmp(&selections[i - 1].end, self).unwrap() > Ordering::Equal { - selections[i - 1].end = removed.end; - } - } else { - i += 1; - } - } - } - pub fn apply_ops>( &mut self, ops: I, @@ -1063,21 +1040,19 @@ impl Buffer { Ok(()) } - pub fn undo(&mut self, ctx: Option<&mut ModelContext>) -> Vec { + pub fn undo(&mut self, mut ctx: Option<&mut ModelContext>) -> Vec { let was_dirty = self.is_dirty(); let old_version = self.version.clone(); let mut ops = Vec::new(); if let Some(transaction) = self.history.pop_undo() { - let transaction_selections = transaction.selections_before.clone(); + let selections = transaction.selections_before.clone(); for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } - if let Some((set_id, transaction_selections)) = transaction_selections { - if let Some(selections) = self.selections.get_mut(&set_id) { - *selections = transaction_selections; - } + if let Some((set_id, selections)) = selections { + let _ = self.update_selection_set(set_id, selections, ctx.as_deref_mut()); } } @@ -1092,21 +1067,19 @@ impl Buffer { ops } - pub fn redo(&mut self, ctx: Option<&mut ModelContext>) -> Vec { + pub fn redo(&mut self, mut ctx: Option<&mut ModelContext>) -> Vec { let was_dirty = self.is_dirty(); let old_version = self.version.clone(); let mut ops = Vec::new(); if let Some(transaction) = self.history.pop_redo() { - let transaction_selections = transaction.selections_after.clone(); + let selections = transaction.selections_after.clone(); for edit_id in transaction.edits.clone() { ops.push(self.undo_or_redo(edit_id).unwrap()); } - if let Some((set_id, transaction_selections)) = transaction_selections { - if let Some(selections) = self.selections.get_mut(&set_id) { - *selections = transaction_selections; - } + if let Some((set_id, selections)) = selections { + let _ = self.update_selection_set(set_id, selections, ctx.as_deref_mut()); } } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index a4d6f15b2c..22cd3f0c9f 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -721,7 +721,28 @@ impl BufferView { .unwrap() } - fn update_selections(&self, selections: Vec, ctx: &mut ViewContext) { + fn update_selections(&self, mut selections: Vec, ctx: &mut ViewContext) { + let buffer = self.buffer.as_ref(ctx); + let mut i = 1; + while i < selections.len() { + if selections[i - 1] + .end + .cmp(&selections[i].start, buffer) + .unwrap() + >= Ordering::Equal + { + let removed = selections.remove(i); + if removed.start.cmp(&selections[i - 1].start, buffer).unwrap() < Ordering::Equal { + selections[i - 1].start = removed.start; + } + if removed.end.cmp(&selections[i - 1].end, buffer).unwrap() > Ordering::Equal { + selections[i - 1].end = removed.end; + } + } else { + i += 1; + } + } + let op = self.buffer.update(ctx, |buffer, ctx| { buffer .update_selection_set(self.selection_set_id, selections, Some(ctx)) From 8f6b8b2e4351891573150e915c1895f1cd6152e0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 19:05:56 +0200 Subject: [PATCH 11/13] :memo: --- zed/src/editor/buffer/mod.rs | 1 + zed/src/editor/buffer_view.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index b143a4bf33..3ce9ba96ca 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2290,6 +2290,7 @@ impl ToPoint for usize { #[cfg(test)] mod tests { use super::*; + use cmp::Ordering; use gpui::App; use std::collections::BTreeMap; use std::{cell::RefCell, rc::Rc}; diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 22cd3f0c9f..5960015b45 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -722,6 +722,7 @@ impl BufferView { } fn update_selections(&self, mut selections: Vec, ctx: &mut ViewContext) { + // Merge overlapping selections. let buffer = self.buffer.as_ref(ctx); let mut i = 1; while i < selections.len() { From afa3f6dcd1ad4ea1e0ddb95114f5a2bf5eefd5ab Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 11:24:10 -0700 Subject: [PATCH 12/13] Add test coverage for edit event grouping in transactions --- zed/src/editor/buffer/mod.rs | 52 +++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index def4d13757..7f06e1700f 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2316,12 +2316,14 @@ mod tests { #[test] fn test_edit_events() { App::test((), |app| { + let mut now = Instant::now(); let buffer_1_events = Rc::new(RefCell::new(Vec::new())); let buffer_2_events = Rc::new(RefCell::new(Vec::new())); let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef")); let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef")); - let ops = buffer1.update(app, |buffer, ctx| { + let mut buffer_ops = Vec::new(); + buffer1.update(app, |buffer, ctx| { let buffer_1_events = buffer_1_events.clone(); ctx.subscribe(&buffer1, move |_, event, _| { buffer_1_events.borrow_mut().push(event.clone()) @@ -2331,10 +2333,33 @@ mod tests { buffer_2_events.borrow_mut().push(event.clone()) }); - buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap() + // An edit emits an edited event, followed by a dirtied event, + // since the buffer was previously in a clean state. + let ops = buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + + // An empty transaction does not emit any events. + buffer.start_transaction(None).unwrap(); + buffer.end_transaction(None, Some(ctx)).unwrap(); + + // A transaction containing two edits emits one edited event. + now += Duration::from_secs(1); + buffer.start_transaction_at(None, now).unwrap(); + let ops = buffer.edit(Some(5..5), "u", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + let ops = buffer.edit(Some(6..6), "w", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + buffer.end_transaction_at(None, now, Some(ctx)).unwrap(); + + // Undoing a transaction emits one edited event. + let ops = buffer.undo(Some(ctx)); + buffer_ops.extend_from_slice(&ops); }); + + // Incorporating a set of remote ops emits a single edited event, + // followed by a dirtied event. buffer2.update(app, |buffer, ctx| { - buffer.apply_ops(ops, Some(ctx)).unwrap(); + buffer.apply_ops(buffer_ops, Some(ctx)).unwrap(); }); let buffer_1_events = buffer_1_events.borrow(); @@ -2344,8 +2369,16 @@ mod tests { Event::Edited(vec![Edit { old_range: 2..4, new_range: 2..5 - },]), - Event::Dirtied + }]), + Event::Dirtied, + Event::Edited(vec![Edit { + old_range: 5..5, + new_range: 5..7 + }]), + Event::Edited(vec![Edit { + old_range: 5..7, + new_range: 5..5 + }]), ] ); @@ -2980,9 +3013,10 @@ mod tests { buffer.start_transaction_at(Some(set_id), now)?; buffer.update_selection_set(set_id, buffer.selections_from_ranges(vec![2..2])?, None)?; buffer.edit(vec![0..1], "a", None)?; + buffer.edit(vec![1..1], "b", None)?; buffer.end_transaction_at(Some(set_id), now, None)?; - assert_eq!(buffer.text(), "a2cde6"); - assert_eq!(buffer.selection_ranges(set_id)?, vec![2..2]); + assert_eq!(buffer.text(), "ab2cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![3..3]); // Last transaction happened past the group interval, undo it on its // own. @@ -3003,8 +3037,8 @@ mod tests { // Redo the last transaction on its own. buffer.redo(None); - assert_eq!(buffer.text(), "a2cde6"); - assert_eq!(buffer.selection_ranges(set_id)?, vec![2..2]); + assert_eq!(buffer.text(), "ab2cde6"); + assert_eq!(buffer.selection_ranges(set_id)?, vec![3..3]); Ok(()) } From b2723fd1ddb1d81dc311091284a46c8f55428ff4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 11:29:24 -0700 Subject: [PATCH 13/13] Remove unused variables --- zed/src/editor/buffer_view.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index f7c516fb60..aa91e39ec5 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -128,7 +128,7 @@ impl BufferView { }); ctx.observe(&display_map, Self::on_display_map_changed); - let (selection_set_id, op) = buffer.update(ctx, |buffer, ctx| { + let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| { buffer.add_selection_set( vec![Selection { start: buffer.anchor_before(0).unwrap(), @@ -745,7 +745,7 @@ impl BufferView { } } - let op = self.buffer.update(ctx, |buffer, ctx| { + self.buffer.update(ctx, |buffer, ctx| { buffer .update_selection_set(self.selection_set_id, selections, Some(ctx)) .unwrap()