Merge pull request #13 from zed-industries/undo-stack

Max Brunsfeld created

Undo/redo support

Change summary

zed/src/editor/buffer/mod.rs       | 630 ++++++++++++++++++++++---------
zed/src/editor/buffer/selection.rs |  75 +++
zed/src/editor/buffer/text.rs      |  20 
zed/src/editor/buffer_view.rs      | 463 +++++++++++-----------
zed/src/worktree/worktree.rs       |   4 
5 files changed, 770 insertions(+), 422 deletions(-)

Detailed changes

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::{
@@ -20,18 +22,17 @@ use gpui::{AppContext, Entity, ModelContext};
 use lazy_static::lazy_static;
 use rand::prelude::*;
 use std::{
-    cmp::{self, Ordering},
+    cmp,
     hash::BuildHasher,
     iter::{self, Iterator},
-    mem,
     ops::{AddAssign, Range},
     path::PathBuf,
     str,
     sync::Arc,
+    time::{Duration, Instant},
 };
 
-pub type SelectionSetId = time::Lamport;
-pub type SelectionsVersion = usize;
+const UNDO_GROUP_INTERVAL: Duration = Duration::from_millis(300);
 
 #[derive(Clone, Default)]
 struct DeterministicState;
@@ -60,12 +61,12 @@ pub struct Buffer {
     file: Option<FileHandle>,
     fragments: SumTree<Fragment>,
     insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
-    edit_ops: HashMap<time::Local, EditOperation>,
     pub version: time::Global,
     saved_version: time::Global,
     last_edit: time::Local,
     undo_map: UndoMap,
-    selections: HashMap<SelectionSetId, Vec<Selection>>,
+    history: History,
+    selections: HashMap<SelectionSetId, Arc<[Selection]>>,
     pub selections_last_update: SelectionsVersion,
     deferred_ops: OperationQueue<Operation>,
     deferred_replicas: HashSet<ReplicaId>,
@@ -78,16 +79,127 @@ pub struct Snapshot {
     fragments: SumTree<Fragment>,
 }
 
+#[derive(Clone)]
+struct Transaction {
+    start: time::Global,
+    buffer_was_dirty: bool,
+    edits: Vec<time::Local>,
+    selections_before: Option<(SelectionSetId, Arc<[Selection]>)>,
+    selections_after: Option<(SelectionSetId, Arc<[Selection]>)>,
+    first_edit_at: Instant,
+    last_edit_at: Instant,
+}
+
 #[derive(Clone)]
 pub struct History {
-    pub base_text: String,
+    pub base_text: Arc<str>,
+    ops: HashMap<time::Local, EditOperation>,
+    undo_stack: Vec<Transaction>,
+    redo_stack: Vec<Transaction>,
+    transaction_depth: usize,
+    group_interval: Duration,
 }
 
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Selection {
-    pub start: Anchor,
-    pub end: Anchor,
-    pub reversed: bool,
+impl History {
+    pub fn new(base_text: Arc<str>) -> Self {
+        Self {
+            base_text,
+            ops: Default::default(),
+            undo_stack: Vec::new(),
+            redo_stack: Vec::new(),
+            transaction_depth: 0,
+            group_interval: UNDO_GROUP_INTERVAL,
+        }
+    }
+
+    fn push(&mut self, op: EditOperation) {
+        self.ops.insert(op.id, op);
+    }
+
+    fn start_transaction(
+        &mut self,
+        start: time::Global,
+        buffer_was_dirty: bool,
+        selections: Option<(SelectionSetId, Arc<[Selection]>)>,
+        now: Instant,
+    ) {
+        self.transaction_depth += 1;
+        if self.transaction_depth == 1 {
+            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,
+            });
+        }
+    }
+
+    fn end_transaction(
+        &mut self,
+        selections: Option<(SelectionSetId, Arc<[Selection]>)>,
+        now: Instant,
+    ) -> 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();
+            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;
+                }
+            }
+        }
+
+        self.undo_stack.truncate(new_len);
+    }
+
+    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<&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
+        }
+    }
 }
 
 #[derive(Clone, Default, Debug)]
@@ -217,7 +329,7 @@ pub enum Operation {
     },
     UpdateSelections {
         set_id: SelectionSetId,
-        selections: Option<Vec<Selection>>,
+        selections: Option<Arc<[Selection]>>,
         lamport_timestamp: time::Lamport,
     },
 }
