From ab14b99a7357474d724be08108b45a8709a053c4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Apr 2021 15:15:27 +0200 Subject: [PATCH] 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"); }