diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 9807b8ef97..7f06e1700f 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::{ @@ -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, fragments: SumTree, insertion_splits: HashMap>, - edit_ops: HashMap, pub version: time::Global, saved_version: time::Global, last_edit: time::Local, undo_map: UndoMap, - selections: HashMap>, + history: History, + selections: HashMap>, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, deferred_replicas: HashSet, @@ -79,15 +80,126 @@ pub struct Snapshot { } #[derive(Clone)] -pub struct History { - pub base_text: String, +struct Transaction { + start: time::Global, + buffer_was_dirty: bool, + edits: Vec, + selections_before: Option<(SelectionSetId, Arc<[Selection]>)>, + selections_after: Option<(SelectionSetId, Arc<[Selection]>)>, + first_edit_at: Instant, + last_edit_at: Instant, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Selection { - pub start: Anchor, - pub end: Anchor, - pub reversed: bool, +#[derive(Clone)] +pub struct History { + pub base_text: Arc, + ops: HashMap, + undo_stack: Vec, + redo_stack: Vec, + transaction_depth: usize, + 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(), + 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>, + selections: Option>, lamport_timestamp: time::Lamport, }, } @@ -241,15 +353,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(); @@ -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) -> 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 + }; + self.history + .start_transaction(self.version.clone(), self.is_dirty(), selections, now); + 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 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::>(); + if !changes.is_empty() { + self.did_edit(changes, was_dirty, ctx); + } + } + } + + Ok(()) + } + pub fn edit( &mut self, old_ranges: I, @@ -499,6 +672,8 @@ impl Buffer { S: ToOffset, T: Into, { + 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::>(); - 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(&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: impl Into>, + ctx: Option<&mut ModelContext>, + ) -> (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( + 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)?; - self.merge_selections(&mut selections); + selections: impl Into>, + ctx: Option<&mut ModelContext>, + ) -> Result { + 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 { + 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, @@ -656,81 +835,11 @@ impl Buffer { }) } - 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| { - 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() - } - - pub fn all_selection_ranges<'a>( - &'a self, - ) -> impl 'a + Iterator>)> { + 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) { - 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(&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, - }); - } - } - Ok(selections) + .get(&set_id) + .map(|s| s.as_ref()) + .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } pub fn apply_ops>( @@ -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>) -> 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 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::>(); + if !changes.is_empty() { + self.did_edit(changes, was_dirty, ctx); + } + } + + ops + } + + 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 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::>(); + 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(), @@ -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::(); @@ -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(&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]>); @@ -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::>(); 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 { 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(&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) + } + + 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 { 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/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/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index fadf782280..aa91e39ec5 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; @@ -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, buffer: ModelHandle, display_map: ModelHandle, - selections: Vec, + selection_set_id: SelectionSetId, pending_selection: Option, scroll_position: Mutex, autoscroll_requested: Mutex, @@ -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) { 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>, { - 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) { - let buffer = self.buffer.read(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.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) { @@ -416,31 +422,49 @@ impl BufferView { } pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext) { - 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.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(); + 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) { + 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) { + 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) { + 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.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 { - self.selections + self.selections(app) .first() .unwrap() .display_range(self.display_map.read(app), app) } pub fn last_selection(&self, app: &AppContext) -> Range { - 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, ctx: &mut ViewContext) { + // 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.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"); } @@ -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::>(); @@ -1086,13 +1148,6 @@ impl BufferView { } } -struct Selection { - start: Anchor, - end: Anchor, - reversed: bool, - goal_column: Option, -} - 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 { - 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::*; @@ -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> { + fn selection_ranges(&self, app: &AppContext) -> Vec> { self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app) .collect::>() } diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index 63be9b6eef..2e685261ce 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 { .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()); }); } }