@@ -241,15 +353,15 @@ pub struct UndoOperation {
 }
 
 impl Buffer {
-    pub fn new<T: Into<String>>(replica_id: ReplicaId, base_text: T) -> Self {
-        Self::build(replica_id, None, base_text.into())
+    pub fn new<T: Into<Arc<str>>>(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<FileHandle>, base_text: String) -> Self {
+    fn build(replica_id: ReplicaId, file: Option<FileHandle>, history: History) -> Self {
         let mut insertion_splits = HashMap::default();
         let mut fragments = SumTree::new();
 
@@ -257,7 +369,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(),
         };
 
@@ -302,11 +414,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(),
+            history,
             selections: HashMap::default(),
             selections_last_update: 0,
             deferred_ops: OperationQueue::new(),
@@ -488,6 +600,67 @@ 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
+        };
+        self.history
+            .start_transaction(self.version.clone(), self.is_dirty(), selections, now);
+        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 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 changes = self.edits_since(since).collect::<Vec<_>>();
+                if !changes.is_empty() {
+                    self.did_edit(changes, was_dirty, ctx);
+                }
+            }
+        }
+
+        Ok(())
+    }
+
     pub fn edit<I, S, T>(
         &mut self,
         old_ranges: I,
@@ -499,6 +672,8 @@ impl Buffer {
         S: ToOffset,
         T: Into<Text>,
     {
+        self.start_transaction_at(None, Instant::now())?;
+
         let new_text = new_text.into();
         let new_text = if new_text.len() > 0 {
             Some(new_text)
@@ -506,8 +681,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)?))
@@ -522,19 +695,12 @@ impl Buffer {
 
         for op in &ops {
             if let Operation::Edit { edit, .. } = op {
-                self.edit_ops.insert(edit.id, edit.clone());
+                self.history.push(edit.clone());
+                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);
@@ -543,6 +709,8 @@ impl Buffer {
             }
         }
 
+        self.end_transaction_at(None, Instant::now(), ctx)?;
+
         Ok(ops)
     }
 
@@ -597,45 +765,47 @@ impl Buffer {
         (old_ranges, new_text, operations)
     }
 
-    pub fn add_selection_set<I>(&mut self, ranges: I) -> Result<(SelectionSetId, Operation)>
-    where
-        I: IntoIterator<Item = Range<Point>>,
-    {
-        let selections = self.selections_from_ranges(ranges)?;
+    pub fn add_selection_set(
+        &mut self,
+        selections: impl Into<Arc<[Selection]>>,
+        ctx: Option<&mut ModelContext<Self>>,
+    ) -> (SelectionSetId, Operation) {
+        let selections = selections.into();
         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;
 
-        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<I>(
+    pub fn update_selection_set(
         &mut self,
         set_id: SelectionSetId,
-        ranges: I,
-    ) -> Result<Operation>
-    where
-        I: IntoIterator<Item = Range<Point>>,
-    {
-        self.selections
-            .remove(&set_id)
-            .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
-
-        let mut selections = self.selections_from_ranges(ranges)?;
-        self.merge_selections(&mut selections);
+        selections: impl Into<Arc<[Selection]>>,
+        ctx: Option<&mut ModelContext<Self>>,
+    ) -> Result<Operation> {
+        let selections = selections.into();
         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),
@@ -643,12 +813,21 @@ impl Buffer {
         })
     }
 
-    pub fn remove_selection_set(&mut self, set_id: SelectionSetId) -> Result<Operation> {
+    pub fn remove_selection_set(
+        &mut self,
+        set_id: SelectionSetId,
+        ctx: Option<&mut ModelContext<Self>>,
+    ) -> Result<Operation> {
         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,
@@ -656,81 +835,11 @@ impl Buffer {
         })
     }
 
-    pub fn selection_ranges<'a>(
-        &'a self,
-        set_id: SelectionSetId,
-    ) -> Result<impl Iterator<Item = Range<Point>> + 'a> {
-        let selections = self
-            .selections
-            .get(&set_id)
-            .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
-        Ok(selections.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<Item = (&SelectionSetId, &Vec<Selection>)> {
-        self.selections.iter()
-    }
-
-    pub fn all_selection_ranges<'a>(
-        &'a self,
-    ) -> impl 'a + Iterator<Item = (SelectionSetId, Vec<Range<Point>>)> {
+    pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> {
         self.selections
-            .keys()
-            .map(move |set_id| (*set_id, self.selection_ranges(*set_id).unwrap().collect()))
-    }
-
-    fn merge_selections(&mut self, selections: &mut Vec<Selection>) {
-        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));
-                    }
-                }
-                new_selections.push(prev_selection);
-            }
-        }
-        *selections = new_selections;
-    }
-
-    fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection>>
-    where
-        I: IntoIterator<Item = Range<Point>>,
-    {
-        let mut ranges = ranges.into_iter().collect::<Vec<_>>();
-        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,
-                });
-            }
-        }
-        Ok(selections)
+            .get(&set_id)
+            .map(|s| s.as_ref())
+            .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))
     }
 
     pub fn apply_ops<I: IntoIterator<Item = Operation>>(
@@ -783,7 +892,7 @@ impl Buffer {
                         lamport_timestamp,
                     )?;
                     self.version.observe(edit.id);
-                    self.edit_ops.insert(edit.id, edit);
+                    self.history.push(edit);
                 }
             }
             Operation::Undo {
@@ -931,6 +1040,60 @@ impl Buffer {
         Ok(())
     }
 
+    pub fn undo(&mut self, mut ctx: Option<&mut ModelContext<Self>>) -> Vec<Operation> {
+        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 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, selections)) = selections {
+                let _ = self.update_selection_set(set_id, selections, ctx.as_deref_mut());
+            }
+        }
+
+        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);
+            }
+        }
+
+        ops
+    }
+
+    pub fn redo(&mut self, mut ctx: Option<&mut ModelContext<Self>>) -> Vec<Operation> {
+        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 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, selections)) = selections {
+                let _ = self.update_selection_set(set_id, selections, ctx.as_deref_mut());
+            }
+        }
+
+        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);
+            }
+        }
+
+        ops
+    }
+
     fn undo_or_redo(&mut self, edit_id: time::Local) -> Result<Operation> {
         let undo = UndoOperation {
             id: self.local_clock.tick(),
@@ -950,7 +1113,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::<FragmentIdRef, ()>();
@@ -1575,11 +1738,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(),
+            history: self.history.clone(),
             selections: self.selections.clone(),
             selections_last_update: self.selections_last_update.clone(),
             deferred_ops: self.deferred_ops.clone(),
@@ -1801,48 +1964,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<S>(&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<Anchor> {
-        self.start.clone()..self.end.clone()
-    }
-}
-
 #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
 struct FragmentId(Arc<[u16]>);
 
@@ -2169,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};
@@ -2194,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())
@@ -2209,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();
@@ -2222,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
+                    }]),
                 ]
             );
 
