From c9dcfff60745dcc46cf0e64afe61c7bd838578af Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 5 May 2022 21:09:26 -0700 Subject: [PATCH 1/6] Move selection helpers to SelectionCollection, add update_anchor_selections, add a number of invariant preserving mutation functions to the MutableSelectionCollection --- crates/breadcrumbs/src/breadcrumbs.rs | 2 +- crates/collab/src/rpc.rs | 20 +- crates/diagnostics/src/diagnostics.rs | 11 +- crates/diagnostics/src/items.rs | 4 +- crates/editor/src/editor.rs | 2274 +++++++---------- crates/editor/src/element.rs | 8 +- crates/editor/src/items.rs | 22 +- crates/editor/src/selections_collection.rs | 703 +++++ crates/editor/src/test.rs | 2 +- crates/go_to_line/src/go_to_line.rs | 6 +- crates/journal/src/journal.rs | 4 +- crates/outline/src/outline.rs | 8 +- crates/project_symbols/src/project_symbols.rs | 8 +- crates/search/src/buffer_search.rs | 46 +- crates/search/src/project_search.rs | 18 +- crates/vim/src/insert.rs | 8 +- crates/vim/src/normal.rs | 38 +- crates/vim/src/normal/change.rs | 27 +- crates/vim/src/normal/delete.rs | 28 +- crates/vim/src/vim_test_context.rs | 7 +- crates/vim/src/visual.rs | 76 +- crates/zed/src/zed.rs | 38 +- 22 files changed, 1891 insertions(+), 1467 deletions(-) create mode 100644 crates/editor/src/selections_collection.rs diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index b2bba37e38..bbd11deac8 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -37,7 +37,7 @@ impl Breadcrumbs { cx: &AppContext, ) -> Option<(ModelHandle, Vec>)> { let editor = self.editor.as_ref()?.read(cx); - let cursor = editor.newest_anchor_selection().head(); + let cursor = editor.selections.newest_anchor().head(); let multibuffer = &editor.buffer().read(cx); let (buffer_id, symbols) = multibuffer .read(cx) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 33d1d52677..1536ceae92 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -3001,7 +3001,7 @@ mod tests { // Type a completion trigger character as the guest. editor_b.update(cx_b, |editor, cx| { - editor.select_ranges([13..13], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([13..13], None)); editor.handle_input(&Input(".".into()), cx); cx.focus(&editor_b); }); @@ -4213,7 +4213,9 @@ mod tests { // Move cursor to a location that contains code actions. editor_b.update(cx_b, |editor, cx| { - editor.select_ranges([Point::new(1, 31)..Point::new(1, 31)], None, cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([Point::new(1, 31)..Point::new(1, 31)], None) + }); cx.focus(&editor_b); }); @@ -4450,7 +4452,7 @@ mod tests { // Move cursor to a location that can be renamed. let prepare_rename = editor_b.update(cx_b, |editor, cx| { - editor.select_ranges([7..7], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([7..7], None)); editor.rename(&Rename, cx).unwrap() }); @@ -5470,8 +5472,12 @@ mod tests { }); // When client B starts following client A, all visible view states are replicated to client B. - editor_a1.update(cx_a, |editor, cx| editor.select_ranges([0..1], None, cx)); - editor_a2.update(cx_a, |editor, cx| editor.select_ranges([2..3], None, cx)); + editor_a1.update(cx_a, |editor, cx| { + editor.change_selections(true, cx, |s| s.select_ranges([0..1], None)) + }); + editor_a2.update(cx_a, |editor, cx| { + editor.change_selections(true, cx, |s| s.select_ranges([2..3], None)) + }); workspace_b .update(cx_b, |workspace, cx| { workspace @@ -5536,7 +5542,7 @@ mod tests { // Changes to client A's editor are reflected on client B. editor_a1.update(cx_a, |editor, cx| { - editor.select_ranges([1..1, 2..2], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([1..1, 2..2], None)); }); editor_b1 .condition(cx_b, |editor, cx| { @@ -5550,7 +5556,7 @@ mod tests { .await; editor_a1.update(cx_a, |editor, cx| { - editor.select_ranges([3..3], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([3..3], None)); editor.set_scroll_position(vec2f(0., 100.), cx); }); editor_b1 diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index eb8fae2ab2..31807f07d8 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -417,8 +417,11 @@ impl ProjectDiagnosticsEditor { }]; } else { groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice(); - new_excerpt_ids_by_selection_id = editor.refresh_selections(cx); - selections = editor.local_selections::(cx); + new_excerpt_ids_by_selection_id = + editor.change_selections(true, cx, |s| s.refresh()); + selections = editor + .selections + .interleaved::(&editor.buffer().read(cx).read(cx)); } // If any selection has lost its position, move it to start of the next primary diagnostic. @@ -441,7 +444,9 @@ impl ProjectDiagnosticsEditor { } } } - editor.update_selections(selections, None, cx); + editor.change_selections(true, cx, |s| { + s.select(selections, None); + }); Some(()) }); diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index ef99cbf5a8..abc0b13181 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -58,9 +58,7 @@ impl DiagnosticIndicator { fn update(&mut self, editor: ViewHandle, cx: &mut ViewContext) { let editor = editor.read(cx); let buffer = editor.buffer().read(cx); - let cursor_position = editor - .newest_selection_with_snapshot::(&buffer.read(cx)) - .head(); + let cursor_position = editor.selections.newest::(&buffer.read(cx)).head(); let new_diagnostic = buffer .read(cx) .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a9a95280d8..b01bee0ab4 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3,6 +3,7 @@ mod element; pub mod items; pub mod movement; mod multi_buffer; +mod selections_collection; #[cfg(test)] mod test; @@ -28,7 +29,6 @@ use gpui::{ ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; -use itertools::Itertools as _; pub use language::{char_kind, CharKind}; use language::{ BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity, @@ -40,6 +40,7 @@ pub use multi_buffer::{ }; use ordered_float::OrderedFloat; use project::{Project, ProjectTransaction}; +use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; use smallvec::SmallVec; @@ -49,8 +50,7 @@ use std::{ any::TypeId, borrow::Cow, cmp::{self, Ordering, Reverse}, - iter::{self, FromIterator}, - mem, + iter, mem, ops::{Deref, DerefMut, Range, RangeInclusive, Sub}, sync::Arc, time::{Duration, Instant}, @@ -377,9 +377,7 @@ pub struct Editor { handle: WeakViewHandle, buffer: ModelHandle, display_map: ModelHandle, - next_selection_id: usize, - selections: Arc<[Selection]>, - pending_selection: Option, + pub selections: SelectionsCollection, columnar_selection_tail: Option, add_selections_state: Option, select_next_state: Option, @@ -429,13 +427,7 @@ pub struct EditorSnapshot { scroll_top_anchor: Anchor, } -#[derive(Clone)] -pub struct PendingSelection { - selection: Selection, - mode: SelectMode, -} - -#[derive(Clone)] +#[derive(Clone, Debug)] struct SelectionHistoryEntry { selections: Arc<[Selection]>, select_next_state: Option, @@ -527,13 +519,13 @@ impl SelectionHistory { } } -#[derive(Clone)] +#[derive(Clone, Debug)] struct AddSelectionsState { above: bool, stack: Vec, } -#[derive(Clone)] +#[derive(Clone, Debug)] struct SelectNextState { query: AhoCorasick, wordwise: bool, @@ -949,19 +941,8 @@ impl Editor { handle: cx.weak_handle(), buffer, display_map, - selections: Arc::from([]), - pending_selection: Some(PendingSelection { - selection: Selection { - id: 0, - start: Anchor::min(), - end: Anchor::min(), - reversed: false, - goal: SelectionGoal::None, - }, - mode: SelectMode::Character, - }), + selections: SelectionsCollection::new(), columnar_selection_tail: None, - next_selection_id: 1, add_selections_state: None, select_next_state: None, selection_history: Default::default(), @@ -1204,12 +1185,15 @@ impl Editor { first_cursor_top = highlighted_rows.start as f32; last_cursor_bottom = first_cursor_top + 1.; } else if autoscroll == Autoscroll::Newest { - let newest_selection = - self.newest_selection_with_snapshot::(&display_map.buffer_snapshot); + let newest_selection = self + .selections + .newest::(&display_map.buffer_snapshot); first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32; last_cursor_bottom = first_cursor_top + 1.; } else { - let selections = self.local_selections::(cx); + let selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); first_cursor_top = selections .first() .unwrap() @@ -1269,7 +1253,9 @@ impl Editor { cx: &mut ViewContext, ) -> bool { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.local_selections::(cx); + let selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); let mut target_left; let mut target_right; @@ -1318,22 +1304,91 @@ impl Editor { } } - pub fn replace_selections_with( + pub fn change_selections( &mut self, + local: bool, cx: &mut ViewContext, - mut find_replacement: impl FnMut(&DisplaySnapshot) -> DisplayPoint, - ) { - let display_map = self.snapshot(cx); - let cursor = find_replacement(&display_map); - let selection = Selection { - id: post_inc(&mut self.next_selection_id), - start: cursor, - end: cursor, - reversed: false, - goal: SelectionGoal::None, + change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R, + ) -> R { + let old_cursor_position = self.selections.newest_anchor().head(); + self.push_to_selection_history(); + + #[allow(deprecated)] + // Using deprecated to prevent using change_with outside of this function + let (autoscroll, result) = + self.selections + .change_with(self.display_map.clone(), self.buffer.clone(), cx, change); + + if let Some(autoscroll) = autoscroll { + self.request_autoscroll(autoscroll, cx); } - .map(|display_point| display_point.to_point(&display_map)); - self.update_selections(vec![selection], None, cx); + + if self.focused && self.leader_replica_id.is_none() { + self.buffer.update(cx, |buffer, cx| { + buffer.set_active_selections(&self.selections.disjoint_anchors(), cx) + }); + } + + let display_map = self + .display_map + .update(cx, |display_map, cx| display_map.snapshot(cx)); + let buffer = &display_map.buffer_snapshot; + self.add_selections_state = None; + self.select_next_state = None; + self.select_larger_syntax_node_stack.clear(); + self.autoclose_stack + .invalidate(&self.selections.disjoint_anchors(), &buffer); + self.snippet_stack + .invalidate(&self.selections.disjoint_anchors(), &buffer); + self.take_rename(false, cx); + + let new_cursor_position = self.selections.newest_anchor().head(); + + self.push_to_nav_history( + old_cursor_position.clone(), + Some(new_cursor_position.to_point(&buffer)), + cx, + ); + + if local { + let completion_menu = match self.context_menu.as_mut() { + Some(ContextMenu::Completions(menu)) => Some(menu), + _ => { + self.context_menu.take(); + None + } + }; + + if let Some(completion_menu) = completion_menu { + let cursor_position = new_cursor_position.to_offset(&buffer); + let (word_range, kind) = + buffer.surrounding_word(completion_menu.initial_position.clone()); + if kind == Some(CharKind::Word) + && word_range.to_inclusive().contains(&cursor_position) + { + let query = Self::completion_query(&buffer, cursor_position); + cx.background() + .block(completion_menu.filter(query.as_deref(), cx.background().clone())); + self.show_completions(&ShowCompletions, cx); + } else { + self.hide_context_menu(cx); + } + } + + if old_cursor_position.to_display_point(&display_map).row() + != new_cursor_position.to_display_point(&display_map).row() + { + self.available_code_actions.take(); + } + self.refresh_code_actions(cx); + self.refresh_document_highlights(cx); + } + + self.pause_cursor_blinking(cx); + cx.emit(Event::SelectionsChanged { local }); + cx.notify(); + + result } pub fn display_selections( @@ -1342,61 +1397,14 @@ impl Editor { ) -> (DisplaySnapshot, Vec>) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let selections = self - .local_selections::(cx) + .selections + .interleaved::(&display_map.buffer_snapshot) .into_iter() .map(|selection| selection.map(|point| point.to_display_point(&display_map))) .collect(); (display_map, selections) } - pub fn move_selections( - &mut self, - cx: &mut ViewContext, - mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection), - ) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .local_selections::(cx) - .into_iter() - .map(|selection| { - let mut selection = selection.map(|point| point.to_display_point(&display_map)); - move_selection(&display_map, &mut selection); - selection.map(|display_point| display_point.to_point(&display_map)) - }) - .collect(); - self.update_selections(selections, Some(Autoscroll::Fit), cx); - } - - pub fn move_selection_heads( - &mut self, - cx: &mut ViewContext, - mut update_head: impl FnMut( - &DisplaySnapshot, - DisplayPoint, - SelectionGoal, - ) -> (DisplayPoint, SelectionGoal), - ) { - self.move_selections(cx, |map, selection| { - let (new_head, new_goal) = update_head(map, selection.head(), selection.goal); - selection.set_head(new_head, new_goal); - }); - } - - pub fn move_cursors( - &mut self, - cx: &mut ViewContext, - mut update_cursor_position: impl FnMut( - &DisplaySnapshot, - DisplayPoint, - SelectionGoal, - ) -> (DisplayPoint, SelectionGoal), - ) { - self.move_selections(cx, |map, selection| { - let (cursor, new_goal) = update_cursor_position(map, selection.head(), selection.goal); - selection.collapse_to(cursor, new_goal) - }); - } - pub fn edit(&mut self, edits: I, cx: &mut ViewContext) where I: IntoIterator, T)>, @@ -1450,29 +1458,34 @@ impl Editor { ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let tail = self - .newest_selection_with_snapshot::(&display_map.buffer_snapshot) + .selections + .newest::(&display_map.buffer_snapshot) .tail(); self.begin_selection(position, false, click_count, cx); let position = position.to_offset(&display_map, Bias::Left); let tail_anchor = display_map.buffer_snapshot.anchor_before(tail); - let mut pending = self.pending_selection.clone().unwrap(); - if position >= tail { - pending.selection.start = tail_anchor.clone(); - } else { - pending.selection.end = tail_anchor.clone(); - pending.selection.reversed = true; - } + self.change_selections(true, cx, |s| { + let mut pending = s + .pending_mut() + .as_mut() + .expect("extend_selection not called with pending selection"); - match &mut pending.mode { - SelectMode::Word(range) | SelectMode::Line(range) => { - *range = tail_anchor.clone()..tail_anchor + if position >= tail { + pending.selection.start = tail_anchor.clone(); + } else { + pending.selection.end = tail_anchor.clone(); + pending.selection.reversed = true; } - _ => {} - } - self.set_selections(self.selections.clone(), Some(pending), true, cx); + match &mut pending.mode { + SelectMode::Word(range) | SelectMode::Line(range) => { + *range = tail_anchor.clone()..tail_anchor + } + _ => {} + } + }); } fn begin_selection( @@ -1489,7 +1502,7 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let newest_selection = self.newest_anchor_selection().clone(); + let newest_selection = self.selections.newest_anchor().clone(); let position = display_map.clip_point(position, Bias::Left); let start; @@ -1527,38 +1540,17 @@ impl Editor { } } - let selection = Selection { - id: post_inc(&mut self.next_selection_id), - start, - end, - reversed: false, - goal: SelectionGoal::None, - }; - - let mut selections; - if add { - selections = self.selections.clone(); - // Remove the newest selection if it was added due to a previous mouse up - // within this multi-click. - if click_count > 1 { - selections = self - .selections - .iter() - .filter(|selection| selection.id != newest_selection.id) - .cloned() - .collect(); + self.change_selections(true, cx, |s| { + if add { + if click_count > 1 { + s.delete(newest_selection.id); + } + } else { + s.clear_disjoint(); } - } else { - selections = Arc::from([]); - } - self.set_selections( - selections, - Some(PendingSelection { selection, mode }), - true, - cx, - ); - cx.notify(); + s.set_pending_range(start..end, mode); + }); } fn begin_columnar_selection( @@ -1574,7 +1566,8 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let tail = self - .newest_selection_with_snapshot::(&display_map.buffer_snapshot) + .selections + .newest::(&display_map.buffer_snapshot) .tail(); self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail)); @@ -1599,14 +1592,14 @@ impl Editor { if let Some(tail) = self.columnar_selection_tail.as_ref() { let tail = tail.to_display_point(&display_map); self.select_columns(tail, position, overshoot, &display_map, cx); - } else if let Some(mut pending) = self.pending_selection.clone() { + } else if let Some(mut pending) = self.selections.pending_anchor().clone() { let buffer = self.buffer.read(cx).snapshot(cx); let head; let tail; - match &pending.mode { + match &self.selections.pending_mode().unwrap() { SelectMode::Character => { head = position.to_point(&display_map); - tail = pending.selection.tail().to_point(&buffer); + tail = pending.tail().to_point(&buffer); } SelectMode::Word(original_range) => { let original_display_range = original_range.start.to_display_point(&display_map) @@ -1662,15 +1655,18 @@ impl Editor { }; if head < tail { - pending.selection.start = buffer.anchor_before(head); - pending.selection.end = buffer.anchor_before(tail); - pending.selection.reversed = true; + pending.start = buffer.anchor_before(head); + pending.end = buffer.anchor_before(tail); + pending.reversed = true; } else { - pending.selection.start = buffer.anchor_before(tail); - pending.selection.end = buffer.anchor_before(head); - pending.selection.reversed = false; + pending.start = buffer.anchor_before(tail); + pending.end = buffer.anchor_before(head); + pending.reversed = false; } - self.set_selections(self.selections.clone(), Some(pending), true, cx); + + self.change_selections(true, cx, |s| { + s.pending_mut().as_mut().unwrap().selection = pending; + }); } else { log::error!("update_selection dispatched with no pending selection"); return; @@ -1682,9 +1678,14 @@ impl Editor { fn end_selection(&mut self, cx: &mut ViewContext) { self.columnar_selection_tail.take(); - if self.pending_selection.is_some() { - let selections = self.local_selections::(cx); - self.update_selections(selections, None, cx); + if self.selections.pending_anchor().is_some() { + let selections = self + .selections + .interleaved::(&self.buffer.read(cx).snapshot(cx)); + self.change_selections(true, cx, |s| { + s.select(selections, None); + s.clear_pending(); + }); } } @@ -1702,7 +1703,7 @@ impl Editor { let end_column = cmp::max(tail.column(), head.column() + overshoot); let reversed = start_column < tail.column(); - let selections = (start_row..=end_row) + let selection_ranges = (start_row..=end_row) .filter_map(|row| { if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) { let start = display_map @@ -1711,25 +1712,25 @@ impl Editor { let end = display_map .clip_point(DisplayPoint::new(row, end_column), Bias::Right) .to_point(&display_map); - Some(Selection { - id: post_inc(&mut self.next_selection_id), - start, - end, - reversed, - goal: SelectionGoal::None, - }) + if reversed { + Some(end..start) + } else { + Some(start..end) + } } else { None } }) .collect::>(); - self.update_selections(selections, None, cx); + self.change_selections(true, cx, |s| { + s.select_ranges(selection_ranges, None); + }); cx.notify(); } pub fn is_selecting(&self) -> bool { - self.pending_selection.is_some() || self.columnar_selection_tail.is_some() + self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some() } pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { @@ -1751,28 +1752,10 @@ impl Editor { return; } - if let Some(pending) = self.pending_selection.clone() { - let mut selections = self.selections.clone(); - if selections.is_empty() { - selections = Arc::from([pending.selection]); - } - self.set_selections(selections, None, true, cx); + if self.change_selections(true, cx, |s| s.try_cancel()) { self.request_autoscroll(Autoscroll::Fit, cx); return; } - - let mut oldest_selection = self.oldest_selection::(&cx); - if self.selection_count() > 1 { - self.update_selections(vec![oldest_selection], Some(Autoscroll::Fit), cx); - return; - } - - if !oldest_selection.is_empty() { - oldest_selection.start = oldest_selection.head().clone(); - oldest_selection.end = oldest_selection.head().clone(); - self.update_selections(vec![oldest_selection], Some(Autoscroll::Fit), cx); - return; - } } cx.propagate_action(); @@ -1783,7 +1766,8 @@ impl Editor { &self, cx: &AppContext, ) -> Vec> { - self.local_selections::(cx) + self.selections + .interleaved::(&self.buffer.read(cx).read(cx)) .iter() .map(|s| { if s.reversed { @@ -1801,12 +1785,9 @@ impl Editor { .display_map .update(cx, |display_map, cx| display_map.snapshot(cx)); self.selections + .disjoint_anchors() .iter() - .chain( - self.pending_selection - .as_ref() - .map(|pending| &pending.selection), - ) + .chain(self.selections.pending_anchor().as_ref()) .map(|s| { if s.reversed { s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map) @@ -1817,68 +1798,6 @@ impl Editor { .collect() } - pub fn select_ranges( - &mut self, - ranges: I, - autoscroll: Option, - cx: &mut ViewContext, - ) where - I: IntoIterator>, - T: ToOffset, - { - let buffer = self.buffer.read(cx).snapshot(cx); - let selections = ranges - .into_iter() - .map(|range| { - let mut start = range.start.to_offset(&buffer); - let mut end = range.end.to_offset(&buffer); - let reversed = if start > end { - mem::swap(&mut start, &mut end); - true - } else { - false - }; - Selection { - id: post_inc(&mut self.next_selection_id), - start, - end, - reversed, - goal: SelectionGoal::None, - } - }) - .collect::>(); - self.update_selections(selections, autoscroll, cx); - } - - #[cfg(any(test, feature = "test-support"))] - pub fn select_display_ranges<'a, T>(&mut self, ranges: T, cx: &mut ViewContext) - where - T: IntoIterator>, - { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = ranges - .into_iter() - .map(|range| { - let mut start = range.start; - let mut end = range.end; - let reversed = if start > end { - mem::swap(&mut start, &mut end); - true - } else { - false - }; - Selection { - id: post_inc(&mut self.next_selection_id), - start: start.to_point(&display_map), - end: end.to_point(&display_map), - reversed, - goal: SelectionGoal::None, - } - }) - .collect(); - self.update_selections(selections, None, cx); - } - pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext) { if !self.input_enabled { cx.propagate_action(); @@ -1900,8 +1819,8 @@ impl Editor { pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { - let selections = this.local_selections::(cx); let buffer = this.buffer.read(cx).snapshot(cx); + let selections = this.selections.interleaved::(&buffer); selections .iter() .map(|selection| { @@ -1947,9 +1866,12 @@ impl Editor { if insert_extra_newline { new_text = new_text.repeat(2); } + + let anchor = buffer.anchor_after(end); + let new_selection = selection.map(|_| anchor.clone()); ( (start..end, new_text), - (insert_extra_newline, buffer.anchor_after(end)), + (insert_extra_newline, new_selection), ) }) .unzip() @@ -1957,25 +1879,22 @@ impl Editor { this.buffer.update(cx, |buffer, cx| { buffer.edit_with_autoindent(edits, cx); + }); + let buffer = this.buffer.read(cx).snapshot(cx); + let new_selections = selection_fixup_info + .into_iter() + .map(|(extra_newline_inserted, new_selection)| { + let mut cursor = new_selection.end.to_point(&buffer); + if extra_newline_inserted { + cursor.row -= 1; + cursor.column = buffer.line_len(cursor.row); + } + new_selection.map(|_| cursor.clone()) + }) + .collect(); - let buffer = buffer.read(cx); - this.selections = this - .selections - .iter() - .cloned() - .zip(selection_fixup_info) - .map(|(mut new_selection, (extra_newline_inserted, end))| { - let mut cursor = end.to_point(&buffer); - if extra_newline_inserted { - cursor.row -= 1; - cursor.column = buffer.line_len(cursor.row); - } - let anchor = buffer.anchor_after(cursor); - new_selection.start = anchor.clone(); - new_selection.end = anchor; - new_selection - }) - .collect(); + this.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)) }); this.request_autoscroll(Autoscroll::Fit, cx); @@ -1985,7 +1904,9 @@ impl Editor { pub fn insert(&mut self, text: &str, cx: &mut ViewContext) { let text: Arc = text.into(); self.transact(cx, |this, cx| { - let old_selections = this.local_selections::(cx); + let old_selections = this + .selections + .interleaved::(&this.buffer.read(cx).snapshot(cx)); let selection_anchors = this.buffer.update(cx, |buffer, cx| { let anchors = { let snapshot = buffer.read(cx); @@ -2019,12 +1940,15 @@ impl Editor { }) .collect() }; - this.update_selections(selections, Some(Autoscroll::Fit), cx); + + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }) }); } fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext) { - let selection = self.newest_anchor_selection(); + let selection = self.selections.newest_anchor(); if self .buffer .read(cx) @@ -2044,51 +1968,52 @@ impl Editor { .cloned() { if self - .local_selections::(cx) + .selections + .interleaved::(&snapshot) .iter() .any(|selection| selection.is_empty()) { - false - } else { - let mut selections = self.selections.to_vec(); - for selection in &mut selections { - selection.end = selection.end.bias_left(&snapshot); - } - drop(snapshot); - - self.buffer.update(cx, |buffer, cx| { - let pair_start: Arc = pair.start.clone().into(); - buffer.edit( - selections - .iter() - .map(|s| (s.start.clone()..s.start.clone(), pair_start.clone())), - cx, - ); - let pair_end: Arc = pair.end.clone().into(); - buffer.edit( - selections - .iter() - .map(|s| (s.end.clone()..s.end.clone(), pair_end.clone())), - cx, - ); - }); - - let snapshot = self.buffer.read(cx).read(cx); - for selection in &mut selections { - selection.end = selection.end.bias_right(&snapshot); - } - drop(snapshot); - - self.set_selections(selections.into(), None, true, cx); - true + return false; } + + let mut selections = self.selections.disjoint_anchors().to_vec(); + for selection in &mut selections { + selection.end = selection.end.bias_left(&snapshot); + } + drop(snapshot); + + self.buffer.update(cx, |buffer, cx| { + let pair_start: Arc = pair.start.clone().into(); + let pair_end: Arc = pair.end.clone().into(); + buffer.edit( + selections + .iter() + .map(|s| (s.start.clone()..s.start.clone(), pair_start.clone())) + .chain( + selections + .iter() + .map(|s| (s.end.clone()..s.end.clone(), pair_end.clone())), + ), + cx, + ); + }); + + let snapshot = self.buffer.read(cx).read(cx); + for selection in &mut selections { + selection.end = selection.end.bias_right(&snapshot); + } + drop(snapshot); + + self.change_selections(true, cx, |s| s.select_anchors(selections, None)); + true } else { false } } fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); + let snapshot = self.buffer.read(cx).snapshot(cx); + let selections = self.selections.interleaved::(&snapshot); let mut bracket_pair_state = None; let mut new_selections = None; self.buffer.update(cx, |buffer, cx| { @@ -2155,7 +2080,7 @@ impl Editor { snapshot = buffer.snapshot(cx); new_selections = Some( - self.resolve_selections::(left_biased_selections.iter(), &snapshot) + resolve_multiple::(left_biased_selections.iter(), &snapshot) .collect::>(), ); @@ -2177,7 +2102,9 @@ impl Editor { }); if let Some(new_selections) = new_selections { - self.update_selections(new_selections, None, cx); + self.change_selections(true, cx, |s| { + s.select(new_selections, None); + }); } if let Some(bracket_pair_state) = bracket_pair_state { self.autoclose_stack.push(bracket_pair_state); @@ -2185,7 +2112,8 @@ impl Editor { } fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext) -> bool { - let old_selections = self.local_selections::(cx); + let buffer = self.buffer.read(cx).snapshot(cx); + let old_selections = self.selections.interleaved::(&buffer); let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() { autoclose_pair } else { @@ -2197,7 +2125,6 @@ impl Editor { debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len()); - let buffer = self.buffer.read(cx).snapshot(cx); if old_selections .iter() .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer))) @@ -2220,7 +2147,9 @@ impl Editor { }) .collect(); self.autoclose_stack.pop(); - self.update_selections(new_selections, Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)); + }); true } else { false @@ -2252,7 +2181,7 @@ impl Editor { return; }; - let position = self.newest_anchor_selection().head(); + let position = self.selections.newest_anchor().head(); let (buffer, buffer_position) = if let Some(output) = self .buffer .read(cx) @@ -2357,8 +2286,10 @@ impl Editor { let old_range = completion.old_range.to_offset(&buffer); let old_text = buffer.text_for_range(old_range.clone()).collect::(); - let selections = self.local_selections::(cx); - let newest_selection = self.newest_anchor_selection(); + let selections = self + .selections + .interleaved::(&self.buffer.read(cx).snapshot(cx)); + let newest_selection = self.selections.newest_anchor(); if newest_selection.start.buffer_id != Some(buffer_handle.id()) { return None; } @@ -2524,7 +2455,7 @@ impl Editor { editor .buffer() .read(cx) - .excerpt_containing(editor.newest_anchor_selection().head(), cx) + .excerpt_containing(editor.selections.newest_anchor().head(), cx) }); if let Some((excerpted_buffer, excerpt_range)) = excerpt { if excerpted_buffer == *buffer { @@ -2585,7 +2516,7 @@ impl Editor { fn refresh_code_actions(&mut self, cx: &mut ViewContext) -> Option<()> { let project = self.project.as_ref()?; let buffer = self.buffer.read(cx); - let newest_selection = self.newest_anchor_selection().clone(); + let newest_selection = self.selections.newest_anchor().clone(); let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?; let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?; if start_buffer != end_buffer { @@ -2620,7 +2551,7 @@ impl Editor { let project = self.project.as_ref()?; let buffer = self.buffer.read(cx); - let newest_selection = self.newest_anchor_selection().clone(); + let newest_selection = self.selections.newest_anchor().clone(); let cursor_position = newest_selection.head(); let (cursor_buffer, cursor_buffer_position) = buffer.text_anchor_for_position(cursor_position.clone(), cx)?; @@ -2808,7 +2739,9 @@ impl Editor { }); if let Some(tabstop) = tabstops.first() { - self.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit)); + }); self.snippet_stack.push(SnippetState { active_index: 0, ranges: tabstops, @@ -2827,14 +2760,13 @@ impl Editor { } pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext) -> bool { - let buffer = self.buffer.read(cx).snapshot(cx); - - if let Some(snippet) = self.snippet_stack.last_mut() { + if let Some(mut snippet) = self.snippet_stack.pop() { match bias { Bias::Left => { if snippet.active_index > 0 { snippet.active_index -= 1; } else { + self.snippet_stack.push(snippet); return false; } } @@ -2842,34 +2774,25 @@ impl Editor { if snippet.active_index + 1 < snippet.ranges.len() { snippet.active_index += 1; } else { + self.snippet_stack.push(snippet); return false; } } } if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) { - let new_selections = current_ranges - .iter() - .map(|new_range| { - let new_range = new_range.to_offset(&buffer); - Selection { - id: post_inc(&mut self.next_selection_id), - start: new_range.start, - end: new_range.end, - reversed: false, - goal: SelectionGoal::None, - } - }) - .collect(); + self.change_selections(true, cx, |s| { + s.select_anchor_ranges( + current_ranges.into_iter().cloned(), + Some(Autoscroll::Fit), + ) + }); - // Remove the snippet state when moving to the last tabstop. - if snippet.active_index + 1 == snippet.ranges.len() { - self.snippet_stack.pop(); + // If snippet state is not at the last tabstop, push it back on the stack + if snippet.active_index + 1 != snippet.ranges.len() { + self.snippet_stack.push(snippet); } - - self.update_selections(new_selections, Some(Autoscroll::Fit), cx); return true; } - self.snippet_stack.pop(); } false @@ -2883,8 +2806,10 @@ impl Editor { } pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { - let mut selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let mut selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); for selection in &mut selections { if selection.is_empty() { let old_head = selection.head(); @@ -2911,18 +2836,20 @@ impl Editor { } self.transact(cx, |this, cx| { - this.update_selections(selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| s.select(selections, Some(Autoscroll::Fit))); this.insert("", cx); }); } pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - this.move_selections(cx, |map, selection| { - if selection.is_empty() { - let cursor = movement::right(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } + this.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() { + let cursor = movement::right(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } + }) }); this.insert(&"", cx); }); @@ -2941,7 +2868,9 @@ impl Editor { return; } - let mut selections = self.local_selections::(cx); + let mut selections = self + .selections + .interleaved::(&self.buffer.read(cx).read(cx)); if selections.iter().all(|s| s.is_empty()) { self.transact(cx, |this, cx| { this.buffer.update(cx, |buffer, cx| { @@ -2966,7 +2895,9 @@ impl Editor { selection.end = selection.start; } }); - this.update_selections(selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); }); } else { self.indent(&Indent, cx); @@ -2974,7 +2905,9 @@ impl Editor { } pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext) { - let mut selections = self.local_selections::(cx); + let mut selections = self + .selections + .interleaved::(&self.buffer.read(cx).read(cx)); self.transact(cx, |this, cx| { let mut last_indent = None; this.buffer.update(cx, |buffer, cx| { @@ -3029,13 +2962,17 @@ impl Editor { } }); - this.update_selections(selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); }); } pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); let mut deletion_ranges = Vec::new(); let mut last_outdent = None; { @@ -3078,18 +3015,18 @@ impl Editor { cx, ); }); - this.update_selections( - this.local_selections::(cx), - Some(Autoscroll::Fit), - cx, - ); + let snapshot = this.buffer.read(cx).snapshot(cx); + this.change_selections(true, cx, |s| { + s.select(s.interleaved::(&snapshot), Some(Autoscroll::Fit)) + }); }); } pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let buffer = self.buffer.read(cx).snapshot(cx); + let selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); let mut new_cursors = Vec::new(); let mut edit_ranges = Vec::new(); @@ -3109,6 +3046,7 @@ impl Editor { } } + let buffer = &display_map.buffer_snapshot; let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer); let edit_end; let cursor_buffer_row; @@ -3160,14 +3098,17 @@ impl Editor { } }) .collect(); - this.update_selections(new_selections, Some(Autoscroll::Fit), cx); + + this.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)); + }); }); } pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; + let selections = self.selections.interleaved::(buffer); let mut edits = Vec::new(); let mut selections_iter = selections.iter().peekable(); @@ -3212,7 +3153,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.local_selections::(cx); + let selections = self.selections.interleaved::(&buffer); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3310,7 +3251,9 @@ impl Editor { } }); this.fold_ranges(refold_ranges, cx); - this.update_selections(new_selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)); + }) }); } @@ -3322,7 +3265,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.local_selections::(cx); + let selections = self.selections.interleaved::(&buffer); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3413,62 +3356,68 @@ impl Editor { } }); this.fold_ranges(refold_ranges, cx); - this.update_selections(new_selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)) + }); }); } pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - let mut edits: Vec<(Range, String)> = Default::default(); - this.move_selections(cx, |display_map, selection| { - if !selection.is_empty() { - return; - } + let edits = this.change_selections(true, cx, |s| { + let mut edits: Vec<(Range, String)> = Default::default(); + s.move_with(|display_map, selection| { + if !selection.is_empty() { + return; + } - let mut head = selection.head(); - let mut transpose_offset = head.to_offset(display_map, Bias::Right); - if head.column() == display_map.line_len(head.row()) { - transpose_offset = display_map + let mut head = selection.head(); + let mut transpose_offset = head.to_offset(display_map, Bias::Right); + if head.column() == display_map.line_len(head.row()) { + transpose_offset = display_map + .buffer_snapshot + .clip_offset(transpose_offset.saturating_sub(1), Bias::Left); + } + + if transpose_offset == 0 { + return; + } + + *head.column_mut() += 1; + head = display_map.clip_point(head, Bias::Right); + selection.collapse_to(head, SelectionGoal::Column(head.column())); + + let transpose_start = display_map .buffer_snapshot .clip_offset(transpose_offset.saturating_sub(1), Bias::Left); - } - - if transpose_offset == 0 { - return; - } - - *head.column_mut() += 1; - head = display_map.clip_point(head, Bias::Right); - selection.collapse_to(head, SelectionGoal::Column(head.column())); - - let transpose_start = display_map - .buffer_snapshot - .clip_offset(transpose_offset.saturating_sub(1), Bias::Left); - if edits.last().map_or(true, |e| e.0.end <= transpose_start) { - let transpose_end = display_map - .buffer_snapshot - .clip_offset(transpose_offset + 1, Bias::Right); - if let Some(ch) = display_map.buffer_snapshot.chars_at(transpose_start).next() { - edits.push((transpose_start..transpose_offset, String::new())); - edits.push((transpose_end..transpose_end, ch.to_string())); + if edits.last().map_or(true, |e| e.0.end <= transpose_start) { + let transpose_end = display_map + .buffer_snapshot + .clip_offset(transpose_offset + 1, Bias::Right); + if let Some(ch) = + display_map.buffer_snapshot.chars_at(transpose_start).next() + { + edits.push((transpose_start..transpose_offset, String::new())); + edits.push((transpose_end..transpose_end, ch.to_string())); + } } - } + }); + edits }); this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); - this.update_selections( - this.local_selections::(cx), - Some(Autoscroll::Fit), - cx, - ); + let buffer = this.buffer.read(cx).snapshot(cx); + this.change_selections(true, cx, |s| { + s.select(s.interleaved::(&buffer), Some(Autoscroll::Fit)); + }); }); } pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext) { let mut text = String::new(); - let mut selections = self.local_selections::(cx); + let buffer = self.buffer.read(cx).snapshot(cx); + let mut selections = self.selections.interleaved::(&buffer); let mut clipboard_selections = Vec::with_capacity(selections.len()); { - let buffer = self.buffer.read(cx).read(cx); let max_point = buffer.max_point(); for selection in &mut selections { let is_entire_line = selection.is_empty(); @@ -3490,18 +3439,20 @@ impl Editor { } self.transact(cx, |this, cx| { - this.update_selections(selections, Some(Autoscroll::Fit), cx); + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); this.insert("", cx); cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections)); }); } pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); + let buffer = self.buffer.read(cx).read(cx); + let selections = self.selections.interleaved::(&buffer); let mut text = String::new(); let mut clipboard_selections = Vec::with_capacity(selections.len()); { - let buffer = self.buffer.read(cx).read(cx); let max_point = buffer.max_point(); for selection in selections.iter() { let mut start = selection.start; @@ -3531,7 +3482,9 @@ impl Editor { if let Some(item) = cx.as_mut().read_from_clipboard() { let mut clipboard_text = Cow::Borrowed(item.text()); if let Some(mut clipboard_selections) = item.metadata::>() { - let old_selections = this.local_selections::(cx); + let old_selections = this + .selections + .interleaved::(&this.buffer.read(cx).read(cx)); let all_selections_were_entire_line = clipboard_selections.iter().all(|s| s.is_entire_line); if clipboard_selections.len() != old_selections.len() { @@ -3584,8 +3537,12 @@ impl Editor { buffer.edit_with_autoindent(edits, cx); }); - let selections = this.local_selections::(cx); - this.update_selections(selections, Some(Autoscroll::Fit), cx); + let selections = this + .selections + .interleaved::(&this.buffer.read(cx).read(cx)); + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)) + }); } else { this.insert(&clipboard_text, cx); } @@ -3596,7 +3553,11 @@ impl Editor { pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext) { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) { if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() { - self.set_selections(selections, None, true, cx); + self.change_selections(true, cx, |s| { + // TODO: move to SelectionsCollection to preserve selection + // invariants without rechecking + s.select_anchors(selections.to_vec(), None); + }); } self.request_autoscroll(Autoscroll::Fit, cx); cx.emit(Event::Edited); @@ -3607,7 +3568,11 @@ impl Editor { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) { if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned() { - self.set_selections(selections, None, true, cx); + self.change_selections(true, cx, |s| { + // TODO: move to SelectionsCollection to preserve selection + // invariants without rechecking + s.select_anchors(selections.to_vec(), None); + }); } self.request_autoscroll(Autoscroll::Fit, cx); cx.emit(Event::Edited); @@ -3620,37 +3585,41 @@ impl Editor { } pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { - self.move_selections(cx, |map, selection| { - let cursor = if selection.is_empty() { - movement::left(map, selection.start) - } else { - selection.start - }; - selection.collapse_to(cursor, SelectionGoal::None); - }); + self.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + let cursor = if selection.is_empty() { + movement::left(map, selection.start) + } else { + selection.start + }; + selection.collapse_to(cursor, SelectionGoal::None); + }); + }) } pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext) { - self.move_selection_heads(cx, |map, head, _| { - (movement::left(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None)); + }) } pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext) { - self.move_selections(cx, |map, selection| { - let cursor = if selection.is_empty() { - movement::right(map, selection.end) - } else { - selection.end - }; - selection.collapse_to(cursor, SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + let cursor = if selection.is_empty() { + movement::right(map, selection.end) + } else { + selection.end + }; + selection.collapse_to(cursor, SelectionGoal::None) + }); + }) } pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext) { - self.move_selection_heads(cx, |map, head, _| { - (movement::right(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None)); + }) } pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { @@ -3669,17 +3638,21 @@ impl Editor { return; } - self.move_selections(cx, |map, selection| { - if !selection.is_empty() { - selection.goal = SelectionGoal::None; - } - let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false); - selection.collapse_to(cursor, goal); - }); + self.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if !selection.is_empty() { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false); + selection.collapse_to(cursor, goal); + }); + }) } pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext) { - self.move_selection_heads(cx, |map, head, goal| movement::up(map, head, goal, false)) + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false)) + }) } pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext) { @@ -3696,17 +3669,21 @@ impl Editor { return; } - self.move_selections(cx, |map, selection| { - if !selection.is_empty() { - selection.goal = SelectionGoal::None; - } - let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false); - selection.collapse_to(cursor, goal); + self.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if !selection.is_empty() { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false); + selection.collapse_to(cursor, goal); + }); }); } pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext) { - self.move_selection_heads(cx, |map, head, goal| movement::down(map, head, goal, false)) + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false)) + }); } pub fn move_to_previous_word_start( @@ -3714,12 +3691,14 @@ impl Editor { _: &MoveToPreviousWordStart, cx: &mut ViewContext, ) { - self.move_cursors(cx, |map, head, _| { - ( - movement::previous_word_start(map, head), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + ( + movement::previous_word_start(map, head), + SelectionGoal::None, + ) + }); + }) } pub fn move_to_previous_subword_start( @@ -3727,12 +3706,14 @@ impl Editor { _: &MoveToPreviousSubwordStart, cx: &mut ViewContext, ) { - self.move_cursors(cx, |map, head, _| { - ( - movement::previous_subword_start(map, head), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + ( + movement::previous_subword_start(map, head), + SelectionGoal::None, + ) + }); + }) } pub fn select_to_previous_word_start( @@ -3740,12 +3721,14 @@ impl Editor { _: &SelectToPreviousWordStart, cx: &mut ViewContext, ) { - self.move_selection_heads(cx, |map, head, _| { - ( - movement::previous_word_start(map, head), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + ( + movement::previous_word_start(map, head), + SelectionGoal::None, + ) + }); + }) } pub fn select_to_previous_subword_start( @@ -3753,12 +3736,14 @@ impl Editor { _: &SelectToPreviousSubwordStart, cx: &mut ViewContext, ) { - self.move_selection_heads(cx, |map, head, _| { - ( - movement::previous_subword_start(map, head), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + ( + movement::previous_subword_start(map, head), + SelectionGoal::None, + ) + }); + }) } pub fn delete_to_previous_word_start( @@ -3767,11 +3752,13 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.move_selections(cx, |map, selection| { - if selection.is_empty() { - let cursor = movement::previous_word_start(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } + this.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() { + let cursor = movement::previous_word_start(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } + }); }); this.insert("", cx); }); @@ -3783,20 +3770,24 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.move_selections(cx, |map, selection| { - if selection.is_empty() { - let cursor = movement::previous_subword_start(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } + this.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() { + let cursor = movement::previous_subword_start(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } + }); }); this.insert("", cx); }); } pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext) { - self.move_cursors(cx, |map, head, _| { - (movement::next_word_end(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + (movement::next_word_end(map, head), SelectionGoal::None) + }); + }) } pub fn move_to_next_subword_end( @@ -3804,15 +3795,19 @@ impl Editor { _: &MoveToNextSubwordEnd, cx: &mut ViewContext, ) { - self.move_cursors(cx, |map, head, _| { - (movement::next_subword_end(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + (movement::next_subword_end(map, head), SelectionGoal::None) + }); + }) } pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext) { - self.move_selection_heads(cx, |map, head, _| { - (movement::next_word_end(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + (movement::next_word_end(map, head), SelectionGoal::None) + }); + }) } pub fn select_to_next_subword_end( @@ -3820,18 +3815,22 @@ impl Editor { _: &SelectToNextSubwordEnd, cx: &mut ViewContext, ) { - self.move_selection_heads(cx, |map, head, _| { - (movement::next_subword_end(map, head), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + (movement::next_subword_end(map, head), SelectionGoal::None) + }); + }) } pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - this.move_selections(cx, |map, selection| { - if selection.is_empty() { - let cursor = movement::next_word_end(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } + this.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() { + let cursor = movement::next_word_end(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } + }); }); this.insert("", cx); }); @@ -3843,11 +3842,13 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.move_selections(cx, |map, selection| { - if selection.is_empty() { - let cursor = movement::next_subword_end(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } + this.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() { + let cursor = movement::next_subword_end(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } + }); }); this.insert("", cx); }); @@ -3858,12 +3859,14 @@ impl Editor { _: &MoveToBeginningOfLine, cx: &mut ViewContext, ) { - self.move_cursors(cx, |map, head, _| { - ( - movement::line_beginning(map, head, true), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + ( + movement::line_beginning(map, head, true), + SelectionGoal::None, + ) + }); + }) } pub fn select_to_beginning_of_line( @@ -3871,11 +3874,13 @@ impl Editor { action: &SelectToBeginningOfLine, cx: &mut ViewContext, ) { - self.move_selection_heads(cx, |map, head, _| { - ( - movement::line_beginning(map, head, action.stop_at_soft_wraps), - SelectionGoal::None, - ) + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + ( + movement::line_beginning(map, head, action.stop_at_soft_wraps), + SelectionGoal::None, + ) + }); }); } @@ -3885,8 +3890,10 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.move_selections(cx, |_, selection| { - selection.reversed = true; + this.change_selections(true, cx, |s| { + s.move_with(|_, selection| { + selection.reversed = true; + }); }); this.select_to_beginning_of_line( @@ -3900,9 +3907,11 @@ impl Editor { } pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext) { - self.move_cursors(cx, |map, head, _| { - (movement::line_end(map, head, true), SelectionGoal::None) - }); + self.change_selections(true, cx, |s| { + s.move_cursors_with(|map, head, _| { + (movement::line_end(map, head, true), SelectionGoal::None) + }); + }) } pub fn select_to_end_of_line( @@ -3910,12 +3919,14 @@ impl Editor { action: &SelectToEndOfLine, cx: &mut ViewContext, ) { - self.move_selection_heads(cx, |map, head, _| { - ( - movement::line_end(map, head, action.stop_at_soft_wraps), - SelectionGoal::None, - ) - }); + self.change_selections(true, cx, |s| { + s.move_heads_with(|map, head, _| { + ( + movement::line_end(map, head, action.stop_at_soft_wraps), + SelectionGoal::None, + ) + }); + }) } pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext) { @@ -3948,20 +3959,20 @@ impl Editor { return; } - let selection = Selection { - id: post_inc(&mut self.next_selection_id), - start: 0, - end: 0, - reversed: false, - goal: SelectionGoal::None, - }; - self.update_selections(vec![selection], Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select_ranges(vec![0..0], Some(Autoscroll::Fit)); + }); } pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext) { - let mut selection = self.local_selections::(cx).last().unwrap().clone(); + let mut selection = self + .selections + .last::(&self.buffer.read(cx).read(cx)); selection.set_head(Point::zero(), SelectionGoal::None); - self.update_selections(vec![selection], Some(Autoscroll::Fit), cx); + + self.change_selections(true, cx, |s| { + s.select(vec![selection], Some(Autoscroll::Fit)); + }); } pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext) { @@ -3971,14 +3982,9 @@ impl Editor { } let cursor = self.buffer.read(cx).read(cx).len(); - let selection = Selection { - id: post_inc(&mut self.next_selection_id), - start: cursor, - end: cursor, - reversed: false, - goal: SelectionGoal::None, - }; - self.update_selections(vec![selection], Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select_ranges(vec![cursor..cursor], Some(Autoscroll::Fit)) + }); } pub fn set_nav_history(&mut self, nav_history: Option) { @@ -4019,25 +4025,26 @@ impl Editor { } pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext) { - let mut selection = self.local_selections::(cx).first().unwrap().clone(); - selection.set_head(self.buffer.read(cx).read(cx).len(), SelectionGoal::None); - self.update_selections(vec![selection], Some(Autoscroll::Fit), cx); + let buffer = self.buffer.read(cx).snapshot(cx); + let mut selection = self.selections.first::(&buffer); + selection.set_head(buffer.len(), SelectionGoal::None); + self.change_selections(true, cx, |s| { + s.select(vec![selection], Some(Autoscroll::Fit)); + }); } pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext) { - let selection = Selection { - id: post_inc(&mut self.next_selection_id), - start: 0, - end: self.buffer.read(cx).read(cx).len(), - reversed: false, - goal: SelectionGoal::None, - }; - self.update_selections(vec![selection], None, cx); + let end = self.buffer.read(cx).read(cx).len(); + self.change_selections(true, cx, |s| { + s.select_ranges(vec![0..end], Some(Autoscroll::Fit)); + }); } pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.local_selections::(cx); + let mut selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); let max_point = display_map.buffer_snapshot.max_point(); for selection in &mut selections { let rows = selection.spanned_rows(true, &display_map); @@ -4045,7 +4052,9 @@ impl Editor { selection.end = cmp::min(max_point, Point::new(rows.end, 0)); selection.reversed = false; } - self.update_selections(selections, Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); } pub fn split_selection_into_lines( @@ -4054,33 +4063,23 @@ impl Editor { cx: &mut ViewContext, ) { let mut to_unfold = Vec::new(); - let mut new_selections = Vec::new(); + let mut new_selection_ranges = Vec::new(); { - let selections = self.local_selections::(cx); let buffer = self.buffer.read(cx).read(cx); + let selections = self.selections.interleaved::(&buffer); for selection in selections { for row in selection.start.row..selection.end.row { let cursor = Point::new(row, buffer.line_len(row)); - new_selections.push(Selection { - id: post_inc(&mut self.next_selection_id), - start: cursor, - end: cursor, - reversed: false, - goal: SelectionGoal::None, - }); + new_selection_ranges.push(cursor..cursor); } - new_selections.push(Selection { - id: selection.id, - start: selection.end, - end: selection.end, - reversed: false, - goal: SelectionGoal::None, - }); + new_selection_ranges.push(selection.end..selection.end); to_unfold.push(selection.start..selection.end); } } self.unfold_ranges(to_unfold, true, cx); - self.update_selections(new_selections, Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select_ranges(new_selection_ranges, Some(Autoscroll::Fit)); + }); } pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext) { @@ -4094,7 +4093,9 @@ impl Editor { fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.local_selections::(cx); + let mut selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); let mut state = self.add_selections_state.take().unwrap_or_else(|| { let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone(); let range = oldest_selection.display_range(&display_map).sorted(); @@ -4179,7 +4180,9 @@ impl Editor { state.stack.pop(); } - self.update_selections(new_selections, Some(Autoscroll::Newest), cx); + self.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)); + }); if state.stack.len() > 1 { self.add_selections_state = Some(state); } @@ -4189,7 +4192,7 @@ impl Editor { self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let mut selections = self.local_selections::(cx); + let mut selections = self.selections.interleaved::(&buffer); if let Some(mut select_next_state) = self.select_next_state.take() { let query = &select_next_state.query; if !select_next_state.done { @@ -4225,22 +4228,13 @@ impl Editor { } if let Some(next_selected_range) = next_selected_range { - if action.replace_newest { - if let Some(newest_id) = - selections.iter().max_by_key(|s| s.id).map(|s| s.id) - { - selections.retain(|s| s.id != newest_id); + self.unfold_ranges([next_selected_range.clone()], false, cx); + self.change_selections(true, cx, |s| { + if action.replace_newest { + s.delete(s.newest_anchor().id); } - } - selections.push(Selection { - id: post_inc(&mut self.next_selection_id), - start: next_selected_range.start, - end: next_selected_range.end, - reversed: false, - goal: SelectionGoal::None, + s.insert_range(next_selected_range, Some(Autoscroll::Newest)); }); - self.unfold_ranges([next_selected_range], false, cx); - self.update_selections(selections, Some(Autoscroll::Newest), cx); } else { select_next_state.done = true; } @@ -4268,7 +4262,9 @@ impl Editor { done: false, }; self.unfold_ranges([selection.start..selection.end], false, cx); - self.update_selections(selections, Some(Autoscroll::Newest), cx); + self.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Newest)); + }); self.select_next_state = Some(select_state); } else { let query = buffer @@ -4286,7 +4282,9 @@ impl Editor { pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - let mut selections = this.local_selections::(cx); + let mut selections = this + .selections + .interleaved::(&this.buffer.read(cx).snapshot(cx)); let mut all_selection_lines_are_comments = true; let mut edit_ranges = Vec::new(); let mut last_toggled_row = None; @@ -4387,11 +4385,12 @@ impl Editor { } }); - this.update_selections( - this.local_selections::(cx), - Some(Autoscroll::Fit), - cx, - ); + let selections = this + .selections + .interleaved::(&this.buffer.read(cx).read(cx)); + this.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); }); } @@ -4400,9 +4399,12 @@ impl Editor { _: &SelectLargerSyntaxNode, cx: &mut ViewContext, ) { - let old_selections = self.local_selections::(cx).into_boxed_slice(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); + let old_selections = self + .selections + .interleaved::(&buffer) + .into_boxed_slice(); let mut stack = mem::take(&mut self.select_larger_syntax_node_stack); let mut selected_larger_node = false; @@ -4435,7 +4437,9 @@ impl Editor { if selected_larger_node { stack.push(old_selections); - self.update_selections(new_selections, Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select(new_selections, Some(Autoscroll::Fit)); + }); } self.select_larger_syntax_node_stack = stack; } @@ -4447,7 +4451,9 @@ impl Editor { ) { let mut stack = mem::take(&mut self.select_larger_syntax_node_stack); if let Some(selections) = stack.pop() { - self.update_selections(selections.to_vec(), Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select(selections.to_vec(), Some(Autoscroll::Fit)); + }); } self.select_larger_syntax_node_stack = stack; } @@ -4457,8 +4463,8 @@ impl Editor { _: &MoveToEnclosingBracket, cx: &mut ViewContext, ) { - let mut selections = self.local_selections::(cx); let buffer = self.buffer.read(cx).snapshot(cx); + let mut selections = self.selections.interleaved::(&buffer); for selection in &mut selections { if let Some((open_range, close_range)) = buffer.enclosing_bracket_ranges(selection.start..selection.end) @@ -4476,14 +4482,20 @@ impl Editor { } } - self.update_selections(selections, Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select(selections, Some(Autoscroll::Fit)); + }); } pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext) { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Undoing; if let Some(entry) = self.selection_history.undo_stack.pop_back() { - self.set_selections(entry.selections, None, true, cx); + self.change_selections(true, cx, |s| { + // TODO: Move to selections so selections invariants can be preserved rather than + // rechecking them. + s.select_anchors(entry.selections.to_vec(), None) + }); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::Newest, cx); @@ -4495,7 +4507,11 @@ impl Editor { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Redoing; if let Some(entry) = self.selection_history.redo_stack.pop_back() { - self.set_selections(entry.selections, None, true, cx); + self.change_selections(true, cx, |s| { + // TODO: Move to selections so selections invariants can be preserved rather than + // rechecking them. + s.select_anchors(entry.selections.to_vec(), None) + }); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::Newest, cx); @@ -4513,7 +4529,7 @@ impl Editor { pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext) { let buffer = self.buffer.read(cx).snapshot(cx); - let selection = self.newest_selection_with_snapshot::(&buffer); + let selection = self.selections.newest::(&buffer); let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| { active_diagnostics .primary_range @@ -4550,17 +4566,18 @@ impl Editor { if let Some((primary_range, group_id)) = group { self.activate_diagnostics(group_id, cx); - self.update_selections( - vec![Selection { - id: selection.id, - start: primary_range.start, - end: primary_range.start, - reversed: false, - goal: SelectionGoal::None, - }], - Some(Autoscroll::Center), - cx, - ); + self.change_selections(true, cx, |s| { + s.select( + vec![Selection { + id: selection.id, + start: primary_range.start, + end: primary_range.start, + reversed: false, + goal: SelectionGoal::None, + }], + Some(Autoscroll::Center), + ); + }); break; } else { // Cycle around to the start of the buffer, potentially moving back to the start of @@ -4599,13 +4616,13 @@ impl Editor { }; let editor = editor_handle.read(cx); - let head = editor.newest_selection::(cx).head(); - let (buffer, head) = - if let Some(text_anchor) = editor.buffer.read(cx).text_anchor_for_position(head, cx) { - text_anchor - } else { - return; - }; + let buffer = editor.buffer.read(cx); + let head = editor.selections.newest::(&buffer.read(cx)).head(); + let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) { + text_anchor + } else { + return; + }; let project = workspace.project().clone(); let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx)); @@ -4623,7 +4640,10 @@ impl Editor { if editor_handle != target_editor_handle { nav_history.borrow_mut().disable(); } - target_editor.select_ranges([range], Some(Autoscroll::Center), cx); + target_editor.change_selections(true, cx, |s| { + s.select_ranges([range], Some(Autoscroll::Center)); + }); + nav_history.borrow_mut().enable(); }); } @@ -4643,8 +4663,9 @@ impl Editor { let editor_handle = active_item.act_as::(cx)?; let editor = editor_handle.read(cx); - let head = editor.newest_selection::(cx).head(); - let (buffer, head) = editor.buffer.read(cx).text_anchor_for_position(head, cx)?; + let buffer = editor.buffer.read(cx); + let head = editor.selections.newest::(&buffer.read(cx)).head(); + let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; let replica_id = editor.replica_id(cx); let project = workspace.project().clone(); @@ -4712,7 +4733,7 @@ impl Editor { use language::ToOffset as _; let project = self.project.clone()?; - let selection = self.newest_anchor_selection().clone(); + let selection = self.selections.newest_anchor().clone(); let (cursor_buffer, cursor_buffer_position) = self .buffer .read(cx) @@ -4901,8 +4922,12 @@ impl Editor { self.show_local_selections = true; if moving_cursor { - let cursor_in_rename_editor = - rename.editor.read(cx).newest_selection::(cx).head(); + let rename_editor = rename.editor.read(cx); + let rename_buffer = rename_editor.buffer.read(cx); + let cursor_in_rename_editor = rename_editor + .selections + .newest::(&rename_buffer.read(cx)) + .head(); // Update the selection to match the position of the selection inside // the rename editor. @@ -4913,17 +4938,14 @@ impl Editor { .min(rename_range.end); drop(snapshot); - self.update_selections( - vec![Selection { - id: self.newest_anchor_selection().id, - start: cursor_in_editor, - end: cursor_in_editor, - reversed: false, - goal: SelectionGoal::None, - }], - None, - cx, - ); + let new_selection = Selection { + id: self.selections.newest_anchor().id, + start: cursor_in_editor, + end: cursor_in_editor, + reversed: false, + goal: SelectionGoal::None, + }; + self.change_selections(true, cx, |s| s.select(vec![new_selection], None)); } Some(rename) @@ -5046,8 +5068,9 @@ impl Editor { if columns.start < line_len || (is_empty && columns.start == line_len) { let start = DisplayPoint::new(row, columns.start); let end = DisplayPoint::new(row, cmp::min(columns.end, line_len)); + // TODO: Don't expose next_selection_id Some(Selection { - id: post_inc(&mut self.next_selection_id), + id: post_inc(&mut self.selections.next_selection_id), start: start.to_point(display_map), end: end.to_point(display_map), reversed, @@ -5061,430 +5084,19 @@ impl Editor { } } - pub fn local_selections_in_range( - &self, - range: Range, - display_map: &DisplaySnapshot, - ) -> Vec> { - let buffer = &display_map.buffer_snapshot; - - let start_ix = match self - .selections - .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer)) - { - Ok(ix) | Err(ix) => ix, - }; - let end_ix = match self - .selections - .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer)) - { - Ok(ix) => ix + 1, - Err(ix) => ix, - }; - - fn point_selection( - selection: &Selection, - buffer: &MultiBufferSnapshot, - ) -> Selection { - let start = selection.start.to_point(&buffer); - let end = selection.end.to_point(&buffer); - Selection { - id: selection.id, - start, - end, - reversed: selection.reversed, - goal: selection.goal, - } - } - - self.selections[start_ix..end_ix] - .iter() - .chain( - self.pending_selection - .as_ref() - .map(|pending| &pending.selection), - ) - .map(|s| point_selection(s, &buffer)) - .collect() - } - - pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec> - where - D: 'a + TextDimension + Ord + Sub, - { - let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self - .resolve_selections::(self.selections.iter(), &buffer) - .peekable(); - - let mut pending_selection = self.pending_selection::(&buffer); - - iter::from_fn(move || { - if let Some(pending) = pending_selection.as_mut() { - while let Some(next_selection) = selections.peek() { - if pending.start <= next_selection.end && pending.end >= next_selection.start { - let next_selection = selections.next().unwrap(); - if next_selection.start < pending.start { - pending.start = next_selection.start; - } - if next_selection.end > pending.end { - pending.end = next_selection.end; - } - } else if next_selection.end < pending.start { - return selections.next(); - } else { - break; - } - } - - pending_selection.take() - } else { - selections.next() - } - }) - .collect() - } - - fn resolve_selections<'a, D, I>( - &self, - selections: I, - snapshot: &MultiBufferSnapshot, - ) -> impl 'a + Iterator> - where - D: TextDimension + Ord + Sub, - I: 'a + IntoIterator>, - { - let (to_summarize, selections) = selections.into_iter().tee(); - let mut summaries = snapshot - .summaries_for_anchors::(to_summarize.flat_map(|s| [&s.start, &s.end])) - .into_iter(); - selections.map(move |s| Selection { - id: s.id, - start: summaries.next().unwrap(), - end: summaries.next().unwrap(), - reversed: s.reversed, - goal: s.goal, - }) - } - - fn pending_selection>( - &self, - snapshot: &MultiBufferSnapshot, - ) -> Option> { - self.pending_selection - .as_ref() - .map(|pending| self.resolve_selection(&pending.selection, &snapshot)) - } - - fn resolve_selection>( - &self, - selection: &Selection, - buffer: &MultiBufferSnapshot, - ) -> Selection { - Selection { - id: selection.id, - start: selection.start.summary::(&buffer), - end: selection.end.summary::(&buffer), - reversed: selection.reversed, - goal: selection.goal, - } - } - - fn selection_count<'a>(&self) -> usize { - let mut count = self.selections.len(); - if self.pending_selection.is_some() { - count += 1; - } - count - } - - pub fn oldest_selection>( - &self, - cx: &AppContext, - ) -> Selection { - let snapshot = self.buffer.read(cx).read(cx); - self.selections - .iter() - .min_by_key(|s| s.id) - .map(|selection| self.resolve_selection(selection, &snapshot)) - .or_else(|| self.pending_selection(&snapshot)) - .unwrap() - } - - pub fn newest_selection>( - &self, - cx: &AppContext, - ) -> Selection { - self.resolve_selection( - self.newest_anchor_selection(), - &self.buffer.read(cx).read(cx), - ) - } - - pub fn newest_selection_with_snapshot>( - &self, - snapshot: &MultiBufferSnapshot, - ) -> Selection { - self.resolve_selection(self.newest_anchor_selection(), snapshot) - } - - pub fn newest_anchor_selection(&self) -> &Selection { - self.pending_selection - .as_ref() - .map(|s| &s.selection) - .or_else(|| self.selections.iter().max_by_key(|s| s.id)) - .unwrap() - } - - pub fn update_selections( - &mut self, - mut selections: Vec>, - autoscroll: Option, - cx: &mut ViewContext, - ) where - T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug, - { - let buffer = self.buffer.read(cx).snapshot(cx); - selections.sort_unstable_by_key(|s| s.start); - - // Merge overlapping selections. - let mut i = 1; - while i < selections.len() { - if selections[i - 1].end >= selections[i].start { - let removed = selections.remove(i); - if removed.start < selections[i - 1].start { - selections[i - 1].start = removed.start; - } - if removed.end > selections[i - 1].end { - selections[i - 1].end = removed.end; - } - } else { - i += 1; - } - } - - if let Some(autoscroll) = autoscroll { - self.request_autoscroll(autoscroll, cx); - } - - self.set_selections( - Arc::from_iter(selections.into_iter().map(|selection| { - let end_bias = if selection.end > selection.start { - Bias::Left - } else { - Bias::Right - }; - Selection { - id: selection.id, - start: buffer.anchor_after(selection.start), - end: buffer.anchor_at(selection.end, end_bias), - reversed: selection.reversed, - goal: selection.goal, - } - })), - None, - true, - cx, - ); - } - pub fn set_selections_from_remote( &mut self, - mut selections: Vec>, + selections: Vec>, cx: &mut ViewContext, ) { - let buffer = self.buffer.read(cx); - let buffer = buffer.read(cx); - selections.sort_by(|a, b| { - a.start - .cmp(&b.start, &*buffer) - .then_with(|| b.end.cmp(&a.end, &*buffer)) + self.change_selections(false, cx, |s| { + s.select_anchors(selections, None); }); - - // Merge overlapping selections - let mut i = 1; - while i < selections.len() { - if selections[i - 1] - .end - .cmp(&selections[i].start, &*buffer) - .is_ge() - { - let removed = selections.remove(i); - if removed - .start - .cmp(&selections[i - 1].start, &*buffer) - .is_lt() - { - selections[i - 1].start = removed.start; - } - if removed.end.cmp(&selections[i - 1].end, &*buffer).is_gt() { - selections[i - 1].end = removed.end; - } - } else { - i += 1; - } - } - - drop(buffer); - self.set_selections(selections.into(), None, false, cx); - } - - /// Compute new ranges for any selections that were located in excerpts that have - /// since been removed. - /// - /// Returns a `HashMap` indicating which selections whose former head position - /// was no longer present. The keys of the map are selection ids. The values are - /// the id of the new excerpt where the head of the selection has been moved. - pub fn refresh_selections(&mut self, cx: &mut ViewContext) -> HashMap { - let snapshot = self.buffer.read(cx).read(cx); - let mut selections_with_lost_position = HashMap::default(); - - let mut pending_selection = self.pending_selection.take(); - if let Some(pending) = pending_selection.as_mut() { - let anchors = - snapshot.refresh_anchors([&pending.selection.start, &pending.selection.end]); - let (_, start, kept_start) = anchors[0].clone(); - let (_, end, kept_end) = anchors[1].clone(); - let kept_head = if pending.selection.reversed { - kept_start - } else { - kept_end - }; - if !kept_head { - selections_with_lost_position.insert( - pending.selection.id, - pending.selection.head().excerpt_id.clone(), - ); - } - - pending.selection.start = start; - pending.selection.end = end; - } - - let anchors_with_status = snapshot.refresh_anchors( - self.selections - .iter() - .flat_map(|selection| [&selection.start, &selection.end]), - ); - self.selections = anchors_with_status - .chunks(2) - .map(|selection_anchors| { - let (anchor_ix, start, kept_start) = selection_anchors[0].clone(); - let (_, end, kept_end) = selection_anchors[1].clone(); - let selection = &self.selections[anchor_ix / 2]; - let kept_head = if selection.reversed { - kept_start - } else { - kept_end - }; - if !kept_head { - selections_with_lost_position - .insert(selection.id, selection.head().excerpt_id.clone()); - } - - Selection { - id: selection.id, - start, - end, - reversed: selection.reversed, - goal: selection.goal, - } - }) - .collect(); - drop(snapshot); - - let new_selections = self.local_selections::(cx); - if !new_selections.is_empty() { - self.update_selections(new_selections, Some(Autoscroll::Fit), cx); - } - self.pending_selection = pending_selection; - - selections_with_lost_position - } - - fn set_selections( - &mut self, - selections: Arc<[Selection]>, - pending_selection: Option, - local: bool, - cx: &mut ViewContext, - ) { - assert!( - !selections.is_empty() || pending_selection.is_some(), - "must have at least one selection" - ); - - let old_cursor_position = self.newest_anchor_selection().head(); - - self.push_to_selection_history(); - self.selections = selections; - self.pending_selection = pending_selection; - if self.focused && self.leader_replica_id.is_none() { - self.buffer.update(cx, |buffer, cx| { - buffer.set_active_selections(&self.selections, cx) - }); - } - - let display_map = self - .display_map - .update(cx, |display_map, cx| display_map.snapshot(cx)); - let buffer = &display_map.buffer_snapshot; - self.add_selections_state = None; - self.select_next_state = None; - self.select_larger_syntax_node_stack.clear(); - self.autoclose_stack.invalidate(&self.selections, &buffer); - self.snippet_stack.invalidate(&self.selections, &buffer); - self.take_rename(false, cx); - - let new_cursor_position = self.newest_anchor_selection().head(); - - self.push_to_nav_history( - old_cursor_position.clone(), - Some(new_cursor_position.to_point(&buffer)), - cx, - ); - - if local { - let completion_menu = match self.context_menu.as_mut() { - Some(ContextMenu::Completions(menu)) => Some(menu), - _ => { - self.context_menu.take(); - None - } - }; - - if let Some(completion_menu) = completion_menu { - let cursor_position = new_cursor_position.to_offset(&buffer); - let (word_range, kind) = - buffer.surrounding_word(completion_menu.initial_position.clone()); - if kind == Some(CharKind::Word) - && word_range.to_inclusive().contains(&cursor_position) - { - let query = Self::completion_query(&buffer, cursor_position); - cx.background() - .block(completion_menu.filter(query.as_deref(), cx.background().clone())); - self.show_completions(&ShowCompletions, cx); - } else { - self.hide_context_menu(cx); - } - } - - if old_cursor_position.to_display_point(&display_map).row() - != new_cursor_position.to_display_point(&display_map).row() - { - self.available_code_actions.take(); - } - self.refresh_code_actions(cx); - self.refresh_document_highlights(cx); - } - - self.pause_cursor_blinking(cx); - cx.emit(Event::SelectionsChanged { local }); } fn push_to_selection_history(&mut self) { self.selection_history.push(SelectionHistoryEntry { - selections: self.selections.clone(), + selections: self.selections.disjoint_anchors().clone(), select_next_state: self.select_next_state.clone(), add_selections_state: self.add_selections_state.clone(), }); @@ -5517,7 +5129,7 @@ impl Editor { .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx)) { self.selection_history - .insert_transaction(tx_id, self.selections.clone()); + .insert_transaction(tx_id, self.selections.disjoint_anchors().clone()); } } @@ -5527,7 +5139,7 @@ impl Editor { .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) { if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) { - *end_selections = Some(self.selections.clone()); + *end_selections = Some(self.selections.disjoint_anchors().clone()); } else { log::error!("unexpectedly ended a transaction that wasn't started by this editor"); } @@ -5547,8 +5159,10 @@ impl Editor { pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { let mut fold_ranges = Vec::new(); - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let selections = self + .selections + .interleaved::(&display_map.buffer_snapshot); for selection in selections { let range = selection.display_range(&display_map).sorted(); let buffer_start_row = range.start.to_point(&display_map).row; @@ -5570,9 +5184,9 @@ impl Editor { } pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; + let selections = self.selections.interleaved::(buffer); let ranges = selections .iter() .map(|s| { @@ -5630,7 +5244,9 @@ impl Editor { } pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); + let selections = self + .selections + .interleaved::(&self.buffer.read(cx).read(cx)); let ranges = selections.into_iter().map(|s| s.start..s.end); self.fold_ranges(ranges, cx); } @@ -6007,7 +5623,7 @@ impl Editor { } let mut new_selections_by_buffer = HashMap::default(); - for selection in editor.local_selections::(cx) { + for selection in editor.selections.interleaved::(&buffer.read(cx)) { for (buffer, mut range) in buffer.range_to_buffer_ranges(selection.start..selection.end, cx) { @@ -6022,7 +5638,7 @@ impl Editor { } editor_handle.update(cx, |editor, cx| { - editor.push_to_nav_history(editor.newest_anchor_selection().head(), None, cx); + editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx); }); let nav_history = workspace.active_pane().read(cx).nav_history().clone(); nav_history.borrow_mut().disable(); @@ -6034,7 +5650,9 @@ impl Editor { for (buffer, ranges) in new_selections_by_buffer.into_iter() { let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { - editor.select_ranges(ranges, Some(Autoscroll::Newest), cx); + editor.change_selections(true, cx, |s| { + s.select_ranges(ranges, Some(Autoscroll::Newest)); + }); }); } @@ -6134,7 +5752,7 @@ impl View for Editor { self.buffer.update(cx, |buffer, cx| { buffer.finalize_last_transaction(cx); if self.leader_replica_id.is_none() { - buffer.set_active_selections(&self.selections, cx); + buffer.set_active_selections(&self.selections.disjoint_anchors(), cx); } }); } @@ -6571,7 +6189,7 @@ mod tests { use std::{cell::RefCell, rc::Rc, time::Instant}; use text::Point; use unindent::Unindent; - use util::test::{marked_text_by, marked_text_ranges, sample_text}; + use util::test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text}; use workspace::{FollowableItem, ItemHandle}; #[gpui::test] @@ -6676,7 +6294,8 @@ mod tests { // No event is emitted when the mutation is a no-op. editor2.update(cx, |editor, cx| { - editor.select_ranges([0..0], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([0..0], None)); + editor.backspace(&Backspace, cx); }); assert_eq!(mem::take(&mut *events.borrow_mut()), []); @@ -6693,21 +6312,22 @@ mod tests { editor.update(cx, |editor, cx| { editor.start_transaction_at(now, cx); - editor.select_ranges([2..4], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([2..4], None)); + editor.insert("cd", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cd56"); assert_eq!(editor.selected_ranges(cx), vec![4..4]); editor.start_transaction_at(now, cx); - editor.select_ranges([4..5], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([4..5], None)); editor.insert("e", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cde6"); assert_eq!(editor.selected_ranges(cx), vec![5..5]); now += group_interval + Duration::from_millis(1); - editor.select_ranges([2..2], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([2..2], None)); // Simulate an edit in another editor buffer.update(cx, |buffer, cx| { @@ -6861,13 +6481,19 @@ mod tests { // Move the cursor a small distance. // Nothing is added to the navigation history. - editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx); - editor.select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) + }); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) + }); assert!(nav_history.borrow_mut().pop_backward().is_none()); // Move the cursor a large distance. // The history can jump back to the previous position. - editor.select_display_ranges(&[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)]) + }); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); editor.navigate(nav_entry.data.unwrap(), cx); assert_eq!(nav_entry.item.id(), cx.view_id()); @@ -7014,7 +6640,9 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]); + }); view.fold(&Fold, cx); assert_eq!( view.display_text(cx), @@ -7130,7 +6758,9 @@ mod tests { &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); - view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]); + }); view.select_to_beginning(&SelectToBeginning, cx); assert_eq!( view.selected_display_ranges(cx), @@ -7252,7 +6882,9 @@ mod tests { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]); + }); view.move_down(&MoveDown, cx); assert_eq!( view.selected_display_ranges(cx), @@ -7297,13 +6929,12 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\n def", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), - ], - cx, - ); + ]); + }); }); view.update(cx, |view, cx| { @@ -7458,13 +7089,12 @@ mod tests { let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4), - ], - cx, - ); + ]) + }); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); assert_selection_ranges( @@ -7570,7 +7200,9 @@ mod tests { "use one::{\n two::three::\n four::five\n};" ); - view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]); + }); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( @@ -7619,7 +7251,7 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); editor.update(cx, |editor, cx| { - editor.select_ranges(ranges, None, cx); + editor.change_selections(true, cx, |s| s.select_ranges(ranges, None)); editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); assert_eq!(editor.text(cx), " four"); }); @@ -7632,30 +7264,28 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ // an empty selection - the preceding word fragment is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), // characters selected - they are deleted DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12), - ], - cx, - ); + ]) + }); view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx); }); assert_eq!(buffer.read(cx).read(cx).text(), "e two te four"); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ // an empty selection - the following word fragment is deleted DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), // characters selected - they are deleted DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10), - ], - cx, - ); + ]) + }); view.delete_to_next_word_end(&DeleteToNextWordEnd, cx); }); @@ -7669,14 +7299,13 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6), - ], - cx, - ); + ]) + }); view.newline(&Newline, cx); assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n"); @@ -7703,14 +7332,15 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(buffer.clone(), cx); - editor.select_ranges( - [ - Point::new(2, 4)..Point::new(2, 5), - Point::new(5, 4)..Point::new(5, 5), - ], - None, - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_ranges( + [ + Point::new(2, 4)..Point::new(2, 5), + Point::new(5, 4)..Point::new(5, 5), + ], + None, + ) + }); editor }); @@ -7773,7 +7403,7 @@ mod tests { let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(buffer.clone(), cx); - editor.select_ranges([3..4, 11..12, 19..20], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([3..4, 11..12, 19..20], None)); editor }); @@ -8022,23 +7652,22 @@ mod tests { view.update(cx, |view, cx| { view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx); - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ // an empty selection - the preceding character is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), // one character selected - it is deleted DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), // a line suffix selected - it is deleted DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), - ], - cx, - ); + ]) + }); view.backspace(&Backspace, cx); assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n"); view.set_text(" one\n two\n three\n four", cx); - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ // cursors at the the end of leading indent - last indent is deleted DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), DisplayPoint::new(1, 8)..DisplayPoint::new(1, 8), @@ -8050,9 +7679,8 @@ mod tests { DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), // selection inside leading indent - only the selected character is deleted DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3), - ], - cx, - ); + ]) + }); view.backspace(&Backspace, cx); assert_eq!(view.text(cx), "one\n two\n three four"); }); @@ -8066,17 +7694,16 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ // an empty selection - the following character is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), // one character selected - it is deleted DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), // a line suffix selected - it is deleted DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), - ], - cx, - ); + ]) + }); view.delete(&Delete, cx); }); @@ -8092,14 +7719,13 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ], - cx, - ); + ]) + }); view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi"); assert_eq!( @@ -8115,7 +7741,9 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)]) + }); view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi\n"); assert_eq!( @@ -8131,15 +7759,14 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ], - cx, - ); + ]) + }); view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n"); assert_eq!( @@ -8156,13 +7783,12 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1), - ], - cx, - ); + ]) + }); view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n"); assert_eq!( @@ -8189,15 +7815,14 @@ mod tests { ], cx, ); - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3), DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2), - ], - cx, - ); + ]) + }); assert_eq!( view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj" @@ -8287,7 +7912,9 @@ mod tests { }], cx, ); - editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None) + }); editor.move_line_down(&MoveLineDown, cx); }); } @@ -8299,7 +7926,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); - editor.select_ranges([1..1], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([1..1], None)); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); assert_eq!(editor.selected_ranges(cx), [2..2]); @@ -8319,12 +7946,12 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.select_ranges([3..3], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([3..3], None)); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acb\nde"); assert_eq!(editor.selected_ranges(cx), [3..3]); - editor.select_ranges([4..4], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([4..4], None)); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); assert_eq!(editor.selected_ranges(cx), [5..5]); @@ -8344,7 +7971,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.select_ranges([1..1, 2..2, 4..4], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([1..1, 2..2, 4..4], None)); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bacd\ne"); assert_eq!(editor.selected_ranges(cx), [2..2, 3..3, 5..5]); @@ -8372,7 +7999,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); - editor.select_ranges([4..4], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([4..4], None)); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); assert_eq!(editor.selected_ranges(cx), [8..8]); @@ -8400,14 +8027,18 @@ mod tests { // Cut with three selections. Clipboard text is divided into three slices. view.update(cx, |view, cx| { - view.select_ranges(vec![0..7, 11..17, 22..27], None, cx); + view.change_selections(true, cx, |s| { + s.select_ranges(vec![0..7, 11..17, 22..27], None) + }); view.cut(&Cut, cx); assert_eq!(view.display_text(cx), "two four six "); }); // Paste with three cursors. Each cursor pastes one slice of the clipboard text. view.update(cx, |view, cx| { - view.select_ranges(vec![4..4, 9..9, 13..13], None, cx); + view.change_selections(true, cx, |s| { + s.select_ranges(vec![4..4, 9..9, 13..13], None) + }); view.paste(&Paste, cx); assert_eq!(view.display_text(cx), "two one✅ four three six five "); assert_eq!( @@ -8424,7 +8055,7 @@ mod tests { // match the number of slices in the clipboard, the entire clipboard text // is pasted at each cursor. view.update(cx, |view, cx| { - view.select_ranges(vec![0..0, 31..31], None, cx); + view.change_selections(true, cx, |s| s.select_ranges(vec![0..0, 31..31], None)); view.handle_input(&Input("( ".into()), cx); view.paste(&Paste, cx); view.handle_input(&Input(") ".into()), cx); @@ -8435,7 +8066,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_ranges(vec![0..0], None, cx); + view.change_selections(true, cx, |s| s.select_ranges(vec![0..0], None)); view.handle_input(&Input("123\n4567\n89\n".into()), cx); assert_eq!( view.display_text(cx), @@ -8445,14 +8076,13 @@ mod tests { // Cut with three selections, one of which is full-line. view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| s.select_display_ranges( + [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1), ], - cx, - ); + )); view.cut(&Cut, cx); assert_eq!( view.display_text(cx), @@ -8463,14 +8093,13 @@ mod tests { // Paste with three selections, noticing how the copied selection that was full-line // gets inserted before the second cursor. view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| s.select_display_ranges( + [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3), ], - cx, - ); + )); view.paste(&Paste, cx); assert_eq!( view.display_text(cx), @@ -8488,21 +8117,22 @@ mod tests { // Copy with a single cursor only, which writes the whole line into the clipboard. view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]) + }); view.copy(&Copy, cx); }); // Paste with three selections, noticing how the copied full-line selection is inserted // before the empty selections but replaces the selection that is non-empty. view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| s.select_display_ranges( + [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), ], - cx, - ); + )); view.paste(&Paste, cx); assert_eq!( view.display_text(cx), @@ -8539,15 +8169,14 @@ mod tests { let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), - ], - cx, - ); + ]) + }); view.select_line(&SelectLine, cx); assert_eq!( view.selected_display_ranges(cx), @@ -8592,15 +8221,14 @@ mod tests { ], cx, ); - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), - ], - cx, - ); + ]) + }); assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"); }); @@ -8622,7 +8250,9 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)]) + }); view.split_selection_into_lines(&SplitSelectionIntoLines, cx); assert_eq!( view.display_text(cx), @@ -8651,7 +8281,9 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]) + }); }); view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); @@ -8721,7 +8353,9 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]) + }); }); view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); @@ -8762,7 +8396,9 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)]) + }); view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( view.selected_display_ranges(cx), @@ -8800,7 +8436,9 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)]) + }); }); view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); @@ -8837,7 +8475,9 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None, cx); + view.change_selections(true, cx, |s| { + s.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None) + }); view.select_next( &SelectNext { replace_newest: false, @@ -8902,14 +8542,13 @@ mod tests { .await; view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), - ], - cx, - ); + ]); + }); view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( @@ -9062,7 +8701,7 @@ mod tests { .await; editor.update(cx, |editor, cx| { - editor.select_ranges([5..5, 8..8, 9..9], None, cx); + editor.change_selections(true, cx, |s| s.select_ranges([5..5, 8..8, 9..9], None)); editor.newline(&Newline, cx); assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); assert_eq!( @@ -9116,13 +8755,12 @@ mod tests { .await; view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ], - cx, - ); + ]) + }); view.handle_input(&Input("{".to_string()), cx); view.handle_input(&Input("{".to_string()), cx); @@ -9168,13 +8806,12 @@ mod tests { ); view.undo(&Undo, cx); - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ], - cx, - ); + ]) + }); view.handle_input(&Input("*".to_string()), cx); assert_eq!( view.text(cx), @@ -9190,7 +8827,9 @@ mod tests { // Don't autoclose if the next character isn't whitespace and isn't // listed in the language's "autoclose_before" section. view.finalize_last_transaction(cx); - view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) + }); view.handle_input(&Input("{".to_string()), cx); assert_eq!( view.text(cx), @@ -9204,7 +8843,9 @@ mod tests { ); view.undo(&Undo, cx); - view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)], cx); + view.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)]) + }); view.handle_input(&Input("{".to_string()), cx); assert_eq!( view.text(cx), @@ -9227,105 +8868,88 @@ mod tests { async fn test_snippets(cx: &mut gpui::TestAppContext) { cx.update(|cx| cx.set_global(Settings::test(cx))); - let text = " - a. b - a. b - a. b - " - .unindent(); + let (text, insertion_ranges) = marked_text_ranges(indoc! {" + a.| b + a.| b + a.| b"}); let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); editor.update(cx, |editor, cx| { - let buffer = &editor.snapshot(cx).buffer_snapshot; let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap(); - let insertion_ranges = [ - Point::new(0, 2).to_offset(buffer)..Point::new(0, 2).to_offset(buffer), - Point::new(1, 2).to_offset(buffer)..Point::new(1, 2).to_offset(buffer), - Point::new(2, 2).to_offset(buffer)..Point::new(2, 2).to_offset(buffer), - ]; editor .insert_snippet(&insertion_ranges, snippet, cx) .unwrap(); - assert_eq!( - editor.text(cx), - " - a.f(one, two, three) b - a.f(one, two, three) b - a.f(one, two, three) b - " - .unindent() - ); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 4)..Point::new(0, 7), - Point::new(0, 14)..Point::new(0, 19), - Point::new(1, 4)..Point::new(1, 7), - Point::new(1, 14)..Point::new(1, 19), - Point::new(2, 4)..Point::new(2, 7), - Point::new(2, 14)..Point::new(2, 19), - ] + + fn assert(editor: &mut Editor, cx: &mut ViewContext, marked_text_ranges: &str) { + let range_markers = ('<', '>'); + let (expected_text, mut selection_ranges_lookup) = + marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone()]); + let selection_ranges = selection_ranges_lookup.remove(&range_markers).unwrap(); + assert_eq!(editor.text(cx), expected_text); + assert_eq!(editor.selected_ranges::(cx), selection_ranges); + } + assert( + editor, + cx, + indoc! {" + a.f(, two, ) b + a.f(, two, ) b + a.f(, two, ) b"}, ); // Can't move earlier than the first tab stop - editor.move_to_prev_snippet_tabstop(cx); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 4)..Point::new(0, 7), - Point::new(0, 14)..Point::new(0, 19), - Point::new(1, 4)..Point::new(1, 7), - Point::new(1, 14)..Point::new(1, 19), - Point::new(2, 4)..Point::new(2, 7), - Point::new(2, 14)..Point::new(2, 19), - ] + assert!(!editor.move_to_prev_snippet_tabstop(cx)); + assert( + editor, + cx, + indoc! {" + a.f(, two, ) b + a.f(, two, ) b + a.f(, two, ) b"}, ); assert!(editor.move_to_next_snippet_tabstop(cx)); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 9)..Point::new(0, 12), - Point::new(1, 9)..Point::new(1, 12), - Point::new(2, 9)..Point::new(2, 12) - ] + assert( + editor, + cx, + indoc! {" + a.f(one, , three) b + a.f(one, , three) b + a.f(one, , three) b"}, ); editor.move_to_prev_snippet_tabstop(cx); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 4)..Point::new(0, 7), - Point::new(0, 14)..Point::new(0, 19), - Point::new(1, 4)..Point::new(1, 7), - Point::new(1, 14)..Point::new(1, 19), - Point::new(2, 4)..Point::new(2, 7), - Point::new(2, 14)..Point::new(2, 19), - ] + assert( + editor, + cx, + indoc! {" + a.f(, two, ) b + a.f(, two, ) b + a.f(, two, ) b"}, ); assert!(editor.move_to_next_snippet_tabstop(cx)); assert!(editor.move_to_next_snippet_tabstop(cx)); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 20)..Point::new(0, 20), - Point::new(1, 20)..Point::new(1, 20), - Point::new(2, 20)..Point::new(2, 20) - ] + assert( + editor, + cx, + indoc! {" + a.f(one, two, three)<> b + a.f(one, two, three)<> b + a.f(one, two, three)<> b"}, ); // As soon as the last tab stop is reached, snippet state is gone editor.move_to_prev_snippet_tabstop(cx); - assert_eq!( - editor.selected_ranges::(cx), - &[ - Point::new(0, 20)..Point::new(0, 20), - Point::new(1, 20)..Point::new(1, 20), - Point::new(2, 20)..Point::new(2, 20) - ] + assert( + editor, + cx, + indoc! {" + a.f(one, two, three)<> b + a.f(one, two, three)<> b + a.f(one, two, three)<> b"}, ); }); } @@ -9489,7 +9113,9 @@ mod tests { editor.update(cx, |editor, cx| { editor.project = Some(project); - editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None) + }); editor.handle_input(&Input(".".to_string()), cx); }); @@ -9542,14 +9168,15 @@ mod tests { ); editor.update(cx, |editor, cx| { - editor.select_ranges( - [ - Point::new(1, 3)..Point::new(1, 3), - Point::new(2, 5)..Point::new(2, 5), - ], - None, - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_ranges( + [ + Point::new(1, 3)..Point::new(1, 3), + Point::new(2, 5)..Point::new(2, 5), + ], + None, + ) + }); editor.handle_input(&Input(" ".to_string()), cx); assert!(editor.context_menu.is_none()); @@ -9702,13 +9329,12 @@ mod tests { view.update(cx, |editor, cx| { // If multiple selections intersect a line, the line is only // toggled once. - editor.select_display_ranges( - &[ + editor.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3), DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6), - ], - cx, - ); + ]) + }); editor.toggle_comments(&ToggleComments, cx); assert_eq!( editor.text(cx), @@ -9724,7 +9350,9 @@ mod tests { // The comment prefix is inserted at the same column for every line // in a selection. - editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)]) + }); editor.toggle_comments(&ToggleComments, cx); assert_eq!( editor.text(cx), @@ -9739,7 +9367,9 @@ mod tests { ); // If a selection ends at the beginning of a line, that line is not toggled. - editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)]) + }); editor.toggle_comments(&ToggleComments, cx); assert_eq!( editor.text(cx), @@ -9777,14 +9407,15 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx)); view.update(cx, |view, cx| { assert_eq!(view.text(cx), "aaaa\nbbbb"); - view.select_ranges( - [ - Point::new(0, 0)..Point::new(0, 0), - Point::new(1, 0)..Point::new(1, 0), - ], - None, - cx, - ); + view.change_selections(true, cx, |s| { + s.select_ranges( + [ + Point::new(0, 0)..Point::new(0, 0), + Point::new(1, 0)..Point::new(1, 0), + ], + None, + ) + }); view.handle_input(&Input("X".to_string()), cx); assert_eq!(view.text(cx), "Xaaaa\nXbbbb"); @@ -9820,7 +9451,7 @@ mod tests { b|bb|b cccc"}); assert_eq!(view.text(cx), expected_text); - view.select_ranges(selection_ranges, None, cx); + view.change_selections(true, cx, |s| s.select_ranges(selection_ranges, None)); view.handle_input(&Input("X".to_string()), cx); @@ -9874,7 +9505,9 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(multibuffer.clone(), cx); let snapshot = editor.snapshot(cx); - editor.select_ranges([Point::new(1, 3)..Point::new(1, 3)], None, cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([Point::new(1, 3)..Point::new(1, 3)], None) + }); editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); assert_eq!( editor.selected_ranges(cx), @@ -9888,7 +9521,9 @@ mod tests { // Refreshing selections is a no-op when excerpts haven't changed. editor.update(cx, |editor, cx| { - editor.refresh_selections(cx); + editor.change_selections(true, cx, |s| { + s.refresh(); + }); assert_eq!( editor.selected_ranges(cx), [ @@ -9913,7 +9548,9 @@ mod tests { // Refreshing selections will relocate the first selection to the original buffer // location. - editor.refresh_selections(cx); + editor.change_selections(true, cx, |s| { + s.refresh(); + }); assert_eq!( editor.selected_ranges(cx), [ @@ -9921,7 +9558,7 @@ mod tests { Point::new(0, 3)..Point::new(0, 3) ] ); - assert!(editor.pending_selection.is_some()); + assert!(editor.selections.pending_anchor().is_some()); }); } @@ -9970,12 +9607,14 @@ mod tests { ); // Ensure we don't panic when selections are refreshed and that the pending selection is finalized. - editor.refresh_selections(cx); + editor.change_selections(true, cx, |s| { + s.refresh(); + }); assert_eq!( editor.selected_ranges(cx), [Point::new(0, 3)..Point::new(0, 3)] ); - assert!(editor.pending_selection.is_some()); + assert!(editor.selections.pending_anchor().is_some()); }); } @@ -10018,14 +9657,13 @@ mod tests { .await; view.update(cx, |view, cx| { - view.select_display_ranges( - &[ + view.change_selections(true, cx, |s| { + s.select_display_ranges([ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), - ], - cx, - ); + ]) + }); view.newline(&Newline, cx); assert_eq!( @@ -10158,7 +9796,7 @@ mod tests { // Update the selections only leader.update(cx, |leader, cx| { - leader.select_ranges([1..1], None, cx); + leader.change_selections(true, cx, |s| s.select_ranges([1..1], None)); }); follower.update(cx, |follower, cx| { follower @@ -10183,7 +9821,7 @@ mod tests { // Update the selections and scroll position leader.update(cx, |leader, cx| { - leader.select_ranges([0..0], None, cx); + leader.change_selections(true, cx, |s| s.select_ranges([0..0], None)); leader.request_autoscroll(Autoscroll::Newest, cx); leader.set_scroll_position(vec2f(1.5, 3.5), cx); }); @@ -10199,7 +9837,7 @@ mod tests { // Creating a pending selection that precedes another selection leader.update(cx, |leader, cx| { - leader.select_ranges([1..1], None, cx); + leader.change_selections(true, cx, |s| s.select_ranges([1..1], None)); leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx); }); follower.update(cx, |follower, cx| { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a04d27a8bb..dcef624d01 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -957,8 +957,9 @@ impl Element for EditorElement { selections.extend(remote_selections); if view.show_local_selections { - let local_selections = - view.local_selections_in_range(start_anchor..end_anchor, &display_map); + let local_selections = view + .selections + .interleaved_in_range(start_anchor..end_anchor, &display_map.buffer_snapshot); for selection in &local_selections { let is_empty = selection.start == selection.end; let selection_start = snapshot.prev_line_boundary(selection.start).1; @@ -1041,7 +1042,8 @@ impl Element for EditorElement { } let newest_selection_head = view - .newest_selection_with_snapshot::(&snapshot.buffer_snapshot) + .selections + .newest::(&snapshot.buffer_snapshot) .head() .to_display_point(&snapshot); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 120826321b..b87719c130 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -102,7 +102,7 @@ impl FollowableItem for Editor { } else { self.buffer.update(cx, |buffer, cx| { if self.focused { - buffer.set_active_selections(&self.selections, cx); + buffer.set_active_selections(&self.selections.disjoint_anchors(), cx); } }); } @@ -118,7 +118,12 @@ impl FollowableItem for Editor { )), scroll_x: self.scroll_position.x(), scroll_y: self.scroll_position.y(), - selections: self.selections.iter().map(serialize_selection).collect(), + selections: self + .selections + .disjoint_anchors() + .iter() + .map(serialize_selection) + .collect(), })) } @@ -144,8 +149,9 @@ impl FollowableItem for Editor { Event::SelectionsChanged { .. } => { update.selections = self .selections + .disjoint_anchors() .iter() - .chain(self.pending_selection.as_ref().map(|p| &p.selection)) + .chain(self.selections.pending_anchor().as_ref()) .map(serialize_selection) .collect(); true @@ -252,7 +258,7 @@ impl Item for Editor { } else { buffer.clip_point(data.cursor_position, Bias::Left) }; - let newest_selection = self.newest_selection_with_snapshot::(&buffer); + let newest_selection = self.selections.newest::(&buffer); let scroll_top_anchor = if buffer.can_resolve(&data.scroll_top_anchor) { data.scroll_top_anchor @@ -270,7 +276,9 @@ impl Item for Editor { let nav_history = self.nav_history.take(); self.scroll_position = data.scroll_position; self.scroll_top_anchor = scroll_top_anchor; - self.select_ranges([offset..offset], Some(Autoscroll::Fit), cx); + self.change_selections(true, cx, |s| { + s.select_ranges([offset..offset], Some(Autoscroll::Fit)) + }); self.nav_history = nav_history; true } @@ -307,7 +315,7 @@ impl Item for Editor { } fn deactivated(&mut self, cx: &mut ViewContext) { - let selection = self.newest_anchor_selection(); + let selection = self.selections.newest_anchor(); self.push_to_nav_history(selection.head(), None, cx); } @@ -457,7 +465,7 @@ impl CursorPosition { self.selected_count = 0; let mut last_selection: Option> = None; - for selection in editor.local_selections::(cx) { + for selection in editor.selections.interleaved::(&buffer) { self.selected_count += selection.end - selection.start; if last_selection .as_ref() diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs new file mode 100644 index 0000000000..fc2f563099 --- /dev/null +++ b/crates/editor/src/selections_collection.rs @@ -0,0 +1,703 @@ +use std::{ + iter, mem, + ops::{Deref, Range, Sub}, + sync::Arc, +}; + +use collections::HashMap; +use gpui::{ModelHandle, MutableAppContext}; +use itertools::Itertools; +use language::{rope::TextDimension, Bias, Point, Selection, SelectionGoal, ToPoint}; +use util::post_inc; + +use crate::{ + display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, + Anchor, Autoscroll, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, + ToOffset, +}; + +#[derive(Clone)] +pub struct PendingSelection { + pub selection: Selection, + pub mode: SelectMode, +} + +pub struct SelectionsCollection { + pub next_selection_id: usize, + disjoint: Arc<[Selection]>, + pending: Option, +} + +impl SelectionsCollection { + pub fn new() -> Self { + Self { + next_selection_id: 1, + disjoint: Arc::from([]), + pending: Some(PendingSelection { + selection: Selection { + id: 0, + start: Anchor::min(), + end: Anchor::min(), + reversed: false, + goal: SelectionGoal::None, + }, + mode: SelectMode::Character, + }), + } + } + + pub fn count<'a>(&self) -> usize { + let mut count = self.disjoint.len(); + if self.pending.is_some() { + count += 1; + } + count + } + + pub fn disjoint_anchors(&self) -> Arc<[Selection]> { + self.disjoint.clone() + } + + pub fn pending_anchor(&self) -> Option> { + self.pending + .as_ref() + .map(|pending| pending.selection.clone()) + } + + pub fn pending>( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Option> { + self.pending_anchor() + .as_ref() + .map(|pending| pending.map(|p| p.summary::(&snapshot))) + } + + pub fn pending_mode(&self) -> Option { + self.pending.as_ref().map(|pending| pending.mode.clone()) + } + + pub fn interleaved<'a, D>(&self, buffer: &MultiBufferSnapshot) -> Vec> + where + D: 'a + TextDimension + Ord + Sub, + { + let anchor_disjoint = &self.disjoint; + let mut disjoint = resolve_multiple::(anchor_disjoint.iter(), &buffer).peekable(); + + let mut pending_opt = self.pending::(&buffer); + + iter::from_fn(move || { + if let Some(pending) = pending_opt.as_mut() { + while let Some(next_selection) = disjoint.peek() { + if pending.start <= next_selection.end && pending.end >= next_selection.start { + let next_selection = disjoint.next().unwrap(); + if next_selection.start < pending.start { + pending.start = next_selection.start; + } + if next_selection.end > pending.end { + pending.end = next_selection.end; + } + } else if next_selection.end < pending.start { + return disjoint.next(); + } else { + break; + } + } + + pending_opt.take() + } else { + disjoint.next() + } + }) + .collect() + } + + pub fn interleaved_in_range<'a>( + &self, + range: Range, + buffer: &MultiBufferSnapshot, + ) -> Vec> { + let start_ix = match self + .disjoint + .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer)) + { + Ok(ix) | Err(ix) => ix, + }; + let end_ix = match self + .disjoint + .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer)) + { + Ok(ix) => ix + 1, + Err(ix) => ix, + }; + + fn point_selection( + selection: &Selection, + buffer: &MultiBufferSnapshot, + ) -> Selection { + let start = crate::ToPoint::to_point(&selection.start, &buffer); + let end = crate::ToPoint::to_point(&selection.end, &buffer); + Selection { + id: selection.id, + start, + end, + reversed: selection.reversed, + goal: selection.goal, + } + } + + self.disjoint[start_ix..end_ix] + .iter() + .chain(self.pending.as_ref().map(|pending| &pending.selection)) + .map(|s| point_selection(s, &buffer)) + .collect() + } + + pub fn newest_anchor(&self) -> &Selection { + self.pending + .as_ref() + .map(|s| &s.selection) + .or_else(|| self.disjoint.iter().max_by_key(|s| s.id)) + .unwrap() + } + + pub fn newest>( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Selection { + resolve(self.newest_anchor(), snapshot) + } + + pub fn oldest_anchor(&self) -> &Selection { + self.disjoint + .iter() + .min_by_key(|s| s.id) + .or_else(|| self.pending.as_ref().map(|p| &p.selection)) + .unwrap() + } + + pub fn oldest>( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Selection { + resolve(self.oldest_anchor(), snapshot) + } + + pub fn first>( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Selection { + self.interleaved(&snapshot).first().unwrap().clone() + } + + pub fn last>( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Selection { + self.interleaved(&snapshot).last().unwrap().clone() + } + + // NOTE do not use. This should only be called from Editor::change_selections. + #[deprecated] + pub fn change_with( + &mut self, + display_map: ModelHandle, + buffer: ModelHandle, + cx: &mut MutableAppContext, + change: impl FnOnce(&mut MutableSelectionsCollection) -> R, + ) -> (Option, R) { + let mut mutable_collection = MutableSelectionsCollection { + collection: self, + autoscroll: None, + display_map, + buffer, + cx, + }; + + let result = change(&mut mutable_collection); + + assert!( + !mutable_collection.disjoint.is_empty() || mutable_collection.pending.is_some(), + "There must be at least one selection" + ); + (mutable_collection.autoscroll, result) + } +} + +pub struct MutableSelectionsCollection<'a> { + collection: &'a mut SelectionsCollection, + pub autoscroll: Option, + buffer: ModelHandle, + display_map: ModelHandle, + cx: &'a mut MutableAppContext, +} + +impl<'a> MutableSelectionsCollection<'a> { + pub fn clear_disjoint(&mut self) { + self.collection.disjoint = Arc::from([]); + } + + pub fn delete(&mut self, selection_id: usize) { + self.collection.disjoint = self + .disjoint + .into_iter() + .filter(|selection| selection.id != selection_id) + .cloned() + .collect(); + } + + pub fn clear_pending(&mut self) { + self.collection.pending = None; + } + + pub fn set_pending_range(&mut self, range: Range, mode: SelectMode) { + self.collection.pending = Some(PendingSelection { + selection: Selection { + id: post_inc(&mut self.collection.next_selection_id), + start: range.start, + end: range.end, + reversed: false, + goal: SelectionGoal::None, + }, + mode, + }) + } + pub fn pending_mut(&mut self) -> &mut Option { + &mut self.collection.pending + } + + pub fn try_cancel(&mut self) -> bool { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + if let Some(pending) = self.collection.pending.take() { + if self.disjoint.is_empty() { + self.collection.disjoint = Arc::from([pending.selection]); + } + return true; + } + + let mut oldest = self.oldest_anchor().clone(); + if self.count() > 1 { + self.collection.disjoint = Arc::from([oldest]); + return true; + } + + if !oldest.start.cmp(&oldest.end, &buffer).is_eq() { + let head = oldest.head(); + oldest.start = head.clone(); + oldest.end = head; + self.collection.disjoint = Arc::from([oldest]); + return true; + } + + return false; + } + + pub fn reset_biases(&mut self) { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + self.collection.disjoint = self + .collection + .disjoint + .into_iter() + .cloned() + .map(|selection| reset_biases(selection, &buffer)) + .collect(); + + if let Some(pending) = self.collection.pending.as_mut() { + pending.selection = reset_biases(pending.selection.clone(), &buffer); + } + } + + pub fn insert_range(&mut self, range: Range, autoscroll: Option) + where + T: 'a + + ToOffset + + ToPoint + + TextDimension + + Ord + + Sub + + std::marker::Copy + + std::fmt::Debug, + { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let mut selections = self.interleaved(&buffer); + let mut start = range.start.to_offset(&buffer); + let mut end = range.end.to_offset(&buffer); + let reversed = if start > end { + mem::swap(&mut start, &mut end); + true + } else { + false + }; + selections.push(Selection { + id: post_inc(&mut self.collection.next_selection_id), + start, + end, + reversed, + goal: SelectionGoal::None, + }); + self.select(selections, autoscroll); + } + + pub fn select(&mut self, mut selections: Vec>, autoscroll: Option) + where + T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug, + { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + selections.sort_unstable_by_key(|s| s.start); + // Merge overlapping selections. + let mut i = 1; + while i < selections.len() { + if selections[i - 1].end >= selections[i].start { + let removed = selections.remove(i); + if removed.start < selections[i - 1].start { + selections[i - 1].start = removed.start; + } + if removed.end > selections[i - 1].end { + selections[i - 1].end = removed.end; + } + } else { + i += 1; + } + } + + self.autoscroll = autoscroll.or(self.autoscroll.take()); + + self.collection.disjoint = Arc::from_iter(selections.into_iter().map(|selection| { + let end_bias = if selection.end > selection.start { + Bias::Left + } else { + Bias::Right + }; + Selection { + id: selection.id, + start: buffer.anchor_after(selection.start), + end: buffer.anchor_at(selection.end, end_bias), + reversed: selection.reversed, + goal: selection.goal, + } + })); + + self.collection.pending = None; + } + + pub fn select_anchors( + &mut self, + mut selections: Vec>, + autoscroll: Option, + ) { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, &buffer)); + + // Merge overlapping selections. + let mut i = 1; + while i < selections.len() { + if selections[i - 1] + .end + .cmp(&selections[i].start, &buffer) + .is_ge() + { + let removed = selections.remove(i); + if removed.start.cmp(&selections[i - 1].start, &buffer).is_lt() { + selections[i - 1].start = removed.start; + } + if removed.end.cmp(&selections[i - 1].end, &buffer).is_gt() { + selections[i - 1].end = removed.end; + } + } else { + i += 1; + } + } + + self.autoscroll = autoscroll.or(self.autoscroll.take()); + + self.collection.disjoint = Arc::from_iter( + selections + .into_iter() + .map(|selection| reset_biases(selection, &buffer)), + ); + + self.collection.pending = None; + } + + pub fn select_ranges(&mut self, ranges: I, autoscroll: Option) + where + I: IntoIterator>, + T: ToOffset, + { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let selections = ranges + .into_iter() + .map(|range| { + let mut start = range.start.to_offset(&buffer); + let mut end = range.end.to_offset(&buffer); + let reversed = if start > end { + mem::swap(&mut start, &mut end); + true + } else { + false + }; + Selection { + id: post_inc(&mut self.collection.next_selection_id), + start, + end, + reversed, + goal: SelectionGoal::None, + } + }) + .collect::>(); + + self.select(selections, autoscroll) + } + + pub fn select_anchor_ranges>>( + &mut self, + ranges: I, + autoscroll: Option, + ) { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let selections = ranges + .into_iter() + .map(|range| { + let mut start = range.start; + let mut end = range.end; + let reversed = if start.cmp(&end, &buffer).is_gt() { + mem::swap(&mut start, &mut end); + true + } else { + false + }; + Selection { + id: post_inc(&mut self.collection.next_selection_id), + start, + end, + reversed, + goal: SelectionGoal::None, + } + }) + .collect::>(); + + self.select_anchors(selections, autoscroll) + } + + #[cfg(any(test, feature = "test-support"))] + pub fn select_display_ranges(&mut self, ranges: T) + where + T: IntoIterator>, + { + let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let selections = ranges + .into_iter() + .map(|range| { + let mut start = range.start; + let mut end = range.end; + let reversed = if start > end { + mem::swap(&mut start, &mut end); + true + } else { + false + }; + Selection { + id: post_inc(&mut self.collection.next_selection_id), + start: start.to_point(&display_map), + end: end.to_point(&display_map), + reversed, + goal: SelectionGoal::None, + } + }) + .collect(); + self.select(selections, None); + } + + pub fn move_with( + &mut self, + mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection), + ) { + let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let selections = self + .interleaved::(&display_map.buffer_snapshot) + .into_iter() + .map(|selection| { + let mut selection = selection.map(|point| point.to_display_point(&display_map)); + move_selection(&display_map, &mut selection); + selection.map(|display_point| display_point.to_point(&display_map)) + }) + .collect(); + + self.select(selections, Some(Autoscroll::Fit)) + } + + pub fn move_heads_with( + &mut self, + mut update_head: impl FnMut( + &DisplaySnapshot, + DisplayPoint, + SelectionGoal, + ) -> (DisplayPoint, SelectionGoal), + ) { + self.move_with(|map, selection| { + let (new_head, new_goal) = update_head(map, selection.head(), selection.goal); + selection.set_head(new_head, new_goal); + }); + } + + pub fn move_cursors_with( + &mut self, + mut update_cursor_position: impl FnMut( + &DisplaySnapshot, + DisplayPoint, + SelectionGoal, + ) -> (DisplayPoint, SelectionGoal), + ) { + self.move_with(|map, selection| { + let (cursor, new_goal) = update_cursor_position(map, selection.head(), selection.goal); + selection.collapse_to(cursor, new_goal) + }); + } + + pub fn replace_cursors_with( + &mut self, + mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec, + ) { + let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let new_selections = find_replacement_cursors(&display_map) + .into_iter() + .map(|cursor| { + let cursor_point = cursor.to_point(&display_map); + Selection { + id: post_inc(&mut self.collection.next_selection_id), + start: cursor_point, + end: cursor_point, + reversed: false, + goal: SelectionGoal::None, + } + }) + .collect(); + self.select(new_selections, None); + } + + /// Compute new ranges for any selections that were located in excerpts that have + /// since been removed. + /// + /// Returns a `HashMap` indicating which selections whose former head position + /// was no longer present. The keys of the map are selection ids. The values are + /// the id of the new excerpt where the head of the selection has been moved. + pub fn refresh(&mut self) -> HashMap { + // TODO: Pull disjoint constraint out of update_selections so we don't have to + // store the pending_selection here. + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let mut pending = self.collection.pending.take(); + let mut selections_with_lost_position = HashMap::default(); + + let anchors_with_status = buffer.refresh_anchors( + self.disjoint + .iter() + .flat_map(|selection| [&selection.start, &selection.end]), + ); + let adjusted_disjoint: Vec<_> = anchors_with_status + .chunks(2) + .map(|selection_anchors| { + let (anchor_ix, start, kept_start) = selection_anchors[0].clone(); + let (_, end, kept_end) = selection_anchors[1].clone(); + let selection = &self.disjoint[anchor_ix / 2]; + let kept_head = if selection.reversed { + kept_start + } else { + kept_end + }; + if !kept_head { + selections_with_lost_position + .insert(selection.id, selection.head().excerpt_id.clone()); + } + + Selection { + id: selection.id, + start, + end, + reversed: selection.reversed, + goal: selection.goal, + } + }) + .collect(); + + if !adjusted_disjoint.is_empty() { + self.select::( + resolve_multiple(adjusted_disjoint.iter(), &buffer).collect(), + None, + ); + } + + if let Some(pending) = pending.as_mut() { + let anchors = + buffer.refresh_anchors([&pending.selection.start, &pending.selection.end]); + let (_, start, kept_start) = anchors[0].clone(); + let (_, end, kept_end) = anchors[1].clone(); + let kept_head = if pending.selection.reversed { + kept_start + } else { + kept_end + }; + if !kept_head { + selections_with_lost_position.insert( + pending.selection.id, + pending.selection.head().excerpt_id.clone(), + ); + } + + pending.selection.start = start; + pending.selection.end = end; + } + self.collection.pending = pending; + + selections_with_lost_position + } +} + +impl<'a> Deref for MutableSelectionsCollection<'a> { + type Target = SelectionsCollection; + fn deref(&self) -> &Self::Target { + self.collection + } +} + +// Panics if passed selections are not in order +pub fn resolve_multiple<'a, D, I>( + selections: I, + snapshot: &MultiBufferSnapshot, +) -> impl 'a + Iterator> +where + D: TextDimension + Ord + Sub, + I: 'a + IntoIterator>, +{ + let (to_summarize, selections) = selections.into_iter().tee(); + let mut summaries = snapshot + .summaries_for_anchors::(to_summarize.flat_map(|s| [&s.start, &s.end])) + .into_iter(); + selections.map(move |s| Selection { + id: s.id, + start: summaries.next().unwrap(), + end: summaries.next().unwrap(), + reversed: s.reversed, + goal: s.goal, + }) +} + +fn resolve>( + selection: &Selection, + buffer: &MultiBufferSnapshot, +) -> Selection { + selection.map(|p| p.summary::(&buffer)) +} + +fn reset_biases( + mut selection: Selection, + buffer: &MultiBufferSnapshot, +) -> Selection { + let end_bias = if selection.end.cmp(&selection.start, buffer).is_gt() { + Bias::Left + } else { + Bias::Right + }; + selection.start = buffer.anchor_after(selection.start); + selection.end = buffer.anchor_at(selection.end, end_bias); + selection +} diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index eb23d7e15f..407733c771 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -43,7 +43,7 @@ pub fn marked_display_snapshot( pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext) { let (umarked_text, text_ranges) = marked_text_ranges(marked_text); assert_eq!(editor.text(cx), umarked_text); - editor.select_ranges(text_ranges, None, cx); + editor.change_selections(true, cx, |s| s.select_ranges(text_ranges, None)); } pub fn assert_text_with_selections( diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 3f8aa933ba..2f7219aa6f 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -43,7 +43,7 @@ impl GoToLine { let buffer = editor.buffer().read(cx).read(cx); ( Some(scroll_position), - editor.newest_selection_with_snapshot(&buffer).head(), + editor.selections.newest(&buffer).head(), buffer.max_point(), ) }); @@ -80,7 +80,9 @@ impl GoToLine { if let Some(rows) = active_editor.highlighted_rows() { let snapshot = active_editor.snapshot(cx).display_snapshot; let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot); - active_editor.select_ranges([position..position], Some(Autoscroll::Center), cx); + active_editor.change_selections(true, cx, |s| { + s.select_ranges([position..position], Some(Autoscroll::Center)) + }); } }); cx.emit(Event::Dismissed); diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 19c044e65f..ff0dfa5145 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -57,7 +57,9 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { if let Some(editor) = item.downcast::() { editor.update(&mut cx, |editor, cx| { let len = editor.buffer().read(cx).read(cx).len(); - editor.select_ranges([len..len], Some(Autoscroll::Center), cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([len..len], Some(Autoscroll::Center)) + }); if len > 0 { editor.insert("\n\n", cx); } diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 5658cf2011..7383b4b3a9 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -172,9 +172,7 @@ impl PickerDelegate for OutlineView { let editor = self.active_editor.read(cx); let buffer = editor.buffer().read(cx).read(cx); - let cursor_offset = editor - .newest_selection_with_snapshot::(&buffer) - .head(); + let cursor_offset = editor.selections.newest::(&buffer).head(); selected_index = self .outline .items @@ -217,7 +215,9 @@ impl PickerDelegate for OutlineView { if let Some(rows) = active_editor.highlighted_rows() { let snapshot = active_editor.snapshot(cx).display_snapshot; let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot); - active_editor.select_ranges([position..position], Some(Autoscroll::Center), cx); + active_editor.change_selections(true, cx, |s| { + s.select_ranges([position..position], Some(Autoscroll::Center)) + }); } }); cx.emit(Event::Dismissed); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 157ea8ef73..80530ff5e6 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -145,11 +145,9 @@ impl ProjectSymbolsView { let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { - editor.select_ranges( - [position..position], - Some(Autoscroll::Center), - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_ranges([position..position], Some(Autoscroll::Center)) + }); }); }); Ok::<_, anyhow::Error>(()) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 549edf89e7..95ef808ab0 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -227,7 +227,8 @@ impl BufferSearchBar { .display_snapshot; let selection = editor .read(cx) - .newest_selection_with_snapshot::(&display_map.buffer_snapshot); + .selections + .newest::(&display_map.buffer_snapshot); let mut text: String; if selection.start == selection.end { @@ -387,14 +388,16 @@ impl BufferSearchBar { if let Some(ranges) = self.editors_with_matches.get(&cx.weak_handle()) { let new_index = match_index_for_direction( ranges, - &editor.newest_anchor_selection().head(), + &editor.selections.newest_anchor().head(), index, direction, &editor.buffer().read(cx).read(cx), ); let range_to_select = ranges[new_index].clone(); editor.unfold_ranges([range_to_select.clone()], false, cx); - editor.select_ranges([range_to_select], Some(Autoscroll::Fit), cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([range_to_select], Some(Autoscroll::Fit)) + }); } }); } @@ -535,11 +538,12 @@ impl BufferSearchBar { editor.update(cx, |editor, cx| { if select_closest_match { if let Some(match_ix) = this.active_match_index { - editor.select_ranges( - [ranges[match_ix].clone()], - Some(Autoscroll::Fit), - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_ranges( + [ranges[match_ix].clone()], + Some(Autoscroll::Fit), + ) + }); } } @@ -564,7 +568,7 @@ impl BufferSearchBar { let editor = editor.read(cx); active_match_index( &ranges, - &editor.newest_anchor_selection().head(), + &editor.selections.newest_anchor().head(), &editor.buffer().read(cx).read(cx), ) }); @@ -721,7 +725,9 @@ mod tests { }); editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(0)); @@ -804,7 +810,9 @@ mod tests { // Park the cursor in between matches and ensure that going to the previous match selects // the closest match to the left. editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(1)); @@ -821,7 +829,9 @@ mod tests { // Park the cursor in between matches and ensure that going to the next match selects the // closest match to the right. editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(1)); @@ -838,7 +848,9 @@ mod tests { // Park the cursor after the last match and ensure that going to the previous match selects // the last match. editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(2)); @@ -855,7 +867,9 @@ mod tests { // Park the cursor after the last match and ensure that going to the next match selects the // first match. editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(2)); @@ -872,7 +886,9 @@ mod tests { // Park the cursor before the first match and ensure that going to the previous match // selects the last match. editor.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) + }); }); search_bar.update(cx, |search_bar, cx| { assert_eq!(search_bar.active_match_index, Some(0)); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index cbd373c468..1e363ebf15 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -454,7 +454,7 @@ impl ProjectSearchView { let results_editor = self.results_editor.read(cx); let new_index = match_index_for_direction( &model.match_ranges, - &results_editor.newest_anchor_selection().head(), + &results_editor.selections.newest_anchor().head(), index, direction, &results_editor.buffer().read(cx).read(cx), @@ -462,7 +462,9 @@ impl ProjectSearchView { let range_to_select = model.match_ranges[new_index].clone(); self.results_editor.update(cx, |editor, cx| { editor.unfold_ranges([range_to_select.clone()], false, cx); - editor.select_ranges([range_to_select], Some(Autoscroll::Fit), cx); + editor.change_selections(true, cx, |s| { + s.select_ranges([range_to_select], Some(Autoscroll::Fit)) + }); }); } } @@ -476,8 +478,10 @@ impl ProjectSearchView { fn focus_results_editor(&self, cx: &mut ViewContext) { self.query_editor.update(cx, |query_editor, cx| { - let cursor = query_editor.newest_anchor_selection().head(); - query_editor.select_ranges([cursor.clone()..cursor], None, cx); + let cursor = query_editor.selections.newest_anchor().head(); + query_editor.change_selections(true, cx, |s| { + s.select_ranges([cursor.clone()..cursor], None) + }); }); cx.focus(&self.results_editor); } @@ -489,7 +493,9 @@ impl ProjectSearchView { } else { self.results_editor.update(cx, |editor, cx| { if reset_selections { - editor.select_ranges(match_ranges.first().cloned(), Some(Autoscroll::Fit), cx); + editor.change_selections(true, cx, |s| { + s.select_ranges(match_ranges.first().cloned(), Some(Autoscroll::Fit)) + }); } editor.highlight_background::( match_ranges, @@ -510,7 +516,7 @@ impl ProjectSearchView { let results_editor = self.results_editor.read(cx); let new_index = active_match_index( &self.model.read(cx).match_ranges, - &results_editor.newest_anchor_selection().head(), + &results_editor.selections.newest_anchor().head(), &results_editor.buffer().read(cx).read(cx), ); if self.active_match_index != new_index { diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index afaeda17b0..69912cff18 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -13,9 +13,11 @@ pub fn init(cx: &mut MutableAppContext) { fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext) { Vim::update(cx, |state, cx| { state.update_active_editor(cx, |editor, cx| { - editor.move_cursors(cx, |map, mut cursor, _| { - *cursor.column_mut() = cursor.column().saturating_sub(1); - (map.clip_point(cursor, Bias::Left), SelectionGoal::None) + editor.change_selections(true, cx, |s| { + s.move_cursors_with(|map, mut cursor, _| { + *cursor.column_mut() = cursor.column().saturating_sub(1); + (map.clip_point(cursor, Bias::Left), SelectionGoal::None) + }); }); }); state.switch_mode(Mode::Normal, cx); diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 63d5ab4ccb..7c4ac6a20c 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -76,7 +76,9 @@ pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) { fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { - editor.move_cursors(cx, |map, cursor, goal| motion.move_point(map, cursor, goal)) + editor.change_selections(true, cx, |s| { + s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal)) + }) }); } @@ -84,8 +86,10 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext = Default::default(); - editor.move_selections(cx, |map, selection| { - let original_head = selection.head(); - motion.expand_selection(map, selection, true); - original_columns.insert(selection.id, original_head.column()); + editor.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + let original_head = selection.head(); + motion.expand_selection(map, selection, true); + original_columns.insert(selection.id, original_head.column()); + }); }); editor.insert(&"", cx); // Fixup cursor position after the deletion editor.set_clip_at_line_ends(true, cx); - editor.move_selections(cx, |map, selection| { - let mut cursor = selection.head(); - if motion.linewise() { - if let Some(column) = original_columns.get(&selection.id) { - *cursor.column_mut() = *column + editor.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + let mut cursor = selection.head(); + if motion.linewise() { + if let Some(column) = original_columns.get(&selection.id) { + *cursor.column_mut() = *column + } } - } - cursor = map.clip_point(cursor, Bias::Left); - selection.collapse_to(cursor, selection.goal) + cursor = map.clip_point(cursor, Bias::Left); + selection.collapse_to(cursor, selection.goal) + }); }); }); }); diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/vim_test_context.rs index 09470c31c8..cfcfa8d2b4 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/vim_test_context.rs @@ -128,7 +128,9 @@ impl<'a> VimTestContext<'a> { let (unmarked_text, markers) = marked_text(&text); editor.set_text(unmarked_text, cx); let cursor_offset = markers[0]; - editor.replace_selections_with(cx, |map| cursor_offset.to_display_point(map)); + editor.change_selections(true, cx, |s| { + s.replace_cursors_with(|map| vec![cursor_offset.to_display_point(map)]) + }); }) } @@ -197,7 +199,8 @@ impl<'a> VimTestContext<'a> { let (empty_selections, reverse_selections, forward_selections) = self.editor.read_with(self.cx, |editor, cx| { let (empty_selections, non_empty_selections): (Vec<_>, Vec<_>) = editor - .local_selections::(cx) + .selections + .interleaved::(&editor.buffer().read(cx).read(cx)) .into_iter() .partition_map(|selection| { if selection.is_empty() { diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 9c41263317..c1b2771f7a 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -14,23 +14,25 @@ pub fn init(cx: &mut MutableAppContext) { pub fn visual_motion(motion: Motion, cx: &mut MutableAppContext) { Vim::update(cx, |vim, cx| { vim.update_active_editor(cx, |editor, cx| { - editor.move_selections(cx, |map, selection| { - let (new_head, goal) = motion.move_point(map, selection.head(), selection.goal); - let new_head = map.clip_at_line_end(new_head); - let was_reversed = selection.reversed; - selection.set_head(new_head, goal); + editor.change_selections(true, cx, |s| { + s.move_with(|map, selection| { + let (new_head, goal) = motion.move_point(map, selection.head(), selection.goal); + let new_head = map.clip_at_line_end(new_head); + let was_reversed = selection.reversed; + selection.set_head(new_head, goal); - if was_reversed && !selection.reversed { - // Head was at the start of the selection, and now is at the end. We need to move the start - // back by one if possible in order to compensate for this change. - *selection.start.column_mut() = selection.start.column().saturating_sub(1); - selection.start = map.clip_point(selection.start, Bias::Left); - } else if !was_reversed && selection.reversed { - // Head was at the end of the selection, and now is at the start. We need to move the end - // forward by one if possible in order to compensate for this change. - *selection.end.column_mut() = selection.end.column() + 1; - selection.end = map.clip_point(selection.end, Bias::Left); - } + if was_reversed && !selection.reversed { + // Head was at the start of the selection, and now is at the end. We need to move the start + // back by one if possible in order to compensate for this change. + *selection.start.column_mut() = selection.start.column().saturating_sub(1); + selection.start = map.clip_point(selection.start, Bias::Left); + } else if !was_reversed && selection.reversed { + // Head was at the end of the selection, and now is at the start. We need to move the end + // forward by one if possible in order to compensate for this change. + *selection.end.column_mut() = selection.end.column() + 1; + selection.end = map.clip_point(selection.end, Bias::Left); + } + }); }); }); }); @@ -40,13 +42,15 @@ pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext() .unwrap(); editor1.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(10, 0)..DisplayPoint::new(10, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(10, 0)..DisplayPoint::new(10, 0)]) + }); }); let editor2 = workspace .update(cx, |w, cx| w.open_path(file2.clone(), true, cx)) @@ -979,10 +981,9 @@ mod tests { editor3 .update(cx, |editor, cx| { - editor.select_display_ranges( - &[DisplayPoint::new(12, 0)..DisplayPoint::new(12, 0)], - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(12, 0)..DisplayPoint::new(12, 0)]) + }); editor.newline(&Default::default(), cx); editor.newline(&Default::default(), cx); editor.move_down(&Default::default(), cx); @@ -1123,34 +1124,37 @@ mod tests { // Modify file to collapse multiple nav history entries into the same location. // Ensure we don't visit the same location twice when navigating. editor1.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)], cx) + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]) + }) }); for _ in 0..5 { editor1.update(cx, |editor, cx| { - editor - .select_display_ranges(&[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)], cx); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) + }); }); editor1.update(cx, |editor, cx| { - editor.select_display_ranges( - &[DisplayPoint::new(13, 0)..DisplayPoint::new(13, 0)], - cx, - ) + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 0)]) + }) }); } editor1.update(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - editor.select_display_ranges( - &[DisplayPoint::new(2, 0)..DisplayPoint::new(14, 0)], - cx, - ); + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(14, 0)]) + }); editor.insert("", cx); }) }); editor1.update(cx, |editor, cx| { - editor.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx) + editor.change_selections(true, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) + }) }); workspace .update(cx, |w, cx| Pane::go_back(w, None, cx)) From db0a9114c2b5b24ba8d8606b031086ef63f84bb0 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 12 May 2022 14:18:46 -0700 Subject: [PATCH 2/6] Passing tests and removed local argument. Also pulled autoscroll argument out to change_selections --- crates/collab/src/rpc.rs | 16 +- crates/diagnostics/src/diagnostics.rs | 8 +- crates/editor/src/editor.rs | 502 +++++++++--------- crates/editor/src/items.rs | 4 +- crates/editor/src/selections_collection.rs | 101 +--- crates/editor/src/test.rs | 2 +- crates/go_to_line/src/go_to_line.rs | 4 +- crates/journal/src/journal.rs | 4 +- crates/outline/src/outline.rs | 4 +- crates/project_symbols/src/project_symbols.rs | 4 +- crates/search/src/buffer_search.rs | 27 +- crates/search/src/project_search.rs | 12 +- crates/vim/src/insert.rs | 4 +- crates/vim/src/normal.rs | 14 +- crates/vim/src/normal/change.rs | 6 +- crates/vim/src/normal/delete.rs | 6 +- crates/vim/src/vim_test_context.rs | 4 +- crates/vim/src/visual.rs | 10 +- crates/zed/src/zed.rs | 16 +- 19 files changed, 351 insertions(+), 397 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 1536ceae92..3ff9e13edb 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -3001,7 +3001,7 @@ mod tests { // Type a completion trigger character as the guest. editor_b.update(cx_b, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([13..13], None)); + editor.change_selections(None, cx, |s| s.select_ranges([13..13])); editor.handle_input(&Input(".".into()), cx); cx.focus(&editor_b); }); @@ -4213,8 +4213,8 @@ mod tests { // Move cursor to a location that contains code actions. editor_b.update(cx_b, |editor, cx| { - editor.change_selections(true, cx, |s| { - s.select_ranges([Point::new(1, 31)..Point::new(1, 31)], None) + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(1, 31)..Point::new(1, 31)]) }); cx.focus(&editor_b); }); @@ -4452,7 +4452,7 @@ mod tests { // Move cursor to a location that can be renamed. let prepare_rename = editor_b.update(cx_b, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([7..7], None)); + editor.change_selections(None, cx, |s| s.select_ranges([7..7])); editor.rename(&Rename, cx).unwrap() }); @@ -5473,10 +5473,10 @@ mod tests { // When client B starts following client A, all visible view states are replicated to client B. editor_a1.update(cx_a, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([0..1], None)) + editor.change_selections(None, cx, |s| s.select_ranges([0..1])) }); editor_a2.update(cx_a, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([2..3], None)) + editor.change_selections(None, cx, |s| s.select_ranges([2..3])) }); workspace_b .update(cx_b, |workspace, cx| { @@ -5542,7 +5542,7 @@ mod tests { // Changes to client A's editor are reflected on client B. editor_a1.update(cx_a, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([1..1, 2..2], None)); + editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2])); }); editor_b1 .condition(cx_b, |editor, cx| { @@ -5556,7 +5556,7 @@ mod tests { .await; editor_a1.update(cx_a, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([3..3], None)); + editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.set_scroll_position(vec2f(0., 100.), cx); }); editor_b1 diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 31807f07d8..7fb7bffb1e 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -5,7 +5,7 @@ use collections::{BTreeSet, HashSet}; use editor::{ diagnostic_block_renderer, display_map::{BlockDisposition, BlockId, BlockProperties, RenderBlock}, - highlight_diagnostic_message, Editor, ExcerptId, MultiBuffer, ToOffset, + highlight_diagnostic_message, Autoscroll, Editor, ExcerptId, MultiBuffer, ToOffset, }; use gpui::{ actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity, @@ -418,7 +418,7 @@ impl ProjectDiagnosticsEditor { } else { groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice(); new_excerpt_ids_by_selection_id = - editor.change_selections(true, cx, |s| s.refresh()); + editor.change_selections(Some(Autoscroll::Fit), cx, |s| s.refresh()); selections = editor .selections .interleaved::(&editor.buffer().read(cx).read(cx)); @@ -444,8 +444,8 @@ impl ProjectDiagnosticsEditor { } } } - editor.change_selections(true, cx, |s| { - s.select(selections, None); + editor.change_selections(None, cx, |s| { + s.select(selections); }); Some(()) }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b01bee0ab4..6d907b2b76 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -537,6 +537,7 @@ struct BracketPairState { pair: BracketPair, } +#[derive(Debug)] struct SnippetState { ranges: Vec>>, active_index: usize, @@ -1304,25 +1305,12 @@ impl Editor { } } - pub fn change_selections( + fn selections_did_change( &mut self, local: bool, + old_cursor_position: &Anchor, cx: &mut ViewContext, - change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R, - ) -> R { - let old_cursor_position = self.selections.newest_anchor().head(); - self.push_to_selection_history(); - - #[allow(deprecated)] - // Using deprecated to prevent using change_with outside of this function - let (autoscroll, result) = - self.selections - .change_with(self.display_map.clone(), self.buffer.clone(), cx, change); - - if let Some(autoscroll) = autoscroll { - self.request_autoscroll(autoscroll, cx); - } - + ) { if self.focused && self.leader_replica_id.is_none() { self.buffer.update(cx, |buffer, cx| { buffer.set_active_selections(&self.selections.disjoint_anchors(), cx) @@ -1337,20 +1325,21 @@ impl Editor { self.select_next_state = None; self.select_larger_syntax_node_stack.clear(); self.autoclose_stack - .invalidate(&self.selections.disjoint_anchors(), &buffer); + .invalidate(&self.selections.disjoint_anchors(), buffer); self.snippet_stack - .invalidate(&self.selections.disjoint_anchors(), &buffer); + .invalidate(&self.selections.disjoint_anchors(), buffer); self.take_rename(false, cx); let new_cursor_position = self.selections.newest_anchor().head(); self.push_to_nav_history( old_cursor_position.clone(), - Some(new_cursor_position.to_point(&buffer)), + Some(new_cursor_position.to_point(buffer)), cx, ); if local { + let new_cursor_position = self.selections.newest_anchor().head(); let completion_menu = match self.context_menu.as_mut() { Some(ContextMenu::Completions(menu)) => Some(menu), _ => { @@ -1360,13 +1349,13 @@ impl Editor { }; if let Some(completion_menu) = completion_menu { - let cursor_position = new_cursor_position.to_offset(&buffer); + let cursor_position = new_cursor_position.to_offset(buffer); let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position.clone()); if kind == Some(CharKind::Word) && word_range.to_inclusive().contains(&cursor_position) { - let query = Self::completion_query(&buffer, cursor_position); + let query = Self::completion_query(buffer, cursor_position); cx.background() .block(completion_menu.filter(query.as_deref(), cx.background().clone())); self.show_completions(&ShowCompletions, cx); @@ -1387,6 +1376,25 @@ impl Editor { self.pause_cursor_blinking(cx); cx.emit(Event::SelectionsChanged { local }); cx.notify(); + } + + pub fn change_selections( + &mut self, + autoscroll: Option, + cx: &mut ViewContext, + change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R, + ) -> R { + let old_cursor_position = self.selections.newest_anchor().head(); + self.push_to_selection_history(); + + let result = + self.selections + .change_with(self.display_map.clone(), self.buffer.clone(), cx, change); + + if let Some(autoscroll) = autoscroll { + self.request_autoscroll(autoscroll, cx); + } + self.selections_did_change(true, &old_cursor_position, cx); result } @@ -1466,7 +1474,7 @@ impl Editor { let position = position.to_offset(&display_map, Bias::Left); let tail_anchor = display_map.buffer_snapshot.anchor_before(tail); - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { let mut pending = s .pending_mut() .as_mut() @@ -1540,7 +1548,7 @@ impl Editor { } } - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { if add { if click_count > 1 { s.delete(newest_selection.id); @@ -1664,7 +1672,7 @@ impl Editor { pending.reversed = false; } - self.change_selections(true, cx, |s| { + self.change_selections(None, cx, |s| { s.pending_mut().as_mut().unwrap().selection = pending; }); } else { @@ -1682,8 +1690,8 @@ impl Editor { let selections = self .selections .interleaved::(&self.buffer.read(cx).snapshot(cx)); - self.change_selections(true, cx, |s| { - s.select(selections, None); + self.change_selections(None, cx, |s| { + s.select(selections); s.clear_pending(); }); } @@ -1723,8 +1731,8 @@ impl Editor { }) .collect::>(); - self.change_selections(true, cx, |s| { - s.select_ranges(selection_ranges, None); + self.change_selections(None, cx, |s| { + s.select_ranges(selection_ranges); }); cx.notify(); } @@ -1752,7 +1760,7 @@ impl Editor { return; } - if self.change_selections(true, cx, |s| s.try_cancel()) { + if self.change_selections(None, cx, |s| s.try_cancel()) { self.request_autoscroll(Autoscroll::Fit, cx); return; } @@ -1762,7 +1770,7 @@ impl Editor { } #[cfg(any(test, feature = "test-support"))] - pub fn selected_ranges>( + pub fn selected_ranges + std::fmt::Debug>( &self, cx: &AppContext, ) -> Vec> { @@ -1893,9 +1901,7 @@ impl Editor { }) .collect(); - this.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)) - }); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections)); this.request_autoscroll(Autoscroll::Fit, cx); }); @@ -1941,8 +1947,8 @@ impl Editor { .collect() }; - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }) }); } @@ -2004,7 +2010,7 @@ impl Editor { } drop(snapshot); - self.change_selections(true, cx, |s| s.select_anchors(selections, None)); + self.change_selections(None, cx, |s| s.select_anchors(selections)); true } else { false @@ -2102,8 +2108,8 @@ impl Editor { }); if let Some(new_selections) = new_selections { - self.change_selections(true, cx, |s| { - s.select(new_selections, None); + self.change_selections(None, cx, |s| { + s.select(new_selections); }); } if let Some(bracket_pair_state) = bracket_pair_state { @@ -2147,8 +2153,8 @@ impl Editor { }) .collect(); self.autoclose_stack.pop(); - self.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(new_selections); }); true } else { @@ -2739,8 +2745,8 @@ impl Editor { }); if let Some(tabstop) = tabstops.first() { - self.change_selections(true, cx, |s| { - s.select_ranges(tabstop.iter().cloned(), Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(tabstop.iter().cloned()); }); self.snippet_stack.push(SnippetState { active_index: 0, @@ -2780,15 +2786,18 @@ impl Editor { } } if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) { - self.change_selections(true, cx, |s| { - s.select_anchor_ranges( - current_ranges.into_iter().cloned(), - Some(Autoscroll::Fit), - ) - }); + let snapshot = self.buffer.read(cx).snapshot(cx); + let current_ranges_resolved = current_ranges + .iter() + .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot)) + .collect::>(); + dbg!(¤t_ranges, current_ranges_resolved); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_anchor_ranges(current_ranges.into_iter().cloned()) + }); // If snippet state is not at the last tabstop, push it back on the stack - if snippet.active_index + 1 != snippet.ranges.len() { + if snippet.active_index + 1 < snippet.ranges.len() { self.snippet_stack.push(snippet); } return true; @@ -2836,14 +2845,14 @@ impl Editor { } self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| s.select(selections, Some(Autoscroll::Fit))); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); this.insert("", cx); }); } pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if selection.is_empty() { let cursor = movement::right(map, selection.head()); @@ -2895,8 +2904,8 @@ impl Editor { selection.end = selection.start; } }); - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); }); } else { @@ -2962,8 +2971,8 @@ impl Editor { } }); - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); }); } @@ -3016,8 +3025,8 @@ impl Editor { ); }); let snapshot = this.buffer.read(cx).snapshot(cx); - this.change_selections(true, cx, |s| { - s.select(s.interleaved::(&snapshot), Some(Autoscroll::Fit)) + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(s.interleaved::(&snapshot)) }); }); } @@ -3099,8 +3108,8 @@ impl Editor { }) .collect(); - this.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(new_selections); }); }); } @@ -3251,8 +3260,8 @@ impl Editor { } }); this.fold_ranges(refold_ranges, cx); - this.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(new_selections); }) }); } @@ -3356,15 +3365,13 @@ impl Editor { } }); this.fold_ranges(refold_ranges, cx); - this.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)) - }); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections)); }); } pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - let edits = this.change_selections(true, cx, |s| { + let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| { let mut edits: Vec<(Range, String)> = Default::default(); s.move_with(|display_map, selection| { if !selection.is_empty() { @@ -3406,8 +3413,8 @@ impl Editor { }); this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); let buffer = this.buffer.read(cx).snapshot(cx); - this.change_selections(true, cx, |s| { - s.select(s.interleaved::(&buffer), Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(s.interleaved::(&buffer)); }); }); } @@ -3439,8 +3446,8 @@ impl Editor { } self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); this.insert("", cx); cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections)); @@ -3540,9 +3547,7 @@ impl Editor { let selections = this .selections .interleaved::(&this.buffer.read(cx).read(cx)); - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)) - }); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); } else { this.insert(&clipboard_text, cx); } @@ -3553,10 +3558,10 @@ impl Editor { pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext) { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) { if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() { - self.change_selections(true, cx, |s| { + self.change_selections(None, cx, |s| { // TODO: move to SelectionsCollection to preserve selection // invariants without rechecking - s.select_anchors(selections.to_vec(), None); + s.select_anchors(selections.to_vec()); }); } self.request_autoscroll(Autoscroll::Fit, cx); @@ -3568,10 +3573,10 @@ impl Editor { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) { if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned() { - self.change_selections(true, cx, |s| { + self.change_selections(None, cx, |s| { // TODO: move to SelectionsCollection to preserve selection // invariants without rechecking - s.select_anchors(selections.to_vec(), None); + s.select_anchors(selections.to_vec()); }); } self.request_autoscroll(Autoscroll::Fit, cx); @@ -3585,7 +3590,7 @@ impl Editor { } pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let cursor = if selection.is_empty() { movement::left(map, selection.start) @@ -3598,13 +3603,13 @@ impl Editor { } pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None)); }) } pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let cursor = if selection.is_empty() { movement::right(map, selection.end) @@ -3617,7 +3622,7 @@ impl Editor { } pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None)); }) } @@ -3638,7 +3643,7 @@ impl Editor { return; } - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if !selection.is_empty() { selection.goal = SelectionGoal::None; @@ -3650,7 +3655,7 @@ impl Editor { } pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false)) }) } @@ -3669,7 +3674,7 @@ impl Editor { return; } - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if !selection.is_empty() { selection.goal = SelectionGoal::None; @@ -3681,7 +3686,7 @@ impl Editor { } pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false)) }); } @@ -3691,7 +3696,7 @@ impl Editor { _: &MoveToPreviousWordStart, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { ( movement::previous_word_start(map, head), @@ -3706,7 +3711,7 @@ impl Editor { _: &MoveToPreviousSubwordStart, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { ( movement::previous_subword_start(map, head), @@ -3721,7 +3726,7 @@ impl Editor { _: &SelectToPreviousWordStart, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { ( movement::previous_word_start(map, head), @@ -3736,7 +3741,7 @@ impl Editor { _: &SelectToPreviousSubwordStart, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { ( movement::previous_subword_start(map, head), @@ -3752,7 +3757,7 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if selection.is_empty() { let cursor = movement::previous_word_start(map, selection.head()); @@ -3770,7 +3775,7 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if selection.is_empty() { let cursor = movement::previous_subword_start(map, selection.head()); @@ -3783,7 +3788,7 @@ impl Editor { } pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { (movement::next_word_end(map, head), SelectionGoal::None) }); @@ -3795,7 +3800,7 @@ impl Editor { _: &MoveToNextSubwordEnd, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { (movement::next_subword_end(map, head), SelectionGoal::None) }); @@ -3803,7 +3808,7 @@ impl Editor { } pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { (movement::next_word_end(map, head), SelectionGoal::None) }); @@ -3815,7 +3820,7 @@ impl Editor { _: &SelectToNextSubwordEnd, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { (movement::next_subword_end(map, head), SelectionGoal::None) }); @@ -3824,7 +3829,7 @@ impl Editor { pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if selection.is_empty() { let cursor = movement::next_word_end(map, selection.head()); @@ -3842,7 +3847,7 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { if selection.is_empty() { let cursor = movement::next_subword_end(map, selection.head()); @@ -3859,7 +3864,7 @@ impl Editor { _: &MoveToBeginningOfLine, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { ( movement::line_beginning(map, head, true), @@ -3874,7 +3879,7 @@ impl Editor { action: &SelectToBeginningOfLine, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { ( movement::line_beginning(map, head, action.stop_at_soft_wraps), @@ -3890,7 +3895,7 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - this.change_selections(true, cx, |s| { + this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|_, selection| { selection.reversed = true; }); @@ -3907,7 +3912,7 @@ impl Editor { } pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { (movement::line_end(map, head, true), SelectionGoal::None) }); @@ -3919,7 +3924,7 @@ impl Editor { action: &SelectToEndOfLine, cx: &mut ViewContext, ) { - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { ( movement::line_end(map, head, action.stop_at_soft_wraps), @@ -3959,8 +3964,8 @@ impl Editor { return; } - self.change_selections(true, cx, |s| { - s.select_ranges(vec![0..0], Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(vec![0..0]); }); } @@ -3970,8 +3975,8 @@ impl Editor { .last::(&self.buffer.read(cx).read(cx)); selection.set_head(Point::zero(), SelectionGoal::None); - self.change_selections(true, cx, |s| { - s.select(vec![selection], Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(vec![selection]); }); } @@ -3982,8 +3987,8 @@ impl Editor { } let cursor = self.buffer.read(cx).read(cx).len(); - self.change_selections(true, cx, |s| { - s.select_ranges(vec![cursor..cursor], Some(Autoscroll::Fit)) + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(vec![cursor..cursor]) }); } @@ -4028,15 +4033,15 @@ impl Editor { let buffer = self.buffer.read(cx).snapshot(cx); let mut selection = self.selections.first::(&buffer); selection.set_head(buffer.len(), SelectionGoal::None); - self.change_selections(true, cx, |s| { - s.select(vec![selection], Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(vec![selection]); }); } pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext) { let end = self.buffer.read(cx).read(cx).len(); - self.change_selections(true, cx, |s| { - s.select_ranges(vec![0..end], Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(vec![0..end]); }); } @@ -4052,8 +4057,8 @@ impl Editor { selection.end = cmp::min(max_point, Point::new(rows.end, 0)); selection.reversed = false; } - self.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); } @@ -4077,8 +4082,8 @@ impl Editor { } } self.unfold_ranges(to_unfold, true, cx); - self.change_selections(true, cx, |s| { - s.select_ranges(new_selection_ranges, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(new_selection_ranges); }); } @@ -4180,8 +4185,8 @@ impl Editor { state.stack.pop(); } - self.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(new_selections); }); if state.stack.len() > 1 { self.add_selections_state = Some(state); @@ -4229,11 +4234,11 @@ impl Editor { if let Some(next_selected_range) = next_selected_range { self.unfold_ranges([next_selected_range.clone()], false, cx); - self.change_selections(true, cx, |s| { + self.change_selections(Some(Autoscroll::Newest), cx, |s| { if action.replace_newest { s.delete(s.newest_anchor().id); } - s.insert_range(next_selected_range, Some(Autoscroll::Newest)); + s.insert_range(next_selected_range); }); } else { select_next_state.done = true; @@ -4262,8 +4267,8 @@ impl Editor { done: false, }; self.unfold_ranges([selection.start..selection.end], false, cx); - self.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Newest)); + self.change_selections(Some(Autoscroll::Newest), cx, |s| { + s.select(selections); }); self.select_next_state = Some(select_state); } else { @@ -4388,8 +4393,8 @@ impl Editor { let selections = this .selections .interleaved::(&this.buffer.read(cx).read(cx)); - this.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); }); } @@ -4437,8 +4442,8 @@ impl Editor { if selected_larger_node { stack.push(old_selections); - self.change_selections(true, cx, |s| { - s.select(new_selections, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(new_selections); }); } self.select_larger_syntax_node_stack = stack; @@ -4451,8 +4456,8 @@ impl Editor { ) { let mut stack = mem::take(&mut self.select_larger_syntax_node_stack); if let Some(selections) = stack.pop() { - self.change_selections(true, cx, |s| { - s.select(selections.to_vec(), Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections.to_vec()); }); } self.select_larger_syntax_node_stack = stack; @@ -4482,8 +4487,8 @@ impl Editor { } } - self.change_selections(true, cx, |s| { - s.select(selections, Some(Autoscroll::Fit)); + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select(selections); }); } @@ -4491,10 +4496,10 @@ impl Editor { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Undoing; if let Some(entry) = self.selection_history.undo_stack.pop_back() { - self.change_selections(true, cx, |s| { + self.change_selections(None, cx, |s| { // TODO: Move to selections so selections invariants can be preserved rather than // rechecking them. - s.select_anchors(entry.selections.to_vec(), None) + s.select_anchors(entry.selections.to_vec()) }); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; @@ -4507,10 +4512,10 @@ impl Editor { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Redoing; if let Some(entry) = self.selection_history.redo_stack.pop_back() { - self.change_selections(true, cx, |s| { + self.change_selections(None, cx, |s| { // TODO: Move to selections so selections invariants can be preserved rather than // rechecking them. - s.select_anchors(entry.selections.to_vec(), None) + s.select_anchors(entry.selections.to_vec()) }); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; @@ -4566,17 +4571,14 @@ impl Editor { if let Some((primary_range, group_id)) = group { self.activate_diagnostics(group_id, cx); - self.change_selections(true, cx, |s| { - s.select( - vec![Selection { - id: selection.id, - start: primary_range.start, - end: primary_range.start, - reversed: false, - goal: SelectionGoal::None, - }], - Some(Autoscroll::Center), - ); + self.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select(vec![Selection { + id: selection.id, + start: primary_range.start, + end: primary_range.start, + reversed: false, + goal: SelectionGoal::None, + }]); }); break; } else { @@ -4640,8 +4642,8 @@ impl Editor { if editor_handle != target_editor_handle { nav_history.borrow_mut().disable(); } - target_editor.change_selections(true, cx, |s| { - s.select_ranges([range], Some(Autoscroll::Center)); + target_editor.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select_ranges([range]); }); nav_history.borrow_mut().enable(); @@ -4945,7 +4947,7 @@ impl Editor { reversed: false, goal: SelectionGoal::None, }; - self.change_selections(true, cx, |s| s.select(vec![new_selection], None)); + self.change_selections(None, cx, |s| s.select(vec![new_selection])); } Some(rename) @@ -5089,9 +5091,12 @@ impl Editor { selections: Vec>, cx: &mut ViewContext, ) { - self.change_selections(false, cx, |s| { - s.select_anchors(selections, None); - }); + let old_cursor_position = self.selections.newest_anchor().head(); + self.selections + .change_with(self.display_map.clone(), self.buffer.clone(), cx, |s| { + s.select_anchors(selections); + }); + self.selections_did_change(false, &old_cursor_position, cx); } fn push_to_selection_history(&mut self) { @@ -5650,8 +5655,8 @@ impl Editor { for (buffer, ranges) in new_selections_by_buffer.into_iter() { let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { - s.select_ranges(ranges, Some(Autoscroll::Newest)); + editor.change_selections(Some(Autoscroll::Newest), cx, |s| { + s.select_ranges(ranges); }); }); } @@ -6294,7 +6299,7 @@ mod tests { // No event is emitted when the mutation is a no-op. editor2.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([0..0], None)); + editor.change_selections(None, cx, |s| s.select_ranges([0..0])); editor.backspace(&Backspace, cx); }); @@ -6312,7 +6317,7 @@ mod tests { editor.update(cx, |editor, cx| { editor.start_transaction_at(now, cx); - editor.change_selections(true, cx, |s| s.select_ranges([2..4], None)); + editor.change_selections(None, cx, |s| s.select_ranges([2..4])); editor.insert("cd", cx); editor.end_transaction_at(now, cx); @@ -6320,14 +6325,14 @@ mod tests { assert_eq!(editor.selected_ranges(cx), vec![4..4]); editor.start_transaction_at(now, cx); - editor.change_selections(true, cx, |s| s.select_ranges([4..5], None)); + editor.change_selections(None, cx, |s| s.select_ranges([4..5])); editor.insert("e", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cde6"); assert_eq!(editor.selected_ranges(cx), vec![5..5]); now += group_interval + Duration::from_millis(1); - editor.change_selections(true, cx, |s| s.select_ranges([2..2], None)); + editor.change_selections(None, cx, |s| s.select_ranges([2..2])); // Simulate an edit in another editor buffer.update(cx, |buffer, cx| { @@ -6481,17 +6486,17 @@ mod tests { // Move the cursor a small distance. // Nothing is added to the navigation history. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) }); - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) }); assert!(nav_history.borrow_mut().pop_backward().is_none()); // Move the cursor a large distance. // The history can jump back to the previous position. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)]) }); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); @@ -6640,7 +6645,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]); }); view.fold(&Fold, cx); @@ -6758,7 +6763,7 @@ mod tests { &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]); }); view.select_to_beginning(&SelectToBeginning, cx); @@ -6882,7 +6887,7 @@ mod tests { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]); }); view.move_down(&MoveDown, cx); @@ -6929,7 +6934,7 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\n def", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), @@ -7089,7 +7094,7 @@ mod tests { let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4), @@ -7200,7 +7205,7 @@ mod tests { "use one::{\n two::three::\n four::five\n};" ); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]); }); @@ -7251,7 +7256,7 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges(ranges, None)); + editor.change_selections(None, cx, |s| s.select_ranges(ranges)); editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); assert_eq!(editor.text(cx), " four"); }); @@ -7264,7 +7269,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ // an empty selection - the preceding word fragment is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -7278,7 +7283,7 @@ mod tests { assert_eq!(buffer.read(cx).read(cx).text(), "e two te four"); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ // an empty selection - the following word fragment is deleted DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), @@ -7299,7 +7304,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -7332,14 +7337,11 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(buffer.clone(), cx); - editor.change_selections(true, cx, |s| { - s.select_ranges( - [ - Point::new(2, 4)..Point::new(2, 5), - Point::new(5, 4)..Point::new(5, 5), - ], - None, - ) + editor.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(2, 4)..Point::new(2, 5), + Point::new(5, 4)..Point::new(5, 5), + ]) }); editor }); @@ -7403,7 +7405,7 @@ mod tests { let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(buffer.clone(), cx); - editor.change_selections(true, cx, |s| s.select_ranges([3..4, 11..12, 19..20], None)); + editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20])); editor }); @@ -7652,7 +7654,7 @@ mod tests { view.update(cx, |view, cx| { view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ // an empty selection - the preceding character is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -7666,7 +7668,7 @@ mod tests { assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n"); view.set_text(" one\n two\n three\n four", cx); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ // cursors at the the end of leading indent - last indent is deleted DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), @@ -7694,7 +7696,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ // an empty selection - the following character is deleted DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -7719,7 +7721,7 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), @@ -7741,7 +7743,7 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)]) }); view.delete_line(&DeleteLine, cx); @@ -7759,7 +7761,7 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -7783,7 +7785,7 @@ mod tests { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1), @@ -7815,7 +7817,7 @@ mod tests { ], cx, ); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), @@ -7912,8 +7914,8 @@ mod tests { }], cx, ); - editor.change_selections(true, cx, |s| { - s.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None) + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(2, 0)..Point::new(2, 0)]) }); editor.move_line_down(&MoveLineDown, cx); }); @@ -7926,7 +7928,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); - editor.change_selections(true, cx, |s| s.select_ranges([1..1], None)); + editor.change_selections(None, cx, |s| s.select_ranges([1..1])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); assert_eq!(editor.selected_ranges(cx), [2..2]); @@ -7946,12 +7948,12 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.change_selections(true, cx, |s| s.select_ranges([3..3], None)); + editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acb\nde"); assert_eq!(editor.selected_ranges(cx), [3..3]); - editor.change_selections(true, cx, |s| s.select_ranges([4..4], None)); + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); assert_eq!(editor.selected_ranges(cx), [5..5]); @@ -7971,7 +7973,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.change_selections(true, cx, |s| s.select_ranges([1..1, 2..2, 4..4], None)); + editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bacd\ne"); assert_eq!(editor.selected_ranges(cx), [2..2, 3..3, 5..5]); @@ -7999,7 +8001,7 @@ mod tests { cx.add_window(Default::default(), |cx| { let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); - editor.change_selections(true, cx, |s| s.select_ranges([4..4], None)); + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); assert_eq!(editor.selected_ranges(cx), [8..8]); @@ -8027,18 +8029,14 @@ mod tests { // Cut with three selections. Clipboard text is divided into three slices. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { - s.select_ranges(vec![0..7, 11..17, 22..27], None) - }); + view.change_selections(None, cx, |s| s.select_ranges(vec![0..7, 11..17, 22..27])); view.cut(&Cut, cx); assert_eq!(view.display_text(cx), "two four six "); }); // Paste with three cursors. Each cursor pastes one slice of the clipboard text. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { - s.select_ranges(vec![4..4, 9..9, 13..13], None) - }); + view.change_selections(None, cx, |s| s.select_ranges(vec![4..4, 9..9, 13..13])); view.paste(&Paste, cx); assert_eq!(view.display_text(cx), "two one✅ four three six five "); assert_eq!( @@ -8055,7 +8053,7 @@ mod tests { // match the number of slices in the clipboard, the entire clipboard text // is pasted at each cursor. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| s.select_ranges(vec![0..0, 31..31], None)); + view.change_selections(None, cx, |s| s.select_ranges(vec![0..0, 31..31])); view.handle_input(&Input("( ".into()), cx); view.paste(&Paste, cx); view.handle_input(&Input(") ".into()), cx); @@ -8066,7 +8064,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| s.select_ranges(vec![0..0], None)); + view.change_selections(None, cx, |s| s.select_ranges(vec![0..0])); view.handle_input(&Input("123\n4567\n89\n".into()), cx); assert_eq!( view.display_text(cx), @@ -8076,7 +8074,7 @@ mod tests { // Cut with three selections, one of which is full-line. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| s.select_display_ranges( + view.change_selections(None, cx, |s| s.select_display_ranges( [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), @@ -8093,7 +8091,7 @@ mod tests { // Paste with three selections, noticing how the copied selection that was full-line // gets inserted before the second cursor. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| s.select_display_ranges( + view.change_selections(None, cx, |s| s.select_display_ranges( [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), @@ -8117,7 +8115,7 @@ mod tests { // Copy with a single cursor only, which writes the whole line into the clipboard. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]) }); view.copy(&Copy, cx); @@ -8126,7 +8124,7 @@ mod tests { // Paste with three selections, noticing how the copied full-line selection is inserted // before the empty selections but replaces the selection that is non-empty. view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| s.select_display_ranges( + view.change_selections(None, cx, |s| s.select_display_ranges( [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2), @@ -8169,7 +8167,7 @@ mod tests { let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -8221,7 +8219,7 @@ mod tests { ], cx, ); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -8250,7 +8248,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)]) }); view.split_selection_into_lines(&SplitSelectionIntoLines, cx); @@ -8281,7 +8279,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]) }); }); @@ -8353,7 +8351,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]) }); }); @@ -8396,7 +8394,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)]) }); view.add_selection_below(&AddSelectionBelow, cx); @@ -8436,7 +8434,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)]) }); }); @@ -8475,8 +8473,8 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { - s.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None) + view.change_selections(None, cx, |s| { + s.select_ranges([ranges[1].start + 1..ranges[1].start + 1]) }); view.select_next( &SelectNext { @@ -8542,7 +8540,7 @@ mod tests { .await; view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), @@ -8701,7 +8699,7 @@ mod tests { .await; editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| s.select_ranges([5..5, 8..8, 9..9], None)); + editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9])); editor.newline(&Newline, cx); assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); assert_eq!( @@ -8755,7 +8753,7 @@ mod tests { .await; view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), @@ -8806,7 +8804,7 @@ mod tests { ); view.undo(&Undo, cx); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), @@ -8827,7 +8825,7 @@ mod tests { // Don't autoclose if the next character isn't whitespace and isn't // listed in the language's "autoclose_before" section. view.finalize_last_transaction(cx); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) }); view.handle_input(&Input("{".to_string()), cx); @@ -8843,7 +8841,7 @@ mod tests { ); view.undo(&Undo, cx); - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)]) }); view.handle_input(&Input("{".to_string()), cx); @@ -8931,6 +8929,14 @@ mod tests { ); assert!(editor.move_to_next_snippet_tabstop(cx)); + assert( + editor, + cx, + indoc! {" + a.f(one, , three) b + a.f(one, , three) b + a.f(one, , three) b"}, + ); assert!(editor.move_to_next_snippet_tabstop(cx)); assert( editor, @@ -9113,8 +9119,8 @@ mod tests { editor.update(cx, |editor, cx| { editor.project = Some(project); - editor.change_selections(true, cx, |s| { - s.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None) + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(0, 3)..Point::new(0, 3)]) }); editor.handle_input(&Input(".".to_string()), cx); }); @@ -9168,14 +9174,11 @@ mod tests { ); editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { - s.select_ranges( - [ - Point::new(1, 3)..Point::new(1, 3), - Point::new(2, 5)..Point::new(2, 5), - ], - None, - ) + editor.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(1, 3)..Point::new(1, 3), + Point::new(2, 5)..Point::new(2, 5), + ]) }); editor.handle_input(&Input(" ".to_string()), cx); @@ -9329,7 +9332,7 @@ mod tests { view.update(cx, |editor, cx| { // If multiple selections intersect a line, the line is only // toggled once. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3), DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6), @@ -9350,7 +9353,7 @@ mod tests { // The comment prefix is inserted at the same column for every line // in a selection. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)]) }); editor.toggle_comments(&ToggleComments, cx); @@ -9367,7 +9370,7 @@ mod tests { ); // If a selection ends at the beginning of a line, that line is not toggled. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)]) }); editor.toggle_comments(&ToggleComments, cx); @@ -9407,14 +9410,11 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx)); view.update(cx, |view, cx| { assert_eq!(view.text(cx), "aaaa\nbbbb"); - view.change_selections(true, cx, |s| { - s.select_ranges( - [ - Point::new(0, 0)..Point::new(0, 0), - Point::new(1, 0)..Point::new(1, 0), - ], - None, - ) + view.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(0, 0)..Point::new(0, 0), + Point::new(1, 0)..Point::new(1, 0), + ]) }); view.handle_input(&Input("X".to_string()), cx); @@ -9451,7 +9451,7 @@ mod tests { b|bb|b cccc"}); assert_eq!(view.text(cx), expected_text); - view.change_selections(true, cx, |s| s.select_ranges(selection_ranges, None)); + view.change_selections(None, cx, |s| s.select_ranges(selection_ranges)); view.handle_input(&Input("X".to_string()), cx); @@ -9505,8 +9505,8 @@ mod tests { let (_, editor) = cx.add_window(Default::default(), |cx| { let mut editor = build_editor(multibuffer.clone(), cx); let snapshot = editor.snapshot(cx); - editor.change_selections(true, cx, |s| { - s.select_ranges([Point::new(1, 3)..Point::new(1, 3)], None) + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(1, 3)..Point::new(1, 3)]) }); editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); assert_eq!( @@ -9521,7 +9521,7 @@ mod tests { // Refreshing selections is a no-op when excerpts haven't changed. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.refresh(); }); assert_eq!( @@ -9548,7 +9548,7 @@ mod tests { // Refreshing selections will relocate the first selection to the original buffer // location. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.refresh(); }); assert_eq!( @@ -9607,7 +9607,7 @@ mod tests { ); // Ensure we don't panic when selections are refreshed and that the pending selection is finalized. - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.refresh(); }); assert_eq!( @@ -9657,7 +9657,7 @@ mod tests { .await; view.update(cx, |view, cx| { - view.change_selections(true, cx, |s| { + view.change_selections(None, cx, |s| { s.select_display_ranges([ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), @@ -9796,7 +9796,7 @@ mod tests { // Update the selections only leader.update(cx, |leader, cx| { - leader.change_selections(true, cx, |s| s.select_ranges([1..1], None)); + leader.change_selections(None, cx, |s| s.select_ranges([1..1])); }); follower.update(cx, |follower, cx| { follower @@ -9821,7 +9821,7 @@ mod tests { // Update the selections and scroll position leader.update(cx, |leader, cx| { - leader.change_selections(true, cx, |s| s.select_ranges([0..0], None)); + leader.change_selections(None, cx, |s| s.select_ranges([0..0])); leader.request_autoscroll(Autoscroll::Newest, cx); leader.set_scroll_position(vec2f(1.5, 3.5), cx); }); @@ -9837,7 +9837,7 @@ mod tests { // Creating a pending selection that precedes another selection leader.update(cx, |leader, cx| { - leader.change_selections(true, cx, |s| s.select_ranges([1..1], None)); + leader.change_selections(None, cx, |s| s.select_ranges([1..1])); leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx); }); follower.update(cx, |follower, cx| { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index b87719c130..1652334b38 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -276,8 +276,8 @@ impl Item for Editor { let nav_history = self.nav_history.take(); self.scroll_position = data.scroll_position; self.scroll_top_anchor = scroll_top_anchor; - self.change_selections(true, cx, |s| { - s.select_ranges([offset..offset], Some(Autoscroll::Fit)) + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges([offset..offset]) }); self.nav_history = nav_history; true diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index fc2f563099..cbe4f0fbef 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -12,8 +12,7 @@ use util::post_inc; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, - Anchor, Autoscroll, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, - ToOffset, + Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset, }; #[derive(Clone)] @@ -79,10 +78,10 @@ impl SelectionsCollection { pub fn interleaved<'a, D>(&self, buffer: &MultiBufferSnapshot) -> Vec> where - D: 'a + TextDimension + Ord + Sub, + D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, { - let anchor_disjoint = &self.disjoint; - let mut disjoint = resolve_multiple::(anchor_disjoint.iter(), &buffer).peekable(); + let disjoint_anchors = &self.disjoint; + let mut disjoint = resolve_multiple::(disjoint_anchors.iter(), &buffer).peekable(); let mut pending_opt = self.pending::(&buffer); @@ -197,36 +196,31 @@ impl SelectionsCollection { self.interleaved(&snapshot).last().unwrap().clone() } - // NOTE do not use. This should only be called from Editor::change_selections. - #[deprecated] - pub fn change_with( + pub(crate) fn change_with( &mut self, display_map: ModelHandle, buffer: ModelHandle, cx: &mut MutableAppContext, change: impl FnOnce(&mut MutableSelectionsCollection) -> R, - ) -> (Option, R) { + ) -> R { let mut mutable_collection = MutableSelectionsCollection { collection: self, - autoscroll: None, display_map, buffer, cx, }; let result = change(&mut mutable_collection); - assert!( !mutable_collection.disjoint.is_empty() || mutable_collection.pending.is_some(), "There must be at least one selection" ); - (mutable_collection.autoscroll, result) + result } } pub struct MutableSelectionsCollection<'a> { collection: &'a mut SelectionsCollection, - pub autoscroll: Option, buffer: ModelHandle, display_map: ModelHandle, cx: &'a mut MutableAppContext, @@ -307,7 +301,7 @@ impl<'a> MutableSelectionsCollection<'a> { } } - pub fn insert_range(&mut self, range: Range, autoscroll: Option) + pub fn insert_range(&mut self, range: Range) where T: 'a + ToOffset @@ -335,10 +329,10 @@ impl<'a> MutableSelectionsCollection<'a> { reversed, goal: SelectionGoal::None, }); - self.select(selections, autoscroll); + self.select(selections); } - pub fn select(&mut self, mut selections: Vec>, autoscroll: Option) + pub fn select(&mut self, mut selections: Vec>) where T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug, { @@ -360,8 +354,6 @@ impl<'a> MutableSelectionsCollection<'a> { } } - self.autoscroll = autoscroll.or(self.autoscroll.take()); - self.collection.disjoint = Arc::from_iter(selections.into_iter().map(|selection| { let end_bias = if selection.end > selection.start { Bias::Left @@ -380,46 +372,14 @@ impl<'a> MutableSelectionsCollection<'a> { self.collection.pending = None; } - pub fn select_anchors( - &mut self, - mut selections: Vec>, - autoscroll: Option, - ) { + pub fn select_anchors(&mut self, selections: Vec>) { let buffer = self.buffer.read(self.cx).snapshot(self.cx); - selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, &buffer)); - - // Merge overlapping selections. - let mut i = 1; - while i < selections.len() { - if selections[i - 1] - .end - .cmp(&selections[i].start, &buffer) - .is_ge() - { - let removed = selections.remove(i); - if removed.start.cmp(&selections[i - 1].start, &buffer).is_lt() { - selections[i - 1].start = removed.start; - } - if removed.end.cmp(&selections[i - 1].end, &buffer).is_gt() { - selections[i - 1].end = removed.end; - } - } else { - i += 1; - } - } - - self.autoscroll = autoscroll.or(self.autoscroll.take()); - - self.collection.disjoint = Arc::from_iter( - selections - .into_iter() - .map(|selection| reset_biases(selection, &buffer)), - ); - - self.collection.pending = None; + let resolved_selections = + resolve_multiple::(&selections, &buffer).collect::>(); + self.select(resolved_selections); } - pub fn select_ranges(&mut self, ranges: I, autoscroll: Option) + pub fn select_ranges(&mut self, ranges: I) where I: IntoIterator>, T: ToOffset, @@ -446,14 +406,10 @@ impl<'a> MutableSelectionsCollection<'a> { }) .collect::>(); - self.select(selections, autoscroll) + self.select(selections) } - pub fn select_anchor_ranges>>( - &mut self, - ranges: I, - autoscroll: Option, - ) { + pub fn select_anchor_ranges>>(&mut self, ranges: I) { let buffer = self.buffer.read(self.cx).snapshot(self.cx); let selections = ranges .into_iter() @@ -476,7 +432,7 @@ impl<'a> MutableSelectionsCollection<'a> { }) .collect::>(); - self.select_anchors(selections, autoscroll) + self.select_anchors(selections) } #[cfg(any(test, feature = "test-support"))] @@ -505,7 +461,7 @@ impl<'a> MutableSelectionsCollection<'a> { } }) .collect(); - self.select(selections, None); + self.select(selections); } pub fn move_with( @@ -523,7 +479,7 @@ impl<'a> MutableSelectionsCollection<'a> { }) .collect(); - self.select(selections, Some(Autoscroll::Fit)) + self.select(selections) } pub fn move_heads_with( @@ -572,7 +528,7 @@ impl<'a> MutableSelectionsCollection<'a> { } }) .collect(); - self.select(new_selections, None); + self.select(new_selections); } /// Compute new ranges for any selections that were located in excerpts that have @@ -620,10 +576,7 @@ impl<'a> MutableSelectionsCollection<'a> { .collect(); if !adjusted_disjoint.is_empty() { - self.select::( - resolve_multiple(adjusted_disjoint.iter(), &buffer).collect(), - None, - ); + self.select::(resolve_multiple(adjusted_disjoint.iter(), &buffer).collect()); } if let Some(pending) = pending.as_mut() { @@ -665,12 +618,16 @@ pub fn resolve_multiple<'a, D, I>( snapshot: &MultiBufferSnapshot, ) -> impl 'a + Iterator> where - D: TextDimension + Ord + Sub, + D: TextDimension + Ord + Sub + std::fmt::Debug, I: 'a + IntoIterator>, { let (to_summarize, selections) = selections.into_iter().tee(); let mut summaries = snapshot - .summaries_for_anchors::(to_summarize.flat_map(|s| [&s.start, &s.end])) + .summaries_for_anchors::( + to_summarize + .flat_map(|s| [&s.start, &s.end]) + .collect::>(), + ) .into_iter(); selections.map(move |s| Selection { id: s.id, @@ -692,7 +649,7 @@ fn reset_biases( mut selection: Selection, buffer: &MultiBufferSnapshot, ) -> Selection { - let end_bias = if selection.end.cmp(&selection.start, buffer).is_gt() { + let end_bias = if selection.end.to_offset(buffer) > selection.start.to_offset(buffer) { Bias::Left } else { Bias::Right diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 407733c771..d707e55c3a 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -43,7 +43,7 @@ pub fn marked_display_snapshot( pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext) { let (umarked_text, text_ranges) = marked_text_ranges(marked_text); assert_eq!(editor.text(cx), umarked_text); - editor.change_selections(true, cx, |s| s.select_ranges(text_ranges, None)); + editor.change_selections(None, cx, |s| s.select_ranges(text_ranges)); } pub fn assert_text_with_selections( diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 2f7219aa6f..bb889a5b37 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -80,8 +80,8 @@ impl GoToLine { if let Some(rows) = active_editor.highlighted_rows() { let snapshot = active_editor.snapshot(cx).display_snapshot; let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot); - active_editor.change_selections(true, cx, |s| { - s.select_ranges([position..position], Some(Autoscroll::Center)) + active_editor.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select_ranges([position..position]) }); } }); diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index ff0dfa5145..dd105fd4e3 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -57,8 +57,8 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { if let Some(editor) = item.downcast::() { editor.update(&mut cx, |editor, cx| { let len = editor.buffer().read(cx).read(cx).len(); - editor.change_selections(true, cx, |s| { - s.select_ranges([len..len], Some(Autoscroll::Center)) + editor.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select_ranges([len..len]) }); if len > 0 { editor.insert("\n\n", cx); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 7383b4b3a9..12f5168c9d 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -215,8 +215,8 @@ impl PickerDelegate for OutlineView { if let Some(rows) = active_editor.highlighted_rows() { let snapshot = active_editor.snapshot(cx).display_snapshot; let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot); - active_editor.change_selections(true, cx, |s| { - s.select_ranges([position..position], Some(Autoscroll::Center)) + active_editor.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select_ranges([position..position]) }); } }); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 80530ff5e6..a18039ecfd 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -145,8 +145,8 @@ impl ProjectSymbolsView { let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { - s.select_ranges([position..position], Some(Autoscroll::Center)) + editor.change_selections(Some(Autoscroll::Center), cx, |s| { + s.select_ranges([position..position]) }); }); }); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 95ef808ab0..00205b634c 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -395,8 +395,8 @@ impl BufferSearchBar { ); let range_to_select = ranges[new_index].clone(); editor.unfold_ranges([range_to_select.clone()], false, cx); - editor.change_selections(true, cx, |s| { - s.select_ranges([range_to_select], Some(Autoscroll::Fit)) + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges([range_to_select]) }); } }); @@ -538,12 +538,11 @@ impl BufferSearchBar { editor.update(cx, |editor, cx| { if select_closest_match { if let Some(match_ix) = this.active_match_index { - editor.change_selections(true, cx, |s| { - s.select_ranges( - [ranges[match_ix].clone()], - Some(Autoscroll::Fit), - ) - }); + editor.change_selections( + Some(Autoscroll::Fit), + cx, + |s| s.select_ranges([ranges[match_ix].clone()]), + ); } } @@ -725,7 +724,7 @@ mod tests { }); editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) }); }); @@ -810,7 +809,7 @@ mod tests { // Park the cursor in between matches and ensure that going to the previous match selects // the closest match to the left. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) }); }); @@ -829,7 +828,7 @@ mod tests { // Park the cursor in between matches and ensure that going to the next match selects the // closest match to the right. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) }); }); @@ -848,7 +847,7 @@ mod tests { // Park the cursor after the last match and ensure that going to the previous match selects // the last match. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)]) }); }); @@ -867,7 +866,7 @@ mod tests { // Park the cursor after the last match and ensure that going to the next match selects the // first match. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)]) }); }); @@ -886,7 +885,7 @@ mod tests { // Park the cursor before the first match and ensure that going to the previous match // selects the last match. editor.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) }); }); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1e363ebf15..8bd043f86b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -462,8 +462,8 @@ impl ProjectSearchView { let range_to_select = model.match_ranges[new_index].clone(); self.results_editor.update(cx, |editor, cx| { editor.unfold_ranges([range_to_select.clone()], false, cx); - editor.change_selections(true, cx, |s| { - s.select_ranges([range_to_select], Some(Autoscroll::Fit)) + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges([range_to_select]) }); }); } @@ -479,9 +479,7 @@ impl ProjectSearchView { fn focus_results_editor(&self, cx: &mut ViewContext) { self.query_editor.update(cx, |query_editor, cx| { let cursor = query_editor.selections.newest_anchor().head(); - query_editor.change_selections(true, cx, |s| { - s.select_ranges([cursor.clone()..cursor], None) - }); + query_editor.change_selections(None, cx, |s| s.select_ranges([cursor.clone()..cursor])); }); cx.focus(&self.results_editor); } @@ -493,8 +491,8 @@ impl ProjectSearchView { } else { self.results_editor.update(cx, |editor, cx| { if reset_selections { - editor.change_selections(true, cx, |s| { - s.select_ranges(match_ranges.first().cloned(), Some(Autoscroll::Fit)) + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(match_ranges.first().cloned()) }); } editor.highlight_background::( diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 69912cff18..1e19b7d918 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -1,5 +1,5 @@ use crate::{state::Mode, Vim}; -use editor::Bias; +use editor::{Autoscroll, Bias}; use gpui::{actions, MutableAppContext, ViewContext}; use language::SelectionGoal; use workspace::Workspace; @@ -13,7 +13,7 @@ pub fn init(cx: &mut MutableAppContext) { fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext) { Vim::update(cx, |state, cx| { state.update_active_editor(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, mut cursor, _| { *cursor.column_mut() = cursor.column().saturating_sub(1); (map.clip_point(cursor, Bias::Left), SelectionGoal::None) diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 7c4ac6a20c..be218592cc 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -8,7 +8,7 @@ use crate::{ }; use change::init as change_init; use collections::HashSet; -use editor::{Bias, DisplayPoint}; +use editor::{Autoscroll, Bias, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; use language::SelectionGoal; use workspace::Workspace; @@ -76,7 +76,7 @@ pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) { fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal)) }) }); @@ -86,7 +86,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext = Default::default(); - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let original_head = selection.head(); motion.expand_selection(map, selection, true); @@ -19,7 +19,7 @@ pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { // Fixup cursor position after the deletion editor.set_clip_at_line_ends(true, cx); - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let mut cursor = selection.head(); if motion.linewise() { diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/vim_test_context.rs index cfcfa8d2b4..28c15ffacd 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/vim_test_context.rs @@ -3,7 +3,7 @@ use std::ops::{Deref, Range}; use collections::BTreeMap; use itertools::{Either, Itertools}; -use editor::display_map::ToDisplayPoint; +use editor::{display_map::ToDisplayPoint, Autoscroll}; use gpui::{json::json, keymap::Keystroke, ViewHandle}; use indoc::indoc; use language::Selection; @@ -128,7 +128,7 @@ impl<'a> VimTestContext<'a> { let (unmarked_text, markers) = marked_text(&text); editor.set_text(unmarked_text, cx); let cursor_offset = markers[0]; - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.replace_cursors_with(|map| vec![cursor_offset.to_display_point(map)]) }); }) diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index c1b2771f7a..4d32d38c30 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -1,4 +1,4 @@ -use editor::Bias; +use editor::{Autoscroll, Bias}; use gpui::{actions, MutableAppContext, ViewContext}; use workspace::Workspace; @@ -14,7 +14,7 @@ pub fn init(cx: &mut MutableAppContext) { pub fn visual_motion(motion: Motion, cx: &mut MutableAppContext) { Vim::update(cx, |vim, cx| { vim.update_active_editor(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let (new_head, goal) = motion.move_point(map, selection.head(), selection.goal); let new_head = map.clip_at_line_end(new_head); @@ -42,7 +42,7 @@ pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext() .unwrap(); editor1.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select_display_ranges([DisplayPoint::new(10, 0)..DisplayPoint::new(10, 0)]) }); }); @@ -981,7 +981,7 @@ mod tests { editor3 .update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select_display_ranges([DisplayPoint::new(12, 0)..DisplayPoint::new(12, 0)]) }); editor.newline(&Default::default(), cx); @@ -1124,19 +1124,19 @@ mod tests { // Modify file to collapse multiple nav history entries into the same location. // Ensure we don't visit the same location twice when navigating. editor1.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]) }) }); for _ in 0..5 { editor1.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) }); }); editor1.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 0)]) }) }); @@ -1144,7 +1144,7 @@ mod tests { editor1.update(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(14, 0)]) }); editor.insert("", cx); @@ -1152,7 +1152,7 @@ mod tests { }); editor1.update(cx, |editor, cx| { - editor.change_selections(true, cx, |s| { + editor.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) }) }); From de9dc279806131d736d4d00e5e1f17418e083656 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 12 May 2022 15:55:18 -0700 Subject: [PATCH 3/6] store buffer and display_map model handles on selections collection --- crates/collab/src/rpc.rs | 10 +- crates/diagnostics/src/diagnostics.rs | 8 +- crates/diagnostics/src/items.rs | 2 +- crates/editor/src/editor.rs | 526 +++++++++------------ crates/editor/src/element.rs | 4 +- crates/editor/src/items.rs | 4 +- crates/editor/src/selections_collection.rs | 117 +++-- crates/editor/src/test.rs | 2 +- crates/go_to_line/src/go_to_line.rs | 2 +- crates/outline/src/outline.rs | 2 +- crates/search/src/buffer_search.rs | 53 ++- crates/search/src/project_search.rs | 32 +- crates/vim/src/vim_test_context.rs | 2 +- crates/zed/src/zed.rs | 2 +- 14 files changed, 373 insertions(+), 393 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 3ff9e13edb..2f182c1522 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -5499,11 +5499,11 @@ mod tests { Some((worktree_id, "2.txt").into()) ); assert_eq!( - editor_b2.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)), + editor_b2.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)), vec![2..3] ); assert_eq!( - editor_b1.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)), + editor_b1.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)), vec![0..1] ); @@ -5546,7 +5546,7 @@ mod tests { }); editor_b1 .condition(cx_b, |editor, cx| { - editor.selected_ranges(cx) == vec![1..1, 2..2] + editor.selections.selected_ranges(cx) == vec![1..1, 2..2] }) .await; @@ -5560,7 +5560,9 @@ mod tests { editor.set_scroll_position(vec2f(0., 100.), cx); }); editor_b1 - .condition(cx_b, |editor, cx| editor.selected_ranges(cx) == vec![3..3]) + .condition(cx_b, |editor, cx| { + editor.selections.selected_ranges(cx) == vec![3..3] + }) .await; // After unfollowing, client B stops receiving updates from client A. diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 7fb7bffb1e..d7eb089830 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -419,9 +419,7 @@ impl ProjectDiagnosticsEditor { groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice(); new_excerpt_ids_by_selection_id = editor.change_selections(Some(Autoscroll::Fit), cx, |s| s.refresh()); - selections = editor - .selections - .interleaved::(&editor.buffer().read(cx).read(cx)); + selections = editor.selections.interleaved::(cx); } // If any selection has lost its position, move it to start of the next primary diagnostic. @@ -899,7 +897,7 @@ mod tests { // Cursor is at the first diagnostic view.editor.update(cx, |editor, cx| { assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)] ); }); @@ -1000,7 +998,7 @@ mod tests { // Cursor keeps its position. view.editor.update(cx, |editor, cx| { assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)] ); }); diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index abc0b13181..752fc26f0f 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -58,7 +58,7 @@ impl DiagnosticIndicator { fn update(&mut self, editor: ViewHandle, cx: &mut ViewContext) { let editor = editor.read(cx); let buffer = editor.buffer().read(cx); - let cursor_position = editor.selections.newest::(&buffer.read(cx)).head(); + let cursor_position = editor.selections.newest::(cx).head(); let new_diagnostic = buffer .read(cx) .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6d907b2b76..60570360dc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -51,12 +51,11 @@ use std::{ borrow::Cow, cmp::{self, Ordering, Reverse}, iter, mem, - ops::{Deref, DerefMut, Range, RangeInclusive, Sub}, + ops::{Deref, DerefMut, Range, RangeInclusive}, sync::Arc, time::{Duration, Instant}, }; pub use sum_tree::Bias; -use text::rope::TextDimension; use theme::{DiagnosticStyle, Theme}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, Workspace}; @@ -938,11 +937,13 @@ impl Editor { cx.observe(&display_map, Self::on_display_map_changed) .detach(); + let selections = SelectionsCollection::new(display_map.clone(), buffer.clone()); + let mut this = Self { handle: cx.weak_handle(), buffer, display_map, - selections: SelectionsCollection::new(), + selections, columnar_selection_tail: None, add_selections_state: None, select_next_state: None, @@ -1186,15 +1187,11 @@ impl Editor { first_cursor_top = highlighted_rows.start as f32; last_cursor_bottom = first_cursor_top + 1.; } else if autoscroll == Autoscroll::Newest { - let newest_selection = self - .selections - .newest::(&display_map.buffer_snapshot); + let newest_selection = self.selections.newest::(cx); first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32; last_cursor_bottom = first_cursor_top + 1.; } else { - let selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let selections = self.selections.interleaved::(cx); first_cursor_top = selections .first() .unwrap() @@ -1254,9 +1251,7 @@ impl Editor { cx: &mut ViewContext, ) -> bool { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let selections = self.selections.interleaved::(cx); let mut target_left; let mut target_right; @@ -1387,9 +1382,7 @@ impl Editor { let old_cursor_position = self.selections.newest_anchor().head(); self.push_to_selection_history(); - let result = - self.selections - .change_with(self.display_map.clone(), self.buffer.clone(), cx, change); + let result = self.selections.change_with(cx, change); if let Some(autoscroll) = autoscroll { self.request_autoscroll(autoscroll, cx); @@ -1406,7 +1399,7 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let selections = self .selections - .interleaved::(&display_map.buffer_snapshot) + .interleaved::(cx) .into_iter() .map(|selection| selection.map(|point| point.to_display_point(&display_map))) .collect(); @@ -1465,10 +1458,7 @@ impl Editor { cx: &mut ViewContext, ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let tail = self - .selections - .newest::(&display_map.buffer_snapshot) - .tail(); + let tail = self.selections.newest::(cx).tail(); self.begin_selection(position, false, click_count, cx); let position = position.to_offset(&display_map, Bias::Left); @@ -1573,10 +1563,7 @@ impl Editor { } let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let tail = self - .selections - .newest::(&display_map.buffer_snapshot) - .tail(); + let tail = self.selections.newest::(cx).tail(); self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail)); self.select_columns( @@ -1687,9 +1674,7 @@ impl Editor { fn end_selection(&mut self, cx: &mut ViewContext) { self.columnar_selection_tail.take(); if self.selections.pending_anchor().is_some() { - let selections = self - .selections - .interleaved::(&self.buffer.read(cx).snapshot(cx)); + let selections = self.selections.interleaved::(cx); self.change_selections(None, cx, |s| { s.select(selections); s.clear_pending(); @@ -1769,43 +1754,6 @@ impl Editor { cx.propagate_action(); } - #[cfg(any(test, feature = "test-support"))] - pub fn selected_ranges + std::fmt::Debug>( - &self, - cx: &AppContext, - ) -> Vec> { - self.selections - .interleaved::(&self.buffer.read(cx).read(cx)) - .iter() - .map(|s| { - if s.reversed { - s.end.clone()..s.start.clone() - } else { - s.start.clone()..s.end.clone() - } - }) - .collect() - } - - #[cfg(any(test, feature = "test-support"))] - pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec> { - let display_map = self - .display_map - .update(cx, |display_map, cx| display_map.snapshot(cx)); - self.selections - .disjoint_anchors() - .iter() - .chain(self.selections.pending_anchor().as_ref()) - .map(|s| { - if s.reversed { - s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map) - } else { - s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map) - } - }) - .collect() - } - pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext) { if !self.input_enabled { cx.propagate_action(); @@ -1827,8 +1775,9 @@ impl Editor { pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { + let selections = this.selections.interleaved::(cx); + let buffer = this.buffer.read(cx).snapshot(cx); - let selections = this.selections.interleaved::(&buffer); selections .iter() .map(|selection| { @@ -1910,9 +1859,7 @@ impl Editor { pub fn insert(&mut self, text: &str, cx: &mut ViewContext) { let text: Arc = text.into(); self.transact(cx, |this, cx| { - let old_selections = this - .selections - .interleaved::(&this.buffer.read(cx).snapshot(cx)); + let old_selections = this.selections.interleaved::(cx); let selection_anchors = this.buffer.update(cx, |buffer, cx| { let anchors = { let snapshot = buffer.read(cx); @@ -1975,7 +1922,7 @@ impl Editor { { if self .selections - .interleaved::(&snapshot) + .interleaved::(cx) .iter() .any(|selection| selection.is_empty()) { @@ -2018,8 +1965,7 @@ impl Editor { } fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext) { - let snapshot = self.buffer.read(cx).snapshot(cx); - let selections = self.selections.interleaved::(&snapshot); + let selections = self.selections.interleaved::(cx); let mut bracket_pair_state = None; let mut new_selections = None; self.buffer.update(cx, |buffer, cx| { @@ -2119,7 +2065,7 @@ impl Editor { fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext) -> bool { let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self.selections.interleaved::(&buffer); + let old_selections = self.selections.interleaved::(cx); let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() { autoclose_pair } else { @@ -2288,13 +2234,11 @@ impl Editor { snippet = None; text = completion.new_text.clone(); }; + let selections = self.selections.interleaved::(cx); let buffer = buffer_handle.read(cx); let old_range = completion.old_range.to_offset(&buffer); let old_text = buffer.text_for_range(old_range.clone()).collect::(); - let selections = self - .selections - .interleaved::(&self.buffer.read(cx).snapshot(cx)); let newest_selection = self.selections.newest_anchor(); if newest_selection.start.buffer_id != Some(buffer_handle.id()) { return None; @@ -2816,9 +2760,7 @@ impl Editor { pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let mut selections = self.selections.interleaved::(cx); for selection in &mut selections { if selection.is_empty() { let old_head = selection.head(); @@ -2877,9 +2819,7 @@ impl Editor { return; } - let mut selections = self - .selections - .interleaved::(&self.buffer.read(cx).read(cx)); + let mut selections = self.selections.interleaved::(cx); if selections.iter().all(|s| s.is_empty()) { self.transact(cx, |this, cx| { this.buffer.update(cx, |buffer, cx| { @@ -2914,9 +2854,7 @@ impl Editor { } pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext) { - let mut selections = self - .selections - .interleaved::(&self.buffer.read(cx).read(cx)); + let mut selections = self.selections.interleaved::(cx); self.transact(cx, |this, cx| { let mut last_indent = None; this.buffer.update(cx, |buffer, cx| { @@ -2979,9 +2917,7 @@ impl Editor { pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let selections = self.selections.interleaved::(cx); let mut deletion_ranges = Vec::new(); let mut last_outdent = None; { @@ -3024,18 +2960,17 @@ impl Editor { cx, ); }); - let snapshot = this.buffer.read(cx).snapshot(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select(s.interleaved::(&snapshot)) + // TODO: Make sure this is a reasonable change + // This used to call select(local_selections) + s.refresh() }); }); } pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let selections = self.selections.interleaved::(cx); let mut new_cursors = Vec::new(); let mut edit_ranges = Vec::new(); @@ -3117,7 +3052,7 @@ impl Editor { pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let selections = self.selections.interleaved::(buffer); + let selections = self.selections.interleaved::(cx); let mut edits = Vec::new(); let mut selections_iter = selections.iter().peekable(); @@ -3162,7 +3097,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.selections.interleaved::(&buffer); + let selections = self.selections.interleaved::(cx); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3274,7 +3209,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.selections.interleaved::(&buffer); + let selections = self.selections.interleaved::(cx); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3412,9 +3347,10 @@ impl Editor { edits }); this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); - let buffer = this.buffer.read(cx).snapshot(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select(s.interleaved::(&buffer)); + // TODO: Make sure this swap is reasonable. + // This was select(interleaved) + s.refresh(); }); }); } @@ -3422,7 +3358,7 @@ impl Editor { pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext) { let mut text = String::new(); let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self.selections.interleaved::(&buffer); + let mut selections = self.selections.interleaved::(cx); let mut clipboard_selections = Vec::with_capacity(selections.len()); { let max_point = buffer.max_point(); @@ -3455,8 +3391,8 @@ impl Editor { } pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { + let selections = self.selections.interleaved::(cx); let buffer = self.buffer.read(cx).read(cx); - let selections = self.selections.interleaved::(&buffer); let mut text = String::new(); let mut clipboard_selections = Vec::with_capacity(selections.len()); { @@ -3489,9 +3425,7 @@ impl Editor { if let Some(item) = cx.as_mut().read_from_clipboard() { let mut clipboard_text = Cow::Borrowed(item.text()); if let Some(mut clipboard_selections) = item.metadata::>() { - let old_selections = this - .selections - .interleaved::(&this.buffer.read(cx).read(cx)); + let old_selections = this.selections.interleaved::(cx); let all_selections_were_entire_line = clipboard_selections.iter().all(|s| s.is_entire_line); if clipboard_selections.len() != old_selections.len() { @@ -3544,9 +3478,7 @@ impl Editor { buffer.edit_with_autoindent(edits, cx); }); - let selections = this - .selections - .interleaved::(&this.buffer.read(cx).read(cx)); + let selections = this.selections.interleaved::(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); } else { this.insert(&clipboard_text, cx); @@ -3970,9 +3902,7 @@ impl Editor { } pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext) { - let mut selection = self - .selections - .last::(&self.buffer.read(cx).read(cx)); + let mut selection = self.selections.last::(cx); selection.set_head(Point::zero(), SelectionGoal::None); self.change_selections(Some(Autoscroll::Fit), cx, |s| { @@ -4031,7 +3961,7 @@ impl Editor { pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext) { let buffer = self.buffer.read(cx).snapshot(cx); - let mut selection = self.selections.first::(&buffer); + let mut selection = self.selections.first::(cx); selection.set_head(buffer.len(), SelectionGoal::None); self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select(vec![selection]); @@ -4047,9 +3977,7 @@ impl Editor { pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let mut selections = self.selections.interleaved::(cx); let max_point = display_map.buffer_snapshot.max_point(); for selection in &mut selections { let rows = selection.spanned_rows(true, &display_map); @@ -4070,8 +3998,8 @@ impl Editor { let mut to_unfold = Vec::new(); let mut new_selection_ranges = Vec::new(); { + let selections = self.selections.interleaved::(cx); let buffer = self.buffer.read(cx).read(cx); - let selections = self.selections.interleaved::(&buffer); for selection in selections { for row in selection.start.row..selection.end.row { let cursor = Point::new(row, buffer.line_len(row)); @@ -4098,9 +4026,7 @@ impl Editor { fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let mut selections = self.selections.interleaved::(cx); let mut state = self.add_selections_state.take().unwrap_or_else(|| { let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone(); let range = oldest_selection.display_range(&display_map).sorted(); @@ -4197,7 +4123,7 @@ impl Editor { self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let mut selections = self.selections.interleaved::(&buffer); + let mut selections = self.selections.interleaved::(cx); if let Some(mut select_next_state) = self.select_next_state.take() { let query = &select_next_state.query; if !select_next_state.done { @@ -4287,9 +4213,7 @@ impl Editor { pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - let mut selections = this - .selections - .interleaved::(&this.buffer.read(cx).snapshot(cx)); + let mut selections = this.selections.interleaved::(cx); let mut all_selection_lines_are_comments = true; let mut edit_ranges = Vec::new(); let mut last_toggled_row = None; @@ -4390,9 +4314,7 @@ impl Editor { } }); - let selections = this - .selections - .interleaved::(&this.buffer.read(cx).read(cx)); + let selections = this.selections.interleaved::(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select(selections); }); @@ -4406,10 +4328,7 @@ impl Editor { ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self - .selections - .interleaved::(&buffer) - .into_boxed_slice(); + let old_selections = self.selections.interleaved::(cx).into_boxed_slice(); let mut stack = mem::take(&mut self.select_larger_syntax_node_stack); let mut selected_larger_node = false; @@ -4469,7 +4388,7 @@ impl Editor { cx: &mut ViewContext, ) { let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self.selections.interleaved::(&buffer); + let mut selections = self.selections.interleaved::(cx); for selection in &mut selections { if let Some((open_range, close_range)) = buffer.enclosing_bracket_ranges(selection.start..selection.end) @@ -4534,7 +4453,7 @@ impl Editor { pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext) { let buffer = self.buffer.read(cx).snapshot(cx); - let selection = self.selections.newest::(&buffer); + let selection = self.selections.newest::(cx); let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| { active_diagnostics .primary_range @@ -4619,7 +4538,7 @@ impl Editor { let editor = editor_handle.read(cx); let buffer = editor.buffer.read(cx); - let head = editor.selections.newest::(&buffer.read(cx)).head(); + let head = editor.selections.newest::(cx).head(); let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) { text_anchor } else { @@ -4666,7 +4585,7 @@ impl Editor { let editor = editor_handle.read(cx); let buffer = editor.buffer.read(cx); - let head = editor.selections.newest::(&buffer.read(cx)).head(); + let head = editor.selections.newest::(cx).head(); let (buffer, head) = buffer.text_anchor_for_position(head, cx)?; let replica_id = editor.replica_id(cx); @@ -4925,11 +4844,7 @@ impl Editor { if moving_cursor { let rename_editor = rename.editor.read(cx); - let rename_buffer = rename_editor.buffer.read(cx); - let cursor_in_rename_editor = rename_editor - .selections - .newest::(&rename_buffer.read(cx)) - .head(); + let cursor_in_rename_editor = rename_editor.selections.newest::(cx).head(); // Update the selection to match the position of the selection inside // the rename editor. @@ -5092,10 +5007,9 @@ impl Editor { cx: &mut ViewContext, ) { let old_cursor_position = self.selections.newest_anchor().head(); - self.selections - .change_with(self.display_map.clone(), self.buffer.clone(), cx, |s| { - s.select_anchors(selections); - }); + self.selections.change_with(cx, |s| { + s.select_anchors(selections); + }); self.selections_did_change(false, &old_cursor_position, cx); } @@ -5165,9 +5079,7 @@ impl Editor { let mut fold_ranges = Vec::new(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .selections - .interleaved::(&display_map.buffer_snapshot); + let selections = self.selections.interleaved::(cx); for selection in selections { let range = selection.display_range(&display_map).sorted(); let buffer_start_row = range.start.to_point(&display_map).row; @@ -5191,7 +5103,7 @@ impl Editor { pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let selections = self.selections.interleaved::(buffer); + let selections = self.selections.interleaved::(cx); let ranges = selections .iter() .map(|s| { @@ -5249,9 +5161,7 @@ impl Editor { } pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext) { - let selections = self - .selections - .interleaved::(&self.buffer.read(cx).read(cx)); + let selections = self.selections.interleaved::(cx); let ranges = selections.into_iter().map(|s| s.start..s.end); self.fold_ranges(ranges, cx); } @@ -5628,7 +5538,7 @@ impl Editor { } let mut new_selections_by_buffer = HashMap::default(); - for selection in editor.selections.interleaved::(&buffer.read(cx)) { + for selection in editor.selections.interleaved::(cx) { for (buffer, mut range) in buffer.range_to_buffer_ranges(selection.start..selection.end, cx) { @@ -6322,14 +6232,14 @@ mod tests { editor.insert("cd", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cd56"); - assert_eq!(editor.selected_ranges(cx), vec![4..4]); + assert_eq!(editor.selections.selected_ranges(cx), vec![4..4]); editor.start_transaction_at(now, cx); editor.change_selections(None, cx, |s| s.select_ranges([4..5])); editor.insert("e", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selected_ranges(cx), vec![5..5]); + assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]); now += group_interval + Duration::from_millis(1); editor.change_selections(None, cx, |s| s.select_ranges([2..2])); @@ -6343,30 +6253,30 @@ mod tests { }); assert_eq!(editor.text(cx), "ab2cde6"); - assert_eq!(editor.selected_ranges(cx), vec![3..3]); + assert_eq!(editor.selections.selected_ranges(cx), vec![3..3]); // Last transaction happened past the group interval in a different editor. // Undo it individually and don't restore selections. editor.undo(&Undo, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selected_ranges(cx), vec![2..2]); + assert_eq!(editor.selections.selected_ranges(cx), vec![2..2]); // First two transactions happened within the group interval in this editor. // Undo them together and restore selections. editor.undo(&Undo, cx); editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op. assert_eq!(editor.text(cx), "123456"); - assert_eq!(editor.selected_ranges(cx), vec![0..0]); + assert_eq!(editor.selections.selected_ranges(cx), vec![0..0]); // Redo the first two transactions together. editor.redo(&Redo, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selected_ranges(cx), vec![5..5]); + assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]); // Redo the last transaction on its own. editor.redo(&Redo, cx); assert_eq!(editor.text(cx), "ab2cde6"); - assert_eq!(editor.selected_ranges(cx), vec![6..6]); + assert_eq!(editor.selections.selected_ranges(cx), vec![6..6]); // Test empty transactions. editor.start_transaction_at(now, cx); @@ -6386,7 +6296,7 @@ mod tests { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] ); @@ -6395,7 +6305,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); @@ -6404,7 +6314,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -6414,7 +6324,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -6424,7 +6334,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [ DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) @@ -6436,7 +6346,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] ); } @@ -6450,7 +6360,7 @@ mod tests { view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] ); }); @@ -6458,7 +6368,7 @@ mod tests { view.update(cx, |view, cx| { view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); }); @@ -6467,7 +6377,7 @@ mod tests { view.cancel(&Cancel, cx); view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); }); @@ -6503,7 +6413,7 @@ mod tests { editor.navigate(nav_entry.data.unwrap(), cx); assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6513,7 +6423,7 @@ mod tests { editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx); editor.end_selection(cx); assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6523,14 +6433,14 @@ mod tests { editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx); editor.end_selection(cx); assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)] ); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); editor.navigate(nav_entry.data.unwrap(), cx); assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6566,7 +6476,7 @@ mod tests { cx, ); assert_eq!( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), &[editor.max_point(cx)..editor.max_point(cx)] ); assert_eq!( @@ -6593,7 +6503,7 @@ mod tests { view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx); view.end_selection(cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), @@ -6604,7 +6514,7 @@ mod tests { view.update(cx, |view, cx| { view.cancel(&Cancel, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] ); }); @@ -6612,7 +6522,7 @@ mod tests { view.update(cx, |view, cx| { view.cancel(&Cancel, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] ); }); @@ -6723,43 +6633,43 @@ mod tests { view.update(cx, |view, cx| { assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); view.move_to_end(&MoveToEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)] ); view.move_to_beginning(&MoveToBeginning, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); @@ -6768,13 +6678,13 @@ mod tests { }); view.select_to_beginning(&SelectToBeginning, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)] ); view.select_to_end(&SelectToEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)] ); }); @@ -6802,80 +6712,80 @@ mod tests { view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐⓑ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐⓑ…".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(1, "ab…".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(1, "ab".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(1, "a".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "α".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "αβ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "αβ…".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "αβ…ε".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(1, "ab…e".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐⓑ…ⓔ".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐⓑ…".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐⓑ".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(0, "ⓐ".len())] ); }); @@ -6892,37 +6802,37 @@ mod tests { }); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(1, "abcd".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "αβγ".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(3, "abcd".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(3, "abcd".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[empty_range(2, "αβγ".len())] ); }); @@ -6945,7 +6855,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -6956,7 +6866,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), @@ -6967,7 +6877,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -6978,7 +6888,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_end_of_line(&MoveToEndOfLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -6990,7 +6900,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_end_of_line(&MoveToEndOfLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -7007,7 +6917,7 @@ mod tests { cx, ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), @@ -7023,7 +6933,7 @@ mod tests { cx, ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0), @@ -7039,7 +6949,7 @@ mod tests { cx, ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), @@ -7055,7 +6965,7 @@ mod tests { cx, ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5), @@ -7067,7 +6977,7 @@ mod tests { view.delete_to_end_of_line(&DeleteToEndOfLine, cx); assert_eq!(view.display_text(cx), "ab\n de"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), @@ -7079,7 +6989,7 @@ mod tests { view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); assert_eq!(view.display_text(cx), "\n"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), @@ -7211,37 +7121,37 @@ mod tests { view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)] ); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] ); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] ); }); @@ -7368,7 +7278,7 @@ mod tests { editor.update(cx, |editor, cx| { assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), &[ Point::new(1, 2)..Point::new(1, 2), Point::new(2, 2)..Point::new(2, 2), @@ -7390,7 +7300,7 @@ mod tests { // The selections are moved after the inserted newlines assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), &[ Point::new(2, 0)..Point::new(2, 0), Point::new(4, 0)..Point::new(4, 0), @@ -7416,13 +7326,13 @@ mod tests { }); editor.update(cx, |editor, cx| { - assert_eq!(editor.selected_ranges(cx), &[2..2, 7..7, 12..12],); + assert_eq!(editor.selections.selected_ranges(cx), &[2..2, 7..7, 12..12],); editor.insert("Z", cx); assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)"); // The selections are moved after the inserted characters - assert_eq!(editor.selected_ranges(cx), &[3..3, 9..9, 15..15],); + assert_eq!(editor.selections.selected_ranges(cx), &[3..3, 9..9, 15..15],); }); } @@ -7731,7 +7641,7 @@ mod tests { view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1) @@ -7749,7 +7659,7 @@ mod tests { view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi\n"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)] ); }); @@ -7772,7 +7682,7 @@ mod tests { view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -7794,7 +7704,7 @@ mod tests { view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n"); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1), DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1), @@ -7836,7 +7746,7 @@ mod tests { "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -7853,7 +7763,7 @@ mod tests { "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), @@ -7870,7 +7780,7 @@ mod tests { "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), @@ -7887,7 +7797,7 @@ mod tests { "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -7931,15 +7841,15 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([1..1])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selected_ranges(cx), [2..2]); + assert_eq!(editor.selections.selected_ranges(cx), [2..2]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bca"); - assert_eq!(editor.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.selected_ranges(cx), [3..3]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.selected_ranges(cx), [3..3]); editor }) @@ -7951,20 +7861,20 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acb\nde"); - assert_eq!(editor.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.selected_ranges(cx), [3..3]); editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selected_ranges(cx), [5..5]); + assert_eq!(editor.selections.selected_ranges(cx), [5..5]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbde\n"); - assert_eq!(editor.selected_ranges(cx), [6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selected_ranges(cx), [6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [6..6]); editor }) @@ -7976,23 +7886,23 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bacd\ne"); - assert_eq!(editor.selected_ranges(cx), [2..2, 3..3, 5..5]); + assert_eq!(editor.selections.selected_ranges(cx), [2..2, 3..3, 5..5]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selected_ranges(cx), [3..3, 4..4, 6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [3..3, 4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcda\ne"); - assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcaed\n"); - assert_eq!(editor.selected_ranges(cx), [5..5, 6..6]); + assert_eq!(editor.selections.selected_ranges(cx), [5..5, 6..6]); editor }) @@ -8004,15 +7914,15 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selected_ranges(cx), [8..8]); + assert_eq!(editor.selections.selected_ranges(cx), [8..8]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀✋🍐"); - assert_eq!(editor.selected_ranges(cx), [11..11]); + assert_eq!(editor.selections.selected_ranges(cx), [11..11]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selected_ranges(cx), [11..11]); + assert_eq!(editor.selections.selected_ranges(cx), [11..11]); editor }) @@ -8040,7 +7950,7 @@ mod tests { view.paste(&Paste, cx); assert_eq!(view.display_text(cx), "two one✅ four three six five "); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22), @@ -8104,7 +8014,7 @@ mod tests { "123\n4567\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) " ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -8137,7 +8047,7 @@ mod tests { "123\n123\n123\n67\n123\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) " ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), @@ -8155,7 +8065,7 @@ mod tests { view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)] ); }); @@ -8177,7 +8087,7 @@ mod tests { }); view.select_line(&SelectLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0), DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0), @@ -8188,7 +8098,7 @@ mod tests { view.update(cx, |view, cx| { view.select_line(&SelectLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0), DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5), @@ -8199,7 +8109,7 @@ mod tests { view.update(cx, |view, cx| { view.select_line(&SelectLine, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)] ); }); @@ -8237,7 +8147,7 @@ mod tests { "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -8257,7 +8167,7 @@ mod tests { "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii" ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [ DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -8286,7 +8196,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8297,7 +8207,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8308,13 +8218,13 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] ); view.undo_selection(&UndoSelection, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8323,7 +8233,7 @@ mod tests { view.redo_selection(&RedoSelection, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] ); }); @@ -8331,7 +8241,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) @@ -8342,7 +8252,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) @@ -8358,7 +8268,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) @@ -8369,7 +8279,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) @@ -8380,7 +8290,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] ); }); @@ -8388,7 +8298,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] ); }); @@ -8399,7 +8309,7 @@ mod tests { }); view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8411,7 +8321,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8424,7 +8334,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8441,7 +8351,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), @@ -8454,7 +8364,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), @@ -8482,7 +8392,7 @@ mod tests { }, cx, ); - assert_eq!(view.selected_ranges(cx), &ranges[1..2]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[1..2]); view.select_next( &SelectNext { @@ -8490,13 +8400,13 @@ mod tests { }, cx, ); - assert_eq!(view.selected_ranges(cx), &ranges[1..3]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[1..3]); view.undo_selection(&UndoSelection, cx); - assert_eq!(view.selected_ranges(cx), &ranges[1..2]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[1..2]); view.redo_selection(&RedoSelection, cx); - assert_eq!(view.selected_ranges(cx), &ranges[1..3]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[1..3]); view.select_next( &SelectNext { @@ -8504,7 +8414,7 @@ mod tests { }, cx, ); - assert_eq!(view.selected_ranges(cx), &ranges[1..4]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[1..4]); view.select_next( &SelectNext { @@ -8512,7 +8422,7 @@ mod tests { }, cx, ); - assert_eq!(view.selected_ranges(cx), &ranges[0..4]); + assert_eq!(view.selections.selected_ranges(cx), &ranges[0..4]); }); } @@ -8550,7 +8460,9 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| { + view.selections.selected_display_ranges(cx) + }), &[ DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8562,7 +8474,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), @@ -8573,7 +8485,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] ); @@ -8582,7 +8494,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] ); @@ -8590,7 +8502,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), @@ -8601,7 +8513,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8613,7 +8525,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), @@ -8626,7 +8538,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), @@ -8647,7 +8559,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8703,7 +8615,7 @@ mod tests { editor.newline(&Newline, cx); assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), &[ Point::new(1, 4)..Point::new(1, 4), Point::new(3, 4)..Point::new(3, 4), @@ -8856,7 +8768,7 @@ mod tests { .unindent() ); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)] ); }); @@ -8886,7 +8798,10 @@ mod tests { marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone()]); let selection_ranges = selection_ranges_lookup.remove(&range_markers).unwrap(); assert_eq!(editor.text(cx), expected_text); - assert_eq!(editor.selected_ranges::(cx), selection_ranges); + assert_eq!( + editor.selections.selected_ranges::(cx), + selection_ranges + ); } assert( editor, @@ -9420,7 +9335,7 @@ mod tests { view.handle_input(&Input("X".to_string()), cx); assert_eq!(view.text(cx), "Xaaaa\nXbbbb"); assert_eq!( - view.selected_ranges(cx), + view.selections.selected_ranges(cx), [ Point::new(0, 1)..Point::new(0, 1), Point::new(1, 1)..Point::new(1, 1), @@ -9461,7 +9376,7 @@ mod tests { bX|bbX|b cccc"}); assert_eq!(view.text(cx), expected_text); - assert_eq!(view.selected_ranges(cx), expected_selections); + assert_eq!(view.selections.selected_ranges(cx), expected_selections); view.newline(&Newline, cx); let (expected_text, expected_selections) = marked_text_ranges(indoc! {" @@ -9474,7 +9389,7 @@ mod tests { |b cccc"}); assert_eq!(view.text(cx), expected_text); - assert_eq!(view.selected_ranges(cx), expected_selections); + assert_eq!(view.selections.selected_ranges(cx), expected_selections); }); } @@ -9510,7 +9425,7 @@ mod tests { }); editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [ Point::new(1, 3)..Point::new(1, 3), Point::new(2, 1)..Point::new(2, 1), @@ -9525,7 +9440,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [ Point::new(1, 3)..Point::new(1, 3), Point::new(2, 1)..Point::new(2, 1), @@ -9539,7 +9454,7 @@ mod tests { editor.update(cx, |editor, cx| { // Removing an excerpt causes the first selection to become degenerate. assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [ Point::new(0, 0)..Point::new(0, 0), Point::new(0, 1)..Point::new(0, 1) @@ -9552,7 +9467,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [ Point::new(0, 1)..Point::new(0, 1), Point::new(0, 3)..Point::new(0, 3) @@ -9591,7 +9506,7 @@ mod tests { let snapshot = editor.snapshot(cx); editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [Point::new(1, 3)..Point::new(1, 3)] ); editor @@ -9602,7 +9517,7 @@ mod tests { }); editor.update(cx, |editor, cx| { assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [Point::new(0, 0)..Point::new(0, 0)] ); @@ -9611,7 +9526,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selected_ranges(cx), + editor.selections.selected_ranges(cx), [Point::new(0, 3)..Point::new(0, 3)] ); assert!(editor.selections.pending_anchor().is_some()); @@ -9803,7 +9718,7 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!(follower.read(cx).selected_ranges(cx), vec![1..1]); + assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![1..1]); // Update the scroll position only leader.update(cx, |leader, cx| { @@ -9833,7 +9748,7 @@ mod tests { assert_eq!(follower.scroll_position(cx), initial_scroll_position); assert!(follower.autoscroll_request.is_some()); }); - assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0]); + assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![0..0]); // Creating a pending selection that precedes another selection leader.update(cx, |leader, cx| { @@ -9845,7 +9760,10 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0, 1..1]); + assert_eq!( + follower.read(cx).selections.selected_ranges(cx), + vec![0..0, 1..1] + ); // Extend the pending selection so that it surrounds another selection leader.update(cx, |leader, cx| { @@ -9856,7 +9774,7 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..2]); + assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![0..2]); } #[test] @@ -9959,7 +9877,7 @@ mod tests { }) .collect(); assert_eq!( - view.selected_display_ranges(cx), + view.selections.selected_display_ranges(cx), &asserted_ranges[..], "Assert selections are {}", marked_text diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index dcef624d01..06c11db137 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -959,7 +959,7 @@ impl Element for EditorElement { if view.show_local_selections { let local_selections = view .selections - .interleaved_in_range(start_anchor..end_anchor, &display_map.buffer_snapshot); + .interleaved_in_range(start_anchor..end_anchor, cx); for selection in &local_selections { let is_empty = selection.start == selection.end; let selection_start = snapshot.prev_line_boundary(selection.start).1; @@ -1043,7 +1043,7 @@ impl Element for EditorElement { let newest_selection_head = view .selections - .newest::(&snapshot.buffer_snapshot) + .newest::(cx) .head() .to_display_point(&snapshot); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 1652334b38..165f4ca676 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -252,13 +252,13 @@ fn deserialize_selection( impl Item for Editor { fn navigate(&mut self, data: Box, cx: &mut ViewContext) -> bool { if let Ok(data) = data.downcast::() { + let newest_selection = self.selections.newest::(cx); let buffer = self.buffer.read(cx).read(cx); let offset = if buffer.can_resolve(&data.cursor_anchor) { data.cursor_anchor.to_point(&buffer) } else { buffer.clip_point(data.cursor_position, Bias::Left) }; - let newest_selection = self.selections.newest::(&buffer); let scroll_top_anchor = if buffer.can_resolve(&data.scroll_top_anchor) { data.scroll_top_anchor @@ -465,7 +465,7 @@ impl CursorPosition { self.selected_count = 0; let mut last_selection: Option> = None; - for selection in editor.selections.interleaved::(&buffer) { + for selection in editor.selections.interleaved::(cx) { self.selected_count += selection.end - selection.start; if last_selection .as_ref() diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index cbe4f0fbef..a5fac015d6 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -5,7 +5,7 @@ use std::{ }; use collections::HashMap; -use gpui::{ModelHandle, MutableAppContext}; +use gpui::{AppContext, ModelHandle, MutableAppContext}; use itertools::Itertools; use language::{rope::TextDimension, Bias, Point, Selection, SelectionGoal, ToPoint}; use util::post_inc; @@ -22,14 +22,18 @@ pub struct PendingSelection { } pub struct SelectionsCollection { + display_map: ModelHandle, + buffer: ModelHandle, pub next_selection_id: usize, disjoint: Arc<[Selection]>, pending: Option, } impl SelectionsCollection { - pub fn new() -> Self { + pub fn new(display_map: ModelHandle, buffer: ModelHandle) -> Self { Self { + display_map, + buffer, next_selection_id: 1, disjoint: Arc::from([]), pending: Some(PendingSelection { @@ -45,6 +49,14 @@ impl SelectionsCollection { } } + fn display_map(&self, cx: &mut MutableAppContext) -> DisplaySnapshot { + self.display_map.update(cx, |map, cx| map.snapshot(cx)) + } + + fn buffer(&self, cx: &AppContext) -> MultiBufferSnapshot { + self.buffer.read(cx).snapshot(cx) + } + pub fn count<'a>(&self) -> usize { let mut count = self.disjoint.len(); if self.pending.is_some() { @@ -65,25 +77,26 @@ impl SelectionsCollection { pub fn pending>( &self, - snapshot: &MultiBufferSnapshot, + cx: &AppContext, ) -> Option> { self.pending_anchor() .as_ref() - .map(|pending| pending.map(|p| p.summary::(&snapshot))) + .map(|pending| pending.map(|p| p.summary::(&self.buffer(cx)))) } pub fn pending_mode(&self) -> Option { self.pending.as_ref().map(|pending| pending.mode.clone()) } - pub fn interleaved<'a, D>(&self, buffer: &MultiBufferSnapshot) -> Vec> + pub fn interleaved<'a, D>(&self, cx: &AppContext) -> Vec> where D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, { let disjoint_anchors = &self.disjoint; - let mut disjoint = resolve_multiple::(disjoint_anchors.iter(), &buffer).peekable(); + let mut disjoint = + resolve_multiple::(disjoint_anchors.iter(), &self.buffer(cx)).peekable(); - let mut pending_opt = self.pending::(&buffer); + let mut pending_opt = self.pending::(cx); iter::from_fn(move || { if let Some(pending) = pending_opt.as_mut() { @@ -114,8 +127,9 @@ impl SelectionsCollection { pub fn interleaved_in_range<'a>( &self, range: Range, - buffer: &MultiBufferSnapshot, + cx: &AppContext, ) -> Vec> { + let buffer = self.buffer(cx); let start_ix = match self .disjoint .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer)) @@ -162,9 +176,9 @@ impl SelectionsCollection { pub fn newest>( &self, - snapshot: &MultiBufferSnapshot, + cx: &AppContext, ) -> Selection { - resolve(self.newest_anchor(), snapshot) + resolve(self.newest_anchor(), &self.buffer(cx)) } pub fn oldest_anchor(&self) -> &Selection { @@ -177,36 +191,65 @@ impl SelectionsCollection { pub fn oldest>( &self, - snapshot: &MultiBufferSnapshot, + cx: &AppContext, ) -> Selection { - resolve(self.oldest_anchor(), snapshot) + resolve(self.oldest_anchor(), &self.buffer(cx)) } pub fn first>( &self, - snapshot: &MultiBufferSnapshot, + cx: &AppContext, ) -> Selection { - self.interleaved(&snapshot).first().unwrap().clone() + self.interleaved(cx).first().unwrap().clone() } pub fn last>( &self, - snapshot: &MultiBufferSnapshot, + cx: &AppContext, ) -> Selection { - self.interleaved(&snapshot).last().unwrap().clone() + self.interleaved(cx).last().unwrap().clone() + } + + #[cfg(any(test, feature = "test-support"))] + pub fn selected_ranges + std::fmt::Debug>( + &self, + cx: &AppContext, + ) -> Vec> { + self.interleaved::(cx) + .iter() + .map(|s| { + if s.reversed { + s.end.clone()..s.start.clone() + } else { + s.start.clone()..s.end.clone() + } + }) + .collect() + } + + #[cfg(any(test, feature = "test-support"))] + pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec> { + let display_map = self.display_map(cx); + self.disjoint_anchors() + .iter() + .chain(self.pending_anchor().as_ref()) + .map(|s| { + if s.reversed { + s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map) + } else { + s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map) + } + }) + .collect() } pub(crate) fn change_with( &mut self, - display_map: ModelHandle, - buffer: ModelHandle, cx: &mut MutableAppContext, change: impl FnOnce(&mut MutableSelectionsCollection) -> R, ) -> R { let mut mutable_collection = MutableSelectionsCollection { collection: self, - display_map, - buffer, cx, }; @@ -221,12 +264,18 @@ impl SelectionsCollection { pub struct MutableSelectionsCollection<'a> { collection: &'a mut SelectionsCollection, - buffer: ModelHandle, - display_map: ModelHandle, cx: &'a mut MutableAppContext, } impl<'a> MutableSelectionsCollection<'a> { + fn display_map(&mut self) -> DisplaySnapshot { + self.collection.display_map(self.cx) + } + + fn buffer(&mut self) -> MultiBufferSnapshot { + self.collection.buffer(self.cx) + } + pub fn clear_disjoint(&mut self) { self.collection.disjoint = Arc::from([]); } @@ -303,19 +352,11 @@ impl<'a> MutableSelectionsCollection<'a> { pub fn insert_range(&mut self, range: Range) where - T: 'a - + ToOffset - + ToPoint - + TextDimension - + Ord - + Sub - + std::marker::Copy - + std::fmt::Debug, + T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub + std::marker::Copy, { - let buffer = self.buffer.read(self.cx).snapshot(self.cx); - let mut selections = self.interleaved(&buffer); - let mut start = range.start.to_offset(&buffer); - let mut end = range.end.to_offset(&buffer); + let mut selections = self.interleaved(self.cx); + let mut start = range.start.to_offset(&self.buffer()); + let mut end = range.end.to_offset(&self.buffer()); let reversed = if start > end { mem::swap(&mut start, &mut end); true @@ -440,7 +481,7 @@ impl<'a> MutableSelectionsCollection<'a> { where T: IntoIterator>, { - let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let display_map = self.display_map(); let selections = ranges .into_iter() .map(|range| { @@ -468,9 +509,9 @@ impl<'a> MutableSelectionsCollection<'a> { &mut self, mut move_selection: impl FnMut(&DisplaySnapshot, &mut Selection), ) { - let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let display_map = self.display_map(); let selections = self - .interleaved::(&display_map.buffer_snapshot) + .interleaved::(self.cx) .into_iter() .map(|selection| { let mut selection = selection.map(|point| point.to_display_point(&display_map)); @@ -514,7 +555,7 @@ impl<'a> MutableSelectionsCollection<'a> { &mut self, mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec, ) { - let display_map = self.display_map.update(self.cx, |map, cx| map.snapshot(cx)); + let display_map = self.display_map(); let new_selections = find_replacement_cursors(&display_map) .into_iter() .map(|cursor| { diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index d707e55c3a..822b033dc3 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -54,5 +54,5 @@ pub fn assert_text_with_selections( let (unmarked_text, text_ranges) = marked_text_ranges(marked_text); assert_eq!(editor.text(cx), unmarked_text); - assert_eq!(editor.selected_ranges(cx), text_ranges); + assert_eq!(editor.selections.selected_ranges(cx), text_ranges); } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index bb889a5b37..f71f0c02f6 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -43,7 +43,7 @@ impl GoToLine { let buffer = editor.buffer().read(cx).read(cx); ( Some(scroll_position), - editor.selections.newest(&buffer).head(), + editor.selections.newest(cx).head(), buffer.max_point(), ) }); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 12f5168c9d..0687048713 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -172,7 +172,7 @@ impl PickerDelegate for OutlineView { let editor = self.active_editor.read(cx); let buffer = editor.buffer().read(cx).read(cx); - let cursor_offset = editor.selections.newest::(&buffer).head(); + let cursor_offset = editor.selections.newest::(cx).head(); selected_index = self .outline .items diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 00205b634c..2209578295 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -225,10 +225,7 @@ impl BufferSearchBar { let display_map = editor .update(cx, |editor, cx| editor.snapshot(cx)) .display_snapshot; - let selection = editor - .read(cx) - .selections - .newest::(&display_map.buffer_snapshot); + let selection = editor.read(cx).selections.newest::(cx); let mut text: String; if selection.start == selection.end { @@ -732,7 +729,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(0)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -743,7 +742,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -754,7 +755,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -765,7 +768,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -776,7 +781,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -787,7 +794,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -798,7 +807,9 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -817,7 +828,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(1)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -836,7 +849,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(1)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -855,7 +870,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(2)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -874,7 +891,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(2)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -893,7 +912,9 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(0)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 8bd043f86b..cac8b449c7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -891,7 +891,7 @@ mod tests { assert_eq!( search_view .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + .update(cx, |editor, cx| editor.selections.selected_display_ranges(cx)), [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] ); @@ -901,9 +901,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(1)); assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + search_view.results_editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] ); search_view.select_match(Direction::Next, cx); @@ -912,9 +912,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(2)); assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + search_view.results_editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] ); search_view.select_match(Direction::Next, cx); @@ -923,9 +923,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(0)); assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + search_view.results_editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] ); search_view.select_match(Direction::Prev, cx); @@ -934,9 +934,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(2)); assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + search_view.results_editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] ); search_view.select_match(Direction::Prev, cx); @@ -945,9 +945,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(1)); assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selected_display_ranges(cx)), + search_view.results_editor.update(cx, |editor, cx| editor + .selections + .selected_display_ranges(cx)), [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] ); }); diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/vim_test_context.rs index 28c15ffacd..f68e3f26d9 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/vim_test_context.rs @@ -200,7 +200,7 @@ impl<'a> VimTestContext<'a> { self.editor.read_with(self.cx, |editor, cx| { let (empty_selections, non_empty_selections): (Vec<_>, Vec<_>) = editor .selections - .interleaved::(&editor.buffer().read(cx).read(cx)) + .interleaved::(cx) .into_iter() .partition_map(|selection| { if selection.is_empty() { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 09d26517ba..a2fb6f740b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1180,7 +1180,7 @@ mod tests { let editor = item.downcast::().unwrap(); let (selections, scroll_position) = editor.update(cx, |editor, cx| { ( - editor.selected_display_ranges(cx), + editor.selections.selected_display_ranges(cx), editor.scroll_position(cx), ) }); From c3a36e6d8af163e43e4aaf24d9b6852158b7541c Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 12 May 2022 16:04:27 -0700 Subject: [PATCH 4/6] Rename selected_ranges and selected_display_ranges to remove redundant selected --- crates/collab/src/rpc.rs | 8 +- crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor.rs | 320 ++++++++++----------- crates/editor/src/selections_collection.rs | 7 +- crates/editor/src/test.rs | 2 +- crates/search/src/buffer_search.rs | 48 +--- crates/search/src/project_search.rs | 32 +-- crates/zed/src/zed.rs | 2 +- 8 files changed, 197 insertions(+), 226 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 2f182c1522..4bdb5459b1 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -5499,11 +5499,11 @@ mod tests { Some((worktree_id, "2.txt").into()) ); assert_eq!( - editor_b2.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)), + editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)), vec![2..3] ); assert_eq!( - editor_b1.read_with(cx_b, |editor, cx| editor.selections.selected_ranges(cx)), + editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)), vec![0..1] ); @@ -5546,7 +5546,7 @@ mod tests { }); editor_b1 .condition(cx_b, |editor, cx| { - editor.selections.selected_ranges(cx) == vec![1..1, 2..2] + editor.selections.ranges(cx) == vec![1..1, 2..2] }) .await; @@ -5561,7 +5561,7 @@ mod tests { }); editor_b1 .condition(cx_b, |editor, cx| { - editor.selections.selected_ranges(cx) == vec![3..3] + editor.selections.ranges(cx) == vec![3..3] }) .await; diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index d7eb089830..54fde4f4ad 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -897,7 +897,7 @@ mod tests { // Cursor is at the first diagnostic view.editor.update(cx, |editor, cx| { assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)] ); }); @@ -998,7 +998,7 @@ mod tests { // Cursor keeps its position. view.editor.update(cx, |editor, cx| { assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)] ); }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 60570360dc..8c6cf33e09 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6232,14 +6232,14 @@ mod tests { editor.insert("cd", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cd56"); - assert_eq!(editor.selections.selected_ranges(cx), vec![4..4]); + assert_eq!(editor.selections.ranges(cx), vec![4..4]); editor.start_transaction_at(now, cx); editor.change_selections(None, cx, |s| s.select_ranges([4..5])); editor.insert("e", cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]); + assert_eq!(editor.selections.ranges(cx), vec![5..5]); now += group_interval + Duration::from_millis(1); editor.change_selections(None, cx, |s| s.select_ranges([2..2])); @@ -6253,30 +6253,30 @@ mod tests { }); assert_eq!(editor.text(cx), "ab2cde6"); - assert_eq!(editor.selections.selected_ranges(cx), vec![3..3]); + assert_eq!(editor.selections.ranges(cx), vec![3..3]); // Last transaction happened past the group interval in a different editor. // Undo it individually and don't restore selections. editor.undo(&Undo, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selections.selected_ranges(cx), vec![2..2]); + assert_eq!(editor.selections.ranges(cx), vec![2..2]); // First two transactions happened within the group interval in this editor. // Undo them together and restore selections. editor.undo(&Undo, cx); editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op. assert_eq!(editor.text(cx), "123456"); - assert_eq!(editor.selections.selected_ranges(cx), vec![0..0]); + assert_eq!(editor.selections.ranges(cx), vec![0..0]); // Redo the first two transactions together. editor.redo(&Redo, cx); assert_eq!(editor.text(cx), "12cde6"); - assert_eq!(editor.selections.selected_ranges(cx), vec![5..5]); + assert_eq!(editor.selections.ranges(cx), vec![5..5]); // Redo the last transaction on its own. editor.redo(&Redo, cx); assert_eq!(editor.text(cx), "ab2cde6"); - assert_eq!(editor.selections.selected_ranges(cx), vec![6..6]); + assert_eq!(editor.selections.ranges(cx), vec![6..6]); // Test empty transactions. editor.start_transaction_at(now, cx); @@ -6296,7 +6296,7 @@ mod tests { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] ); @@ -6305,7 +6305,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); @@ -6314,7 +6314,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -6324,7 +6324,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -6334,7 +6334,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [ DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) @@ -6346,7 +6346,7 @@ mod tests { }); assert_eq!( - editor.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] ); } @@ -6360,7 +6360,7 @@ mod tests { view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] ); }); @@ -6368,7 +6368,7 @@ mod tests { view.update(cx, |view, cx| { view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); }); @@ -6377,7 +6377,7 @@ mod tests { view.cancel(&Cancel, cx); view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); }); @@ -6413,7 +6413,7 @@ mod tests { editor.navigate(nav_entry.data.unwrap(), cx); assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6423,7 +6423,7 @@ mod tests { editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx); editor.end_selection(cx); assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6433,14 +6433,14 @@ mod tests { editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx); editor.end_selection(cx); assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)] ); let nav_entry = nav_history.borrow_mut().pop_backward().unwrap(); editor.navigate(nav_entry.data.unwrap(), cx); assert_eq!(nav_entry.item.id(), cx.view_id()); assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] ); assert!(nav_history.borrow_mut().pop_backward().is_none()); @@ -6476,7 +6476,7 @@ mod tests { cx, ); assert_eq!( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), &[editor.max_point(cx)..editor.max_point(cx)] ); assert_eq!( @@ -6503,7 +6503,7 @@ mod tests { view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx); view.end_selection(cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), @@ -6514,7 +6514,7 @@ mod tests { view.update(cx, |view, cx| { view.cancel(&Cancel, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] ); }); @@ -6522,7 +6522,7 @@ mod tests { view.update(cx, |view, cx| { view.cancel(&Cancel, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] ); }); @@ -6633,43 +6633,43 @@ mod tests { view.update(cx, |view, cx| { assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); view.move_to_end(&MoveToEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)] ); view.move_to_beginning(&MoveToBeginning, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); @@ -6678,13 +6678,13 @@ mod tests { }); view.select_to_beginning(&SelectToBeginning, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)] ); view.select_to_end(&SelectToEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)] ); }); @@ -6712,80 +6712,80 @@ mod tests { view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐⓑ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐⓑ…".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(1, "ab…".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(1, "ab".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(1, "a".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "α".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "αβ".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "αβ…".len())] ); view.move_right(&MoveRight, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "αβ…ε".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(1, "ab…e".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐⓑ…ⓔ".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐⓑ…".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐⓑ".len())] ); view.move_left(&MoveLeft, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(0, "ⓐ".len())] ); }); @@ -6802,37 +6802,37 @@ mod tests { }); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(1, "abcd".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "αβγ".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(3, "abcd".len())] ); view.move_down(&MoveDown, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(3, "abcd".len())] ); view.move_up(&MoveUp, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[empty_range(2, "αβγ".len())] ); }); @@ -6855,7 +6855,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -6866,7 +6866,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), @@ -6877,7 +6877,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -6888,7 +6888,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_end_of_line(&MoveToEndOfLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -6900,7 +6900,7 @@ mod tests { view.update(cx, |view, cx| { view.move_to_end_of_line(&MoveToEndOfLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -6917,7 +6917,7 @@ mod tests { cx, ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), @@ -6933,7 +6933,7 @@ mod tests { cx, ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0), @@ -6949,7 +6949,7 @@ mod tests { cx, ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), @@ -6965,7 +6965,7 @@ mod tests { cx, ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5), @@ -6977,7 +6977,7 @@ mod tests { view.delete_to_end_of_line(&DeleteToEndOfLine, cx); assert_eq!(view.display_text(cx), "ab\n de"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), @@ -6989,7 +6989,7 @@ mod tests { view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); assert_eq!(view.display_text(cx), "\n"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), @@ -7121,37 +7121,37 @@ mod tests { view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] ); view.move_to_next_word_end(&MoveToNextWordEnd, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)] ); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] ); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] ); }); @@ -7278,7 +7278,7 @@ mod tests { editor.update(cx, |editor, cx| { assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), &[ Point::new(1, 2)..Point::new(1, 2), Point::new(2, 2)..Point::new(2, 2), @@ -7300,7 +7300,7 @@ mod tests { // The selections are moved after the inserted newlines assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), &[ Point::new(2, 0)..Point::new(2, 0), Point::new(4, 0)..Point::new(4, 0), @@ -7326,13 +7326,13 @@ mod tests { }); editor.update(cx, |editor, cx| { - assert_eq!(editor.selections.selected_ranges(cx), &[2..2, 7..7, 12..12],); + assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],); editor.insert("Z", cx); assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)"); // The selections are moved after the inserted characters - assert_eq!(editor.selections.selected_ranges(cx), &[3..3, 9..9, 15..15],); + assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],); }); } @@ -7641,7 +7641,7 @@ mod tests { view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1) @@ -7659,7 +7659,7 @@ mod tests { view.delete_line(&DeleteLine, cx); assert_eq!(view.display_text(cx), "ghi\n"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)] ); }); @@ -7682,7 +7682,7 @@ mod tests { view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), @@ -7704,7 +7704,7 @@ mod tests { view.duplicate_line(&DuplicateLine, cx); assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n"); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1), DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1), @@ -7746,7 +7746,7 @@ mod tests { "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -7763,7 +7763,7 @@ mod tests { "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), @@ -7780,7 +7780,7 @@ mod tests { "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), @@ -7797,7 +7797,7 @@ mod tests { "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -7841,15 +7841,15 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([1..1])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.selected_ranges(cx), [2..2]); + assert_eq!(editor.selections.ranges(cx), [2..2]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bca"); - assert_eq!(editor.selections.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.ranges(cx), [3..3]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.ranges(cx), [3..3]); editor }) @@ -7861,20 +7861,20 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acb\nde"); - assert_eq!(editor.selections.selected_ranges(cx), [3..3]); + assert_eq!(editor.selections.ranges(cx), [3..3]); editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.selected_ranges(cx), [5..5]); + assert_eq!(editor.selections.ranges(cx), [5..5]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbde\n"); - assert_eq!(editor.selections.selected_ranges(cx), [6..6]); + assert_eq!(editor.selections.ranges(cx), [6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.selected_ranges(cx), [6..6]); + assert_eq!(editor.selections.ranges(cx), [6..6]); editor }) @@ -7886,23 +7886,23 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bacd\ne"); - assert_eq!(editor.selections.selected_ranges(cx), [2..2, 3..3, 5..5]); + assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.selected_ranges(cx), [3..3, 4..4, 6..6]); + assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcda\ne"); - assert_eq!(editor.selections.selected_ranges(cx), [4..4, 6..6]); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.selected_ranges(cx), [4..4, 6..6]); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "bcaed\n"); - assert_eq!(editor.selections.selected_ranges(cx), [5..5, 6..6]); + assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); editor }) @@ -7914,15 +7914,15 @@ mod tests { editor.change_selections(None, cx, |s| s.select_ranges([4..4])); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.selected_ranges(cx), [8..8]); + assert_eq!(editor.selections.ranges(cx), [8..8]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀✋🍐"); - assert_eq!(editor.selections.selected_ranges(cx), [11..11]); + assert_eq!(editor.selections.ranges(cx), [11..11]); editor.transpose(&Default::default(), cx); assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.selected_ranges(cx), [11..11]); + assert_eq!(editor.selections.ranges(cx), [11..11]); editor }) @@ -7950,7 +7950,7 @@ mod tests { view.paste(&Paste, cx); assert_eq!(view.display_text(cx), "two one✅ four three six five "); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22), @@ -8014,7 +8014,7 @@ mod tests { "123\n4567\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) " ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), @@ -8047,7 +8047,7 @@ mod tests { "123\n123\n123\n67\n123\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) " ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[ DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), @@ -8065,7 +8065,7 @@ mod tests { view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)] ); }); @@ -8087,7 +8087,7 @@ mod tests { }); view.select_line(&SelectLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0), DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0), @@ -8098,7 +8098,7 @@ mod tests { view.update(cx, |view, cx| { view.select_line(&SelectLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0), DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5), @@ -8109,7 +8109,7 @@ mod tests { view.update(cx, |view, cx| { view.select_line(&SelectLine, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)] ); }); @@ -8147,7 +8147,7 @@ mod tests { "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), @@ -8167,7 +8167,7 @@ mod tests { "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii" ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [ DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5), DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), @@ -8196,7 +8196,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8207,7 +8207,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8218,13 +8218,13 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] ); view.undo_selection(&UndoSelection, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) @@ -8233,7 +8233,7 @@ mod tests { view.redo_selection(&RedoSelection, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] ); }); @@ -8241,7 +8241,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) @@ -8252,7 +8252,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) @@ -8268,7 +8268,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) @@ -8279,7 +8279,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) @@ -8290,7 +8290,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] ); }); @@ -8298,7 +8298,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] ); }); @@ -8309,7 +8309,7 @@ mod tests { }); view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8321,7 +8321,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8334,7 +8334,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), @@ -8351,7 +8351,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_above(&AddSelectionAbove, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), @@ -8364,7 +8364,7 @@ mod tests { view.update(cx, |view, cx| { view.add_selection_below(&AddSelectionBelow, cx); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), vec![ DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), @@ -8392,7 +8392,7 @@ mod tests { }, cx, ); - assert_eq!(view.selections.selected_ranges(cx), &ranges[1..2]); + assert_eq!(view.selections.ranges(cx), &ranges[1..2]); view.select_next( &SelectNext { @@ -8400,13 +8400,13 @@ mod tests { }, cx, ); - assert_eq!(view.selections.selected_ranges(cx), &ranges[1..3]); + assert_eq!(view.selections.ranges(cx), &ranges[1..3]); view.undo_selection(&UndoSelection, cx); - assert_eq!(view.selections.selected_ranges(cx), &ranges[1..2]); + assert_eq!(view.selections.ranges(cx), &ranges[1..2]); view.redo_selection(&RedoSelection, cx); - assert_eq!(view.selections.selected_ranges(cx), &ranges[1..3]); + assert_eq!(view.selections.ranges(cx), &ranges[1..3]); view.select_next( &SelectNext { @@ -8414,7 +8414,7 @@ mod tests { }, cx, ); - assert_eq!(view.selections.selected_ranges(cx), &ranges[1..4]); + assert_eq!(view.selections.ranges(cx), &ranges[1..4]); view.select_next( &SelectNext { @@ -8422,7 +8422,7 @@ mod tests { }, cx, ); - assert_eq!(view.selections.selected_ranges(cx), &ranges[0..4]); + assert_eq!(view.selections.ranges(cx), &ranges[0..4]); }); } @@ -8460,9 +8460,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| { - view.selections.selected_display_ranges(cx) - }), + view.update(cx, |view, cx| { view.selections.display_ranges(cx) }), &[ DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8474,7 +8472,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), @@ -8485,7 +8483,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] ); @@ -8494,7 +8492,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] ); @@ -8502,7 +8500,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), @@ -8513,7 +8511,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8525,7 +8523,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), @@ -8538,7 +8536,7 @@ mod tests { view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), @@ -8559,7 +8557,7 @@ mod tests { view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); }); assert_eq!( - view.update(cx, |view, cx| view.selections.selected_display_ranges(cx)), + view.update(cx, |view, cx| view.selections.display_ranges(cx)), &[ DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), @@ -8615,7 +8613,7 @@ mod tests { editor.newline(&Newline, cx); assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), &[ Point::new(1, 4)..Point::new(1, 4), Point::new(3, 4)..Point::new(3, 4), @@ -8768,7 +8766,7 @@ mod tests { .unindent() ); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)] ); }); @@ -8798,10 +8796,7 @@ mod tests { marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone()]); let selection_ranges = selection_ranges_lookup.remove(&range_markers).unwrap(); assert_eq!(editor.text(cx), expected_text); - assert_eq!( - editor.selections.selected_ranges::(cx), - selection_ranges - ); + assert_eq!(editor.selections.ranges::(cx), selection_ranges); } assert( editor, @@ -9335,7 +9330,7 @@ mod tests { view.handle_input(&Input("X".to_string()), cx); assert_eq!(view.text(cx), "Xaaaa\nXbbbb"); assert_eq!( - view.selections.selected_ranges(cx), + view.selections.ranges(cx), [ Point::new(0, 1)..Point::new(0, 1), Point::new(1, 1)..Point::new(1, 1), @@ -9376,7 +9371,7 @@ mod tests { bX|bbX|b cccc"}); assert_eq!(view.text(cx), expected_text); - assert_eq!(view.selections.selected_ranges(cx), expected_selections); + assert_eq!(view.selections.ranges(cx), expected_selections); view.newline(&Newline, cx); let (expected_text, expected_selections) = marked_text_ranges(indoc! {" @@ -9389,7 +9384,7 @@ mod tests { |b cccc"}); assert_eq!(view.text(cx), expected_text); - assert_eq!(view.selections.selected_ranges(cx), expected_selections); + assert_eq!(view.selections.ranges(cx), expected_selections); }); } @@ -9425,7 +9420,7 @@ mod tests { }); editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [ Point::new(1, 3)..Point::new(1, 3), Point::new(2, 1)..Point::new(2, 1), @@ -9440,7 +9435,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [ Point::new(1, 3)..Point::new(1, 3), Point::new(2, 1)..Point::new(2, 1), @@ -9454,7 +9449,7 @@ mod tests { editor.update(cx, |editor, cx| { // Removing an excerpt causes the first selection to become degenerate. assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [ Point::new(0, 0)..Point::new(0, 0), Point::new(0, 1)..Point::new(0, 1) @@ -9467,7 +9462,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [ Point::new(0, 1)..Point::new(0, 1), Point::new(0, 3)..Point::new(0, 3) @@ -9506,7 +9501,7 @@ mod tests { let snapshot = editor.snapshot(cx); editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [Point::new(1, 3)..Point::new(1, 3)] ); editor @@ -9517,7 +9512,7 @@ mod tests { }); editor.update(cx, |editor, cx| { assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [Point::new(0, 0)..Point::new(0, 0)] ); @@ -9526,7 +9521,7 @@ mod tests { s.refresh(); }); assert_eq!( - editor.selections.selected_ranges(cx), + editor.selections.ranges(cx), [Point::new(0, 3)..Point::new(0, 3)] ); assert!(editor.selections.pending_anchor().is_some()); @@ -9718,7 +9713,7 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![1..1]); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]); // Update the scroll position only leader.update(cx, |leader, cx| { @@ -9748,7 +9743,7 @@ mod tests { assert_eq!(follower.scroll_position(cx), initial_scroll_position); assert!(follower.autoscroll_request.is_some()); }); - assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![0..0]); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]); // Creating a pending selection that precedes another selection leader.update(cx, |leader, cx| { @@ -9760,10 +9755,7 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!( - follower.read(cx).selections.selected_ranges(cx), - vec![0..0, 1..1] - ); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]); // Extend the pending selection so that it surrounds another selection leader.update(cx, |leader, cx| { @@ -9774,7 +9766,7 @@ mod tests { .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) .unwrap(); }); - assert_eq!(follower.read(cx).selections.selected_ranges(cx), vec![0..2]); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]); } #[test] @@ -9877,7 +9869,7 @@ mod tests { }) .collect(); assert_eq!( - view.selections.selected_display_ranges(cx), + view.selections.display_ranges(cx), &asserted_ranges[..], "Assert selections are {}", marked_text diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index a5fac015d6..5965ad58f3 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -129,6 +129,9 @@ impl SelectionsCollection { range: Range, cx: &AppContext, ) -> Vec> { + // TODO: Make sure pending selection is handled correctly here + // if it is interleaved properly, we can sue resolve_multiple + // to improve performance let buffer = self.buffer(cx); let start_ix = match self .disjoint @@ -211,7 +214,7 @@ impl SelectionsCollection { } #[cfg(any(test, feature = "test-support"))] - pub fn selected_ranges + std::fmt::Debug>( + pub fn ranges + std::fmt::Debug>( &self, cx: &AppContext, ) -> Vec> { @@ -228,7 +231,7 @@ impl SelectionsCollection { } #[cfg(any(test, feature = "test-support"))] - pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec> { + pub fn display_ranges(&self, cx: &mut MutableAppContext) -> Vec> { let display_map = self.display_map(cx); self.disjoint_anchors() .iter() diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 822b033dc3..cb064be545 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -54,5 +54,5 @@ pub fn assert_text_with_selections( let (unmarked_text, text_ranges) = marked_text_ranges(marked_text); assert_eq!(editor.text(cx), unmarked_text); - assert_eq!(editor.selections.selected_ranges(cx), text_ranges); + assert_eq!(editor.selections.ranges(cx), text_ranges); } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 2209578295..ec09fd45b5 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -729,9 +729,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(0)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -742,9 +740,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -755,9 +751,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -768,9 +762,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -781,9 +773,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -794,9 +784,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -807,9 +795,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -828,9 +814,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(1)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -849,9 +833,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(1)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)] ); }); @@ -870,9 +852,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(2)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); @@ -891,9 +871,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(2)); search_bar.select_next_match(&SelectNextMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)] ); }); @@ -912,9 +890,7 @@ mod tests { assert_eq!(search_bar.active_match_index, Some(0)); search_bar.select_prev_match(&SelectPrevMatch, cx); assert_eq!( - editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)] ); }); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index cac8b449c7..4915d50ec9 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -891,7 +891,7 @@ mod tests { assert_eq!( search_view .results_editor - .update(cx, |editor, cx| editor.selections.selected_display_ranges(cx)), + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] ); @@ -901,9 +901,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(1)); assert_eq!( - search_view.results_editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + search_view + .results_editor + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] ); search_view.select_match(Direction::Next, cx); @@ -912,9 +912,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(2)); assert_eq!( - search_view.results_editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + search_view + .results_editor + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] ); search_view.select_match(Direction::Next, cx); @@ -923,9 +923,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(0)); assert_eq!( - search_view.results_editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + search_view + .results_editor + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] ); search_view.select_match(Direction::Prev, cx); @@ -934,9 +934,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(2)); assert_eq!( - search_view.results_editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + search_view + .results_editor + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] ); search_view.select_match(Direction::Prev, cx); @@ -945,9 +945,9 @@ mod tests { search_view.update(cx, |search_view, cx| { assert_eq!(search_view.active_match_index, Some(1)); assert_eq!( - search_view.results_editor.update(cx, |editor, cx| editor - .selections - .selected_display_ranges(cx)), + search_view + .results_editor + .update(cx, |editor, cx| editor.selections.display_ranges(cx)), [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] ); }); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a2fb6f740b..fc8c9b3534 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1180,7 +1180,7 @@ mod tests { let editor = item.downcast::().unwrap(); let (selections, scroll_position) = editor.update(cx, |editor, cx| { ( - editor.selections.selected_display_ranges(cx), + editor.selections.display_ranges(cx), editor.scroll_position(cx), ) }); From 20c97637a4a5dda781dc1d8e5342604055f242fa Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Fri, 13 May 2022 12:55:15 -0700 Subject: [PATCH 5/6] minor tweaks to selections collection api --- crates/editor/src/editor.rs | 78 +++++++--------------- crates/editor/src/selections_collection.rs | 24 +++++-- crates/vim/src/normal.rs | 4 +- 3 files changed, 46 insertions(+), 60 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8c6cf33e09..3a9934e5c6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1392,20 +1392,6 @@ impl Editor { result } - pub fn display_selections( - &mut self, - cx: &mut ViewContext, - ) -> (DisplaySnapshot, Vec>) { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self - .selections - .interleaved::(cx) - .into_iter() - .map(|selection| selection.map(|point| point.to_display_point(&display_map))) - .collect(); - (display_map, selections) - } - pub fn edit(&mut self, edits: I, cx: &mut ViewContext) where I: IntoIterator, T)>, @@ -1464,25 +1450,27 @@ impl Editor { let position = position.to_offset(&display_map, Bias::Left); let tail_anchor = display_map.buffer_snapshot.anchor_before(tail); + let mut pending_selection = self + .selections + .pending_anchor() + .expect("extend_selection not called with pending selection"); + if position >= tail { + pending_selection.start = tail_anchor.clone(); + } else { + pending_selection.end = tail_anchor.clone(); + pending_selection.reversed = true; + } + + let mut pending_mode = self.selections.pending_mode().unwrap(); + match &mut pending_mode { + SelectMode::Word(range) | SelectMode::Line(range) => { + *range = tail_anchor.clone()..tail_anchor + } + _ => {} + } + self.change_selections(Some(Autoscroll::Fit), cx, |s| { - let mut pending = s - .pending_mut() - .as_mut() - .expect("extend_selection not called with pending selection"); - - if position >= tail { - pending.selection.start = tail_anchor.clone(); - } else { - pending.selection.end = tail_anchor.clone(); - pending.selection.reversed = true; - } - - match &mut pending.mode { - SelectMode::Word(range) | SelectMode::Line(range) => { - *range = tail_anchor.clone()..tail_anchor - } - _ => {} - } + s.set_pending(pending_selection, pending_mode) }); } @@ -1591,7 +1579,8 @@ impl Editor { let buffer = self.buffer.read(cx).snapshot(cx); let head; let tail; - match &self.selections.pending_mode().unwrap() { + let mode = self.selections.pending_mode().unwrap(); + match &mode { SelectMode::Character => { head = position.to_point(&display_map); tail = pending.tail().to_point(&buffer); @@ -1660,7 +1649,7 @@ impl Editor { } self.change_selections(None, cx, |s| { - s.pending_mut().as_mut().unwrap().selection = pending; + s.set_pending(pending, mode); }); } else { log::error!("update_selection dispatched with no pending selection"); @@ -1745,8 +1734,7 @@ impl Editor { return; } - if self.change_selections(None, cx, |s| s.try_cancel()) { - self.request_autoscroll(Autoscroll::Fit, cx); + if self.change_selections(Some(Autoscroll::Fit), cx, |s| s.try_cancel()) { return; } } @@ -1851,8 +1839,6 @@ impl Editor { .collect(); this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections)); - - this.request_autoscroll(Autoscroll::Fit, cx); }); } @@ -1972,13 +1958,7 @@ impl Editor { let mut snapshot = buffer.snapshot(cx); let left_biased_selections = selections .iter() - .map(|selection| Selection { - id: selection.id, - start: snapshot.anchor_before(selection.start), - end: snapshot.anchor_before(selection.end), - reversed: selection.reversed, - goal: selection.goal, - }) + .map(|selection| selection.map(|p| snapshot.anchor_before(p))) .collect::>(); let autoclose_pair = snapshot.language().and_then(|language| { @@ -2730,13 +2710,6 @@ impl Editor { } } if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) { - let snapshot = self.buffer.read(cx).snapshot(cx); - let current_ranges_resolved = current_ranges - .iter() - .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot)) - .collect::>(); - dbg!(¤t_ranges, current_ranges_resolved); - self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select_anchor_ranges(current_ranges.into_iter().cloned()) }); @@ -4024,7 +3997,6 @@ impl Editor { } fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { - self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut selections = self.selections.interleaved::(cx); let mut state = self.add_selections_state.take().unwrap_or_else(|| { diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 5965ad58f3..d889d14c14 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -169,6 +169,19 @@ impl SelectionsCollection { .collect() } + pub fn display_interleaved( + &mut self, + cx: &mut MutableAppContext, + ) -> (DisplaySnapshot, Vec>) { + let display_map = self.display_map(cx); + let selections = self + .interleaved::(cx) + .into_iter() + .map(|selection| selection.map(|point| point.to_display_point(&display_map))) + .collect(); + (display_map, selections) + } + pub fn newest_anchor(&self) -> &Selection { self.pending .as_ref() @@ -308,12 +321,12 @@ impl<'a> MutableSelectionsCollection<'a> { mode, }) } - pub fn pending_mut(&mut self) -> &mut Option { - &mut self.collection.pending + + pub fn set_pending(&mut self, selection: Selection, mode: SelectMode) { + self.collection.pending = Some(PendingSelection { selection, mode }); } pub fn try_cancel(&mut self) -> bool { - let buffer = self.buffer.read(self.cx).snapshot(self.cx); if let Some(pending) = self.collection.pending.take() { if self.disjoint.is_empty() { self.collection.disjoint = Arc::from([pending.selection]); @@ -327,7 +340,7 @@ impl<'a> MutableSelectionsCollection<'a> { return true; } - if !oldest.start.cmp(&oldest.end, &buffer).is_eq() { + if !oldest.start.cmp(&oldest.end, &self.buffer()).is_eq() { let head = oldest.head(); oldest.start = head.clone(); oldest.end = head; @@ -584,7 +597,8 @@ impl<'a> MutableSelectionsCollection<'a> { pub fn refresh(&mut self) -> HashMap { // TODO: Pull disjoint constraint out of update_selections so we don't have to // store the pending_selection here. - let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let buffer = self.buffer(); + let mut pending = self.collection.pending.take(); let mut selections_with_lost_position = HashMap::default(); diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index be218592cc..36fec6fbfc 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -130,7 +130,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - let (map, old_selections) = editor.display_selections(cx); + let (map, old_selections) = editor.selections.display_interleaved(cx); let selection_start_rows: HashSet = old_selections .into_iter() .map(|selection| selection.start.row()) @@ -162,7 +162,7 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - let (map, old_selections) = editor.display_selections(cx); + let (map, old_selections) = editor.selections.display_interleaved(cx); let selection_end_rows: HashSet = old_selections .into_iter() .map(|selection| selection.end.row()) From 45ea3d4c38832aa37564d0928e9c6fdbe7d31f9b Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Fri, 13 May 2022 15:49:14 -0700 Subject: [PATCH 6/6] Review fixes --- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/editor.rs | 141 +++++++-------------- crates/editor/src/element.rs | 5 +- crates/editor/src/items.rs | 2 +- crates/editor/src/selections_collection.rs | 105 ++++++++------- crates/vim/src/normal.rs | 4 +- crates/vim/src/vim_test_context.rs | 2 +- 7 files changed, 111 insertions(+), 150 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 54fde4f4ad..5e94bbe055 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -419,7 +419,7 @@ impl ProjectDiagnosticsEditor { groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice(); new_excerpt_ids_by_selection_id = editor.change_selections(Some(Autoscroll::Fit), cx, |s| s.refresh()); - selections = editor.selections.interleaved::(cx); + selections = editor.selections.all::(cx); } // If any selection has lost its position, move it to start of the next primary diagnostic. diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3a9934e5c6..9b160acf64 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1191,7 +1191,7 @@ impl Editor { first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32; last_cursor_bottom = first_cursor_top + 1.; } else { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); first_cursor_top = selections .first() .unwrap() @@ -1251,7 +1251,7 @@ impl Editor { cx: &mut ViewContext, ) -> bool { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut target_left; let mut target_right; @@ -1663,7 +1663,7 @@ impl Editor { fn end_selection(&mut self, cx: &mut ViewContext) { self.columnar_selection_tail.take(); if self.selections.pending_anchor().is_some() { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); self.change_selections(None, cx, |s| { s.select(selections); s.clear_pending(); @@ -1763,7 +1763,7 @@ impl Editor { pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = { - let selections = this.selections.interleaved::(cx); + let selections = this.selections.all::(cx); let buffer = this.buffer.read(cx).snapshot(cx); selections @@ -1845,7 +1845,7 @@ impl Editor { pub fn insert(&mut self, text: &str, cx: &mut ViewContext) { let text: Arc = text.into(); self.transact(cx, |this, cx| { - let old_selections = this.selections.interleaved::(cx); + let old_selections = this.selections.all::(cx); let selection_anchors = this.buffer.update(cx, |buffer, cx| { let anchors = { let snapshot = buffer.read(cx); @@ -1908,7 +1908,7 @@ impl Editor { { if self .selections - .interleaved::(cx) + .all::(cx) .iter() .any(|selection| selection.is_empty()) { @@ -1951,7 +1951,7 @@ impl Editor { } fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext) { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut bracket_pair_state = None; let mut new_selections = None; self.buffer.update(cx, |buffer, cx| { @@ -2045,7 +2045,7 @@ impl Editor { fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext) -> bool { let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self.selections.interleaved::(cx); + let old_selections = self.selections.all::(cx); let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() { autoclose_pair } else { @@ -2214,7 +2214,7 @@ impl Editor { snippet = None; text = completion.new_text.clone(); }; - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let buffer = buffer_handle.read(cx); let old_range = completion.old_range.to_offset(&buffer); let old_text = buffer.text_for_range(old_range.clone()).collect::(); @@ -2733,7 +2733,7 @@ impl Editor { pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); for selection in &mut selections { if selection.is_empty() { let old_head = selection.head(); @@ -2792,7 +2792,7 @@ impl Editor { return; } - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); if selections.iter().all(|s| s.is_empty()) { self.transact(cx, |this, cx| { this.buffer.update(cx, |buffer, cx| { @@ -2827,7 +2827,7 @@ impl Editor { } pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext) { - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); self.transact(cx, |this, cx| { let mut last_indent = None; this.buffer.update(cx, |buffer, cx| { @@ -2890,7 +2890,7 @@ impl Editor { pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut deletion_ranges = Vec::new(); let mut last_outdent = None; { @@ -2933,17 +2933,14 @@ impl Editor { cx, ); }); - this.change_selections(Some(Autoscroll::Fit), cx, |s| { - // TODO: Make sure this is a reasonable change - // This used to call select(local_selections) - s.refresh() - }); + let selections = this.selections.all::(cx); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); }); } pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut new_cursors = Vec::new(); let mut edit_ranges = Vec::new(); @@ -3025,7 +3022,7 @@ impl Editor { pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut edits = Vec::new(); let mut selections_iter = selections.iter().peekable(); @@ -3070,7 +3067,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3182,7 +3179,7 @@ impl Editor { let mut unfold_ranges = Vec::new(); let mut refold_ranges = Vec::new(); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); let mut new_selections = Vec::new(); @@ -3320,10 +3317,9 @@ impl Editor { edits }); this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); + let selections = this.selections.all::(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| { - // TODO: Make sure this swap is reasonable. - // This was select(interleaved) - s.refresh(); + s.select(selections); }); }); } @@ -3331,7 +3327,7 @@ impl Editor { pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext) { let mut text = String::new(); let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); let mut clipboard_selections = Vec::with_capacity(selections.len()); { let max_point = buffer.max_point(); @@ -3364,7 +3360,7 @@ impl Editor { } pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let buffer = self.buffer.read(cx).read(cx); let mut text = String::new(); let mut clipboard_selections = Vec::with_capacity(selections.len()); @@ -3398,7 +3394,7 @@ impl Editor { if let Some(item) = cx.as_mut().read_from_clipboard() { let mut clipboard_text = Cow::Borrowed(item.text()); if let Some(mut clipboard_selections) = item.metadata::>() { - let old_selections = this.selections.interleaved::(cx); + let old_selections = this.selections.all::(cx); let all_selections_were_entire_line = clipboard_selections.iter().all(|s| s.is_entire_line); if clipboard_selections.len() != old_selections.len() { @@ -3451,7 +3447,7 @@ impl Editor { buffer.edit_with_autoindent(edits, cx); }); - let selections = this.selections.interleaved::(cx); + let selections = this.selections.all::(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); } else { this.insert(&clipboard_text, cx); @@ -3464,8 +3460,6 @@ impl Editor { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) { if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() { self.change_selections(None, cx, |s| { - // TODO: move to SelectionsCollection to preserve selection - // invariants without rechecking s.select_anchors(selections.to_vec()); }); } @@ -3479,8 +3473,6 @@ impl Editor { if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned() { self.change_selections(None, cx, |s| { - // TODO: move to SelectionsCollection to preserve selection - // invariants without rechecking s.select_anchors(selections.to_vec()); }); } @@ -3950,7 +3942,7 @@ impl Editor { pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); let max_point = display_map.buffer_snapshot.max_point(); for selection in &mut selections { let rows = selection.spanned_rows(true, &display_map); @@ -3971,7 +3963,7 @@ impl Editor { let mut to_unfold = Vec::new(); let mut new_selection_ranges = Vec::new(); { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let buffer = self.buffer.read(cx).read(cx); for selection in selections { for row in selection.start.row..selection.end.row { @@ -3998,7 +3990,7 @@ impl Editor { fn add_selection(&mut self, above: bool, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); let mut state = self.add_selections_state.take().unwrap_or_else(|| { let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone(); let range = oldest_selection.display_range(&display_map).sorted(); @@ -4008,7 +4000,7 @@ impl Editor { selections.clear(); let mut stack = Vec::new(); for row in range.start.row()..=range.end.row() { - if let Some(selection) = self.build_columnar_selection( + if let Some(selection) = self.selections.build_columnar_selection( &display_map, row, &columns, @@ -4055,7 +4047,7 @@ impl Editor { row += 1; } - if let Some(new_selection) = self.build_columnar_selection( + if let Some(new_selection) = self.selections.build_columnar_selection( &display_map, row, &columns, @@ -4095,7 +4087,7 @@ impl Editor { self.push_to_selection_history(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); if let Some(mut select_next_state) = self.select_next_state.take() { let query = &select_next_state.query; if !select_next_state.done { @@ -4185,7 +4177,7 @@ impl Editor { pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - let mut selections = this.selections.interleaved::(cx); + let mut selections = this.selections.all::(cx); let mut all_selection_lines_are_comments = true; let mut edit_ranges = Vec::new(); let mut last_toggled_row = None; @@ -4286,10 +4278,8 @@ impl Editor { } }); - let selections = this.selections.interleaved::(cx); - this.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select(selections); - }); + let selections = this.selections.all::(cx); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); }); } @@ -4300,7 +4290,7 @@ impl Editor { ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self.selections.interleaved::(cx).into_boxed_slice(); + let old_selections = self.selections.all::(cx).into_boxed_slice(); let mut stack = mem::take(&mut self.select_larger_syntax_node_stack); let mut selected_larger_node = false; @@ -4360,7 +4350,7 @@ impl Editor { cx: &mut ViewContext, ) { let buffer = self.buffer.read(cx).snapshot(cx); - let mut selections = self.selections.interleaved::(cx); + let mut selections = self.selections.all::(cx); for selection in &mut selections { if let Some((open_range, close_range)) = buffer.enclosing_bracket_ranges(selection.start..selection.end) @@ -4387,11 +4377,7 @@ impl Editor { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Undoing; if let Some(entry) = self.selection_history.undo_stack.pop_back() { - self.change_selections(None, cx, |s| { - // TODO: Move to selections so selections invariants can be preserved rather than - // rechecking them. - s.select_anchors(entry.selections.to_vec()) - }); + self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec())); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::Newest, cx); @@ -4403,11 +4389,7 @@ impl Editor { self.end_selection(cx); self.selection_history.mode = SelectionHistoryMode::Redoing; if let Some(entry) = self.selection_history.redo_stack.pop_back() { - self.change_selections(None, cx, |s| { - // TODO: Move to selections so selections invariants can be preserved rather than - // rechecking them. - s.select_anchors(entry.selections.to_vec()) - }); + self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec())); self.select_next_state = entry.select_next_state; self.add_selections_state = entry.add_selections_state; self.request_autoscroll(Autoscroll::Newest, cx); @@ -4827,14 +4809,9 @@ impl Editor { .min(rename_range.end); drop(snapshot); - let new_selection = Selection { - id: self.selections.newest_anchor().id, - start: cursor_in_editor, - end: cursor_in_editor, - reversed: false, - goal: SelectionGoal::None, - }; - self.change_selections(None, cx, |s| s.select(vec![new_selection])); + self.change_selections(None, cx, |s| { + s.select_ranges(vec![cursor_in_editor..cursor_in_editor]) + }); } Some(rename) @@ -4945,34 +4922,6 @@ impl Editor { } } - fn build_columnar_selection( - &mut self, - display_map: &DisplaySnapshot, - row: u32, - columns: &Range, - reversed: bool, - ) -> Option> { - let is_empty = columns.start == columns.end; - let line_len = display_map.line_len(row); - if columns.start < line_len || (is_empty && columns.start == line_len) { - let start = DisplayPoint::new(row, columns.start); - let end = DisplayPoint::new(row, cmp::min(columns.end, line_len)); - // TODO: Don't expose next_selection_id - Some(Selection { - id: post_inc(&mut self.selections.next_selection_id), - start: start.to_point(display_map), - end: end.to_point(display_map), - reversed, - goal: SelectionGoal::ColumnRange { - start: columns.start, - end: columns.end, - }, - }) - } else { - None - } - } - pub fn set_selections_from_remote( &mut self, selections: Vec>, @@ -5051,7 +5000,7 @@ impl Editor { let mut fold_ranges = Vec::new(); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); for selection in selections { let range = selection.display_range(&display_map).sorted(); let buffer_start_row = range.start.to_point(&display_map).row; @@ -5075,7 +5024,7 @@ impl Editor { pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let ranges = selections .iter() .map(|s| { @@ -5133,7 +5082,7 @@ impl Editor { } pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext) { - let selections = self.selections.interleaved::(cx); + let selections = self.selections.all::(cx); let ranges = selections.into_iter().map(|s| s.start..s.end); self.fold_ranges(ranges, cx); } @@ -5510,7 +5459,7 @@ impl Editor { } let mut new_selections_by_buffer = HashMap::default(); - for selection in editor.selections.interleaved::(cx) { + for selection in editor.selections.all::(cx) { for (buffer, mut range) in buffer.range_to_buffer_ranges(selection.start..selection.end, cx) { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 06c11db137..d3d67060f2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -957,9 +957,10 @@ impl Element for EditorElement { selections.extend(remote_selections); if view.show_local_selections { - let local_selections = view + let mut local_selections = view .selections - .interleaved_in_range(start_anchor..end_anchor, cx); + .disjoint_in_range(start_anchor..end_anchor, cx); + local_selections.extend(view.selections.pending(cx)); for selection in &local_selections { let is_empty = selection.start == selection.end; let selection_start = snapshot.prev_line_boundary(selection.start).1; diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 165f4ca676..4b4df09c3a 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -465,7 +465,7 @@ impl CursorPosition { self.selected_count = 0; let mut last_selection: Option> = None; - for selection in editor.selections.interleaved::(cx) { + for selection in editor.selections.all::(cx) { self.selected_count += selection.end - selection.start; if last_selection .as_ref() diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index d889d14c14..aabfb676ff 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -1,5 +1,6 @@ use std::{ - iter, mem, + cell::Ref, + cmp, iter, mem, ops::{Deref, Range, Sub}, sync::Arc, }; @@ -53,8 +54,8 @@ impl SelectionsCollection { self.display_map.update(cx, |map, cx| map.snapshot(cx)) } - fn buffer(&self, cx: &AppContext) -> MultiBufferSnapshot { - self.buffer.read(cx).snapshot(cx) + fn buffer<'a>(&self, cx: &'a AppContext) -> Ref<'a, MultiBufferSnapshot> { + self.buffer.read(cx).read(cx) } pub fn count<'a>(&self) -> usize { @@ -88,7 +89,7 @@ impl SelectionsCollection { self.pending.as_ref().map(|pending| pending.mode.clone()) } - pub fn interleaved<'a, D>(&self, cx: &AppContext) -> Vec> + pub fn all<'a, D>(&self, cx: &AppContext) -> Vec> where D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, { @@ -124,14 +125,14 @@ impl SelectionsCollection { .collect() } - pub fn interleaved_in_range<'a>( + pub fn disjoint_in_range<'a, D>( &self, range: Range, cx: &AppContext, - ) -> Vec> { - // TODO: Make sure pending selection is handled correctly here - // if it is interleaved properly, we can sue resolve_multiple - // to improve performance + ) -> Vec> + where + D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, + { let buffer = self.buffer(cx); let start_ix = match self .disjoint @@ -146,36 +147,16 @@ impl SelectionsCollection { Ok(ix) => ix + 1, Err(ix) => ix, }; - - fn point_selection( - selection: &Selection, - buffer: &MultiBufferSnapshot, - ) -> Selection { - let start = crate::ToPoint::to_point(&selection.start, &buffer); - let end = crate::ToPoint::to_point(&selection.end, &buffer); - Selection { - id: selection.id, - start, - end, - reversed: selection.reversed, - goal: selection.goal, - } - } - - self.disjoint[start_ix..end_ix] - .iter() - .chain(self.pending.as_ref().map(|pending| &pending.selection)) - .map(|s| point_selection(s, &buffer)) - .collect() + resolve_multiple(&self.disjoint[start_ix..end_ix], &buffer).collect() } - pub fn display_interleaved( + pub fn all_display( &mut self, cx: &mut MutableAppContext, ) -> (DisplaySnapshot, Vec>) { let display_map = self.display_map(cx); let selections = self - .interleaved::(cx) + .all::(cx) .into_iter() .map(|selection| selection.map(|point| point.to_display_point(&display_map))) .collect(); @@ -216,14 +197,14 @@ impl SelectionsCollection { &self, cx: &AppContext, ) -> Selection { - self.interleaved(cx).first().unwrap().clone() + self.all(cx).first().unwrap().clone() } pub fn last>( &self, cx: &AppContext, ) -> Selection { - self.interleaved(cx).last().unwrap().clone() + self.all(cx).last().unwrap().clone() } #[cfg(any(test, feature = "test-support"))] @@ -231,7 +212,7 @@ impl SelectionsCollection { &self, cx: &AppContext, ) -> Vec> { - self.interleaved::(cx) + self.all::(cx) .iter() .map(|s| { if s.reversed { @@ -259,6 +240,34 @@ impl SelectionsCollection { .collect() } + pub fn build_columnar_selection( + &mut self, + display_map: &DisplaySnapshot, + row: u32, + columns: &Range, + reversed: bool, + ) -> Option> { + let is_empty = columns.start == columns.end; + let line_len = display_map.line_len(row); + if columns.start < line_len || (is_empty && columns.start == line_len) { + let start = DisplayPoint::new(row, columns.start); + let end = DisplayPoint::new(row, cmp::min(columns.end, line_len)); + + Some(Selection { + id: post_inc(&mut self.next_selection_id), + start: start.to_point(display_map), + end: end.to_point(display_map), + reversed, + goal: SelectionGoal::ColumnRange { + start: columns.start, + end: columns.end, + }, + }) + } else { + None + } + } + pub(crate) fn change_with( &mut self, cx: &mut MutableAppContext, @@ -288,7 +297,7 @@ impl<'a> MutableSelectionsCollection<'a> { self.collection.display_map(self.cx) } - fn buffer(&mut self) -> MultiBufferSnapshot { + fn buffer(&self) -> Ref { self.collection.buffer(self.cx) } @@ -370,7 +379,7 @@ impl<'a> MutableSelectionsCollection<'a> { where T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub + std::marker::Copy, { - let mut selections = self.interleaved(self.cx); + let mut selections = self.all(self.cx); let mut start = range.start.to_offset(&self.buffer()); let mut end = range.end.to_offset(&self.buffer()); let reversed = if start > end { @@ -527,7 +536,7 @@ impl<'a> MutableSelectionsCollection<'a> { ) { let display_map = self.display_map(); let selections = self - .interleaved::(self.cx) + .all::(self.cx) .into_iter() .map(|selection| { let mut selection = selection.map(|point| point.to_display_point(&display_map)); @@ -595,18 +604,17 @@ impl<'a> MutableSelectionsCollection<'a> { /// was no longer present. The keys of the map are selection ids. The values are /// the id of the new excerpt where the head of the selection has been moved. pub fn refresh(&mut self) -> HashMap { - // TODO: Pull disjoint constraint out of update_selections so we don't have to - // store the pending_selection here. - let buffer = self.buffer(); - let mut pending = self.collection.pending.take(); let mut selections_with_lost_position = HashMap::default(); - let anchors_with_status = buffer.refresh_anchors( - self.disjoint + let anchors_with_status = { + let buffer = self.buffer(); + let disjoint_anchors = self + .disjoint .iter() - .flat_map(|selection| [&selection.start, &selection.end]), - ); + .flat_map(|selection| [&selection.start, &selection.end]); + buffer.refresh_anchors(disjoint_anchors) + }; let adjusted_disjoint: Vec<_> = anchors_with_status .chunks(2) .map(|selection_anchors| { @@ -634,10 +642,13 @@ impl<'a> MutableSelectionsCollection<'a> { .collect(); if !adjusted_disjoint.is_empty() { - self.select::(resolve_multiple(adjusted_disjoint.iter(), &buffer).collect()); + let resolved_selections = + resolve_multiple(adjusted_disjoint.iter(), &self.buffer()).collect(); + self.select::(resolved_selections); } if let Some(pending) = pending.as_mut() { + let buffer = self.buffer(); let anchors = buffer.refresh_anchors([&pending.selection.start, &pending.selection.end]); let (_, start, kept_start) = anchors[0].clone(); diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 36fec6fbfc..2a391676fa 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -130,7 +130,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - let (map, old_selections) = editor.selections.display_interleaved(cx); + let (map, old_selections) = editor.selections.all_display(cx); let selection_start_rows: HashSet = old_selections .into_iter() .map(|selection| selection.start.row()) @@ -162,7 +162,7 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex vim.switch_mode(Mode::Insert, cx); vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { - let (map, old_selections) = editor.selections.display_interleaved(cx); + let (map, old_selections) = editor.selections.all_display(cx); let selection_end_rows: HashSet = old_selections .into_iter() .map(|selection| selection.end.row()) diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/vim_test_context.rs index f68e3f26d9..4122c46059 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/vim_test_context.rs @@ -200,7 +200,7 @@ impl<'a> VimTestContext<'a> { self.editor.read_with(self.cx, |editor, cx| { let (empty_selections, non_empty_selections): (Vec<_>, Vec<_>) = editor .selections - .interleaved::(cx) + .all::(cx) .into_iter() .partition_map(|selection| { if selection.is_empty() {