Introduce transactional edits and allow snapshotting of selections

This commit is contained in:
Antonio Scandurra 2021-04-12 15:15:27 +02:00
parent 40bfdd38ae
commit ab14b99a73
2 changed files with 203 additions and 44 deletions

View file

@ -66,7 +66,8 @@ pub struct Buffer {
last_edit: time::Local,
undo_map: UndoMap,
history: History,
selections: HashMap<SelectionSetId, Vec<Selection>>,
pending_transaction: Option<PendingTransaction>,
selections: HashMap<SelectionSetId, Arc<[Selection]>>,
pub selections_last_update: SelectionsVersion,
deferred_ops: OperationQueue<Operation>,
deferred_replicas: HashSet<ReplicaId>,
@ -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<time::Local>,
selections: Option<SelectionSetSnapshot>,
last_edit_at: Instant,
}
@ -89,8 +104,9 @@ struct EditGroup {
pub struct History {
pub base_text: Arc<str>,
ops: HashMap<time::Local, EditOperation>,
undo_stack: Vec<EditGroup>,
redo_stack: Vec<EditGroup>,
undo_stack: Vec<Transaction>,
redo_stack: Vec<Transaction>,
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<Vec<Selection>>,
selections: Option<Arc<[Selection]>>,
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<SelectionSetId>) -> Result<()> {
self.start_transaction_at(set_id, Instant::now())
}
fn start_transaction_at(&mut self, set_id: Option<SelectionSetId>, 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<SelectionSetId>,
ctx: Option<&mut ModelContext<Self>>,
) -> Result<()> {
self.end_transaction_at(set_id, Instant::now(), ctx)
}
fn end_transaction_at(
&mut self,
set_id: Option<SelectionSetId>,
now: Instant,
ctx: Option<&mut ModelContext<Self>>,
) -> 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::<Vec<_>>();
if !changes.is_empty() {
self.did_edit(changes, transaction.buffer_was_dirty, ctx);
}
}
}
Ok(())
}
pub fn edit<I, S, T>(
&mut self,
old_ranges: I,
@ -571,6 +699,8 @@ impl Buffer {
S: ToOffset,
T: Into<Text>,
{
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::<Vec<_>>();
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<Selection>,
ctx: Option<&mut ModelContext<Self>>,
) -> (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<Self>>,
) -> Result<Operation> {
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<Item = (&SelectionSetId, &Vec<Selection>)> {
self.selections.iter()
pub fn all_selections(&self) -> impl Iterator<Item = (&SelectionSetId, &[Selection])> {
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(),

View file

@ -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<Self>) {
@ -421,6 +421,7 @@ impl BufferView {
}
pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
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<Self>) {
@ -727,6 +729,22 @@ impl BufferView {
});
}
fn start_transaction(&self, ctx: &mut ViewContext<Self>) {
self.buffer.update(ctx, |buffer, _| {
buffer
.start_transaction(Some(self.selection_set_id))
.unwrap()
});
}
fn end_transaction(&self, ctx: &mut ViewContext<Self>) {
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<Self>) {
log::info!("BufferView::page_up");
}