@@ -2834,6 +2989,60 @@ 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(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]);
+
+        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(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(), "ab2cde6");
+        assert_eq!(buffer.selection_ranges(set_id)?, vec![3..3]);
+
+        // 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(), "ab2cde6");
+        assert_eq!(buffer.selection_ranges(set_id)?, vec![3..3]);
+
+        Ok(())
+    }
+
     #[test]
     fn test_random_concurrent_edits() {
         use crate::test::Network;
@@ -2923,22 +3132,21 @@ mod tests {
                 .collect::<Vec<_>>();
             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();
                 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();
 
                 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);
@@ -2950,12 +3158,70 @@ mod tests {
         pub fn randomly_undo_redo(&mut self, rng: &mut impl Rng) -> Vec<Operation> {
             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());
                 }
             }
             ops
         }
+
+        fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection>>
+        where
+            I: IntoIterator<Item = Range<usize>>,
+        {
+            let mut ranges = ranges.into_iter().collect::<Vec<_>>();
+            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)
+        }
+
+        pub fn selection_ranges<'a>(&'a self, set_id: SelectionSetId) -> Result<Vec<Range<usize>>> {
+            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<Item = (&SelectionSetId, &[Selection])> {
+            self.selections
+                .iter()
+                .map(|(set_id, selections)| (set_id, selections.as_ref()))
+        }
+
+        pub fn all_selection_ranges<'a>(
+            &'a self,
+        ) -> impl 'a + Iterator<Item = (SelectionSetId, Vec<Range<usize>>)> {
+            self.selections
+                .keys()
+                .map(move |set_id| (*set_id, self.selection_ranges(*set_id).unwrap()))
+        }
     }
 
     impl Operation {

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<u32>,
+}
+
+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<Point> {
+        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<DisplayPoint> {
+        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
+        }
+    }
+}

zed/src/editor/buffer/text.rs 🔗

@@ -117,6 +117,18 @@ pub struct Text {
 
 impl From<String> 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<Arc<str>> for Text {
+    fn from(text: Arc<str>) -> Self {
         let mut runs = Vec::new();
 
         let mut chars_len = 0;
@@ -147,19 +159,13 @@ impl From<String> 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()

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;
@@ -17,7 +17,6 @@ use smol::Timer;
 use std::{
     cmp::{self, Ordering},
     fmt::Write,
-    mem,
     ops::Range,
     sync::Arc,
     time::Duration,
@@ -29,6 +28,8 @@ pub fn init(app: &mut MutableAppContext) {
     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")),
@@ -53,6 +54,8 @@ pub fn init(app: &mut MutableAppContext) {
     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);
@@ -87,7 +90,7 @@ pub struct BufferView {
     handle: WeakViewHandle<Self>,
     buffer: ModelHandle<Buffer>,
     display_map: ModelHandle<DisplayMap>,
-    selections: Vec<Selection>,
+    selection_set_id: SelectionSetId,
     pending_selection: Option<Selection>,
     scroll_position: Mutex<Vector2F>,
     autoscroll_requested: Mutex<bool>,
@@ -125,17 +128,22 @@ impl BufferView {
         });
         ctx.observe(&display_map, Self::on_display_map_changed);
 
-        let buffer_ref = buffer.read(ctx);
+        let (selection_set_id, _) = 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),
@@ -191,7 +199,7 @@ impl BufferView {
         let map = self.display_map.read(app);
         let visible_lines = viewport_height / line_height;
         let first_cursor_top = self
-            .selections
+            .selections(app)
             .first()
             .unwrap()
             .head()
@@ -199,7 +207,7 @@ impl BufferView {
             .unwrap()
             .row() as f32;
         let last_cursor_bottom = self
-            .selections
+            .selections(app)
             .last()
             .unwrap()
             .head()
@@ -242,7 +250,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);
@@ -299,7 +307,7 @@ impl BufferView {
         };
 
         if !add {
-            self.selections.clear();
+            self.update_selections(Vec::new(), ctx);
         }
         self.pending_selection = Some(selection);
 
@@ -330,9 +338,9 @@ impl BufferView {
     fn end_selection(&mut self, ctx: &mut ViewContext<Self>) {
         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");
         }
@@ -347,7 +355,6 @@ impl BufferView {
     where
         T: IntoIterator<Item = &'a Range<DisplayPoint>>,
     {
-        let buffer = self.buffer.read(ctx);
         let map = self.display_map.read(ctx);
         let mut selections = Vec::new();
         for range in ranges {
@@ -358,53 +365,52 @@ 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<Self>) {
-        let buffer = self.buffer.read(ctx);
         let mut offset_ranges = SmallVec::<[Range<usize>; 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.read(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);
+            }
         }
 
+        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))
             {
                 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.read(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.pause_cursor_blinking(ctx);
-        *self.autoscroll_requested.lock() = true;
+        self.update_selections(new_selections, ctx);
+        self.end_transaction(ctx);
     }
 
     fn newline(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
@@ -416,31 +422,49 @@ impl BufferView {
     }
 
     pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
-        let buffer = self.buffer.read(ctx);
-        let map = self.display_map.read(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;
+        self.start_transaction(ctx);
+        let mut selections = self.selections(ctx.app()).to_vec();
+        {
+            let buffer = self.buffer.read(ctx);
+            let map = self.display_map.read(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);
+        self.end_transaction(ctx);
+    }
+
+    pub fn undo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.buffer
+            .update(ctx, |buffer, ctx| buffer.undo(Some(ctx)));
+    }
+
+    pub fn redo(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        self.buffer
+            .update(ctx, |buffer, ctx| buffer.redo(Some(ctx)));
     }
 
     pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
+        let app = ctx.app();
+        let mut selections = self.selections(app).to_vec();
         {
-            let app = ctx.app();
-            let map = self.display_map.read(ctx);
-            for selection in &mut self.selections {
+            let map = self.display_map.read(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();
 
@@ -457,14 +481,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<Self>) {
+        let mut selections = self.selections(ctx.app()).to_vec();
         {
             let buffer = self.buffer.read(ctx);
             let map = self.display_map.read(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(
@@ -477,14 +503,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<Self>) {
+        let mut selections = self.selections(ctx.app()).to_vec();
         {
             let app = ctx.app();
             let map = self.display_map.read(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();
 
@@ -501,15 +529,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<Self>) {
+        let mut selections = self.selections(ctx.app()).to_vec();
         {
-            let buffer = self.buffer.read(ctx);
             let app = ctx.app();
+            let buffer = self.buffer.read(app);
             let map = self.display_map.read(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)
@@ -518,6 +548,7 @@ impl BufferView {
                 selection.goal_column = None;
             }
         }
+        self.update_selections(selections, ctx);
         self.changed_selections(ctx);
     }
 
@@ -525,23 +556,27 @@ impl BufferView {
         if self.single_line {
             ctx.propagate_action();
         } else {
-            let app = ctx.app();
-            let map = self.display_map.read(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.read(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);
         }
     }
@@ -550,16 +585,20 @@ impl BufferView {
         if self.single_line {
             ctx.propagate_action();
         } else {
-            let app = ctx.app();
-            let buffer = self.buffer.read(app);
-            let map = self.display_map.read(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.read(app);
+                let map = self.display_map.read(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);
         }
     }
@@ -568,23 +607,27 @@ impl BufferView {
         if self.single_line {
             ctx.propagate_action();
         } else {
-            let app = ctx.app();
-            let map = self.display_map.read(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.read(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);
         }
     }
@@ -593,69 +636,39 @@ impl BufferView {
         if self.single_line {
             ctx.propagate_action();
         } else {
-            let app = ctx.app();
-            let buffer = self.buffer.read(ctx);
-            let map = self.display_map.read(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.read(app);
+                let map = self.display_map.read(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>) {
-        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.read(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<DisplayPoint> {
-        self.selections
+        self.selections(app)
             .first()
             .unwrap()
             .display_range(self.display_map.read(app), app)
     }
 
     pub fn last_selection(&self, app: &AppContext) -> Range<DisplayPoint> {
-        self.selections
+        self.selections(app)
             .last()
             .unwrap()
             .display_range(self.display_map.read(app), app)
@@ -678,7 +691,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)
@@ -687,16 +700,12 @@ impl BufferView {
 
     fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize {
         let buffer = self.buffer.read(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 {
@@ -706,6 +715,59 @@ impl BufferView {
         }
     }
 
+    fn selections<'a>(&self, app: &'a AppContext) -> &'a [Selection] {
+        self.buffer
+            .read(app)
+            .selections(self.selection_set_id)
+            .unwrap()
+    }
+
+    fn update_selections(&self, mut selections: Vec<Selection>, ctx: &mut ViewContext<Self>) {
+        // Merge overlapping selections.
+        let buffer = self.buffer.read(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;
+            }
+        }
+
+        self.buffer.update(ctx, |buffer, ctx| {
+            buffer
+                .update_selection_set(self.selection_set_id, selections, Some(ctx))
+                .unwrap()
+        });
+    }
+
+    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");
     }
@@ -721,7 +783,7 @@ impl BufferView {
 
         let app = ctx.app();
         let map = self.display_map.read(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;
 
@@ -753,7 +815,7 @@ impl BufferView {
         let map = self.display_map.read(app);
         let buffer = self.buffer.read(app);
         let ranges = self
-            .selections
+            .selections(app)
             .iter()
             .map(|s| {
                 let (start, end) = s.display_range(map, app).sorted();
@@ -833,7 +895,7 @@ impl BufferView {
         self.display_map.update(ctx, |map, ctx| {
             let buffer = self.buffer.read(ctx);
             let ranges = self
-                .selections
+                .selections(ctx.app())
                 .iter()
                 .map(|s| s.range(buffer))
                 .collect::<Vec<_>>();
@@ -1086,13 +1148,6 @@ impl BufferView {
     }
 }
 
-struct Selection {
-    start: Anchor,
-    end: Anchor,
-    reversed: bool,
-    goal_column: Option<u32>,
-}
-
 pub enum Event {
     Activate,
     Edited,
@@ -1181,60 +1236,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<Point> {
-        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<DisplayPoint> {
-        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::*;
@@ -1487,12 +1488,12 @@ mod tests {
             view.update(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>(())
@@ -1536,7 +1537,7 @@ mod tests {
     }
 
     impl BufferView {
-        fn selections(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
+        fn selection_ranges(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
             self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)
                 .collect::<Vec<_>>()
         }

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 {
                 .read(|ctx| tree.read(ctx).load_history(file_id))
                 .await
                 .unwrap();
-            assert_eq!(history.base_text, buffer.text());
+            assert_eq!(history.base_text.as_ref(), buffer.text());
         });
     }
 }