From d249618ee6f5e412558d0056bc2bf5bb5a9357de Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 25 Nov 2021 13:19:49 -0700 Subject: [PATCH] Improve range-based selection queries to only resolve the requested selections --- crates/buffer/src/anchor.rs | 20 ++++++++++++---- crates/buffer/src/selection.rs | 23 ++++++++++++++++++ crates/editor/src/element.rs | 12 ++++++---- crates/editor/src/lib.rs | 43 +++++++++++----------------------- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/crates/buffer/src/anchor.rs b/crates/buffer/src/anchor.rs index f2575ffa0497482e8505c526fb4b22e93adaccfd..c158cc36e11296214e4bee3a1d7caf47c53b7348 100644 --- a/crates/buffer/src/anchor.rs +++ b/crates/buffer/src/anchor.rs @@ -210,14 +210,14 @@ impl AnchorRangeMap { .zip(self.entries.iter().map(|e| &e.1)) } - pub fn intersecting_ranges<'a, D, T>( + pub fn intersecting_ranges<'a, D, I>( &'a self, - range: Range<(T, Bias)>, + range: Range<(I, Bias)>, content: impl Into> + 'a, ) -> impl Iterator, &'a T)> + 'a where D: 'a + TextDimension<'a>, - T: ToOffset, + I: ToOffset, { let content = content.into(); let range = content.anchor_at(range.start.0, range.start.1) @@ -229,10 +229,20 @@ impl AnchorRangeMap { version: self.version.clone(), }; let start_ix = self.entries.binary_search_by(|probe| { - probe_anchor.full_offset = probe.0.start; + probe_anchor.full_offset = probe.0.end; probe_anchor.cmp(&range.start, &content).unwrap() }); - std::iter::empty() + + match start_ix { + Ok(start_ix) | Err(start_ix) => content + .summaries_for_anchor_ranges( + self.version.clone(), + self.start_bias, + self.end_bias, + self.entries[start_ix..].iter().map(|e| &e.0), + ) + .zip(self.entries.iter().map(|e| &e.1)), + } } pub fn full_offset_ranges(&self) -> impl Iterator, T)> { diff --git a/crates/buffer/src/selection.rs b/crates/buffer/src/selection.rs index b90a6fa10579a63aaace2877cf722d40de53e9eb..bca0b7663feaf69d68482014513bf114f9bfbe3e 100644 --- a/crates/buffer/src/selection.rs +++ b/crates/buffer/src/selection.rs @@ -1,3 +1,5 @@ +use sum_tree::Bias; + use crate::rope::TextDimension; use super::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint}; @@ -117,6 +119,27 @@ impl SelectionSet { }) } + pub fn intersecting_selections<'a, D, I, C>( + &'a self, + range: Range<(I, Bias)>, + content: C, + ) -> impl 'a + Iterator> + where + D: 'a + TextDimension<'a>, + I: 'a + ToOffset, + C: 'a + Into>, + { + self.selections + .intersecting_ranges(range, content) + .map(|(range, state)| Selection { + id: state.id, + start: range.start, + end: range.end, + reversed: state.reversed, + goal: state.goal, + }) + } + pub fn oldest_selection<'a, D, C>(&'a self, content: C) -> Option> where D: 'a + TextDimension<'a>, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 570a64f5e245db049577090e6589f092645009f2..03b04c8c6bf391bbed0378b3223609589f18a754 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -748,11 +748,13 @@ impl Element for EditorElement { self.update_view(cx.app, |view, cx| { highlighted_row = view.highlighted_row(); for selection_set_id in view.active_selection_sets(cx).collect::>() { - let replica_selections = view.selections_in_range( - selection_set_id, - DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0), - cx, - ); + let replica_selections = view + .intersecting_selections( + selection_set_id, + DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0), + cx, + ) + .collect::>(); for selection in &replica_selections { if selection_set_id == view.selection_set_id { let is_empty = selection.start == selection.end; diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 80199a8e8ed762f02db6a90e3ccb2a082ddef551..0404c725083eb62ae1150ba360ca29ef01253b05 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -3017,23 +3017,15 @@ impl Editor { .map(|(set_id, _)| *set_id) } - pub fn selections_in_range<'a>( + pub fn intersecting_selections<'a>( &'a self, set_id: SelectionSetId, range: Range, cx: &'a mut MutableAppContext, - ) -> Vec> { + ) -> impl 'a + Iterator> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx); - let selections = self - .buffer - .read(cx) - .selection_set(set_id) - .unwrap() - .selections::(buffer) - .collect::>(); - let start = range.start.to_point(&display_map); - let start_index = self.selection_insertion_index(&selections, start); + let pending_selection = if set_id == self.selection_set_id { self.pending_selection.as_ref().and_then(|pending| { let selection_start = pending.selection.start.to_display_point(&display_map); @@ -3053,9 +3045,17 @@ impl Editor { } else { None }; + + let range = (range.start.to_offset(&display_map, Bias::Left), Bias::Left) + ..(range.end.to_offset(&display_map, Bias::Left), Bias::Right); + let selections = self + .buffer + .read(cx) + .selection_set(set_id) + .unwrap() + .intersecting_selections::(range, buffer); + selections - .into_iter() - .skip(start_index) .map(move |s| Selection { id: s.id, start: s.start.to_display_point(&display_map), @@ -3063,22 +3063,7 @@ impl Editor { reversed: s.reversed, goal: s.goal, }) - .take_while(move |r| r.start <= range.end || r.end <= range.end) .chain(pending_selection) - .collect() - } - - fn selection_insertion_index(&self, selections: &[Selection], start: Point) -> usize { - match selections.binary_search_by_key(&start, |probe| probe.start) { - Ok(index) => index, - Err(index) => { - if index > 0 && selections[index - 1].end > start { - index - 1 - } else { - index - } - } - } } pub fn selections<'a, D>(&self, cx: &'a AppContext) -> impl 'a + Iterator> @@ -5694,7 +5679,7 @@ mod tests { impl Editor { fn selection_ranges(&self, cx: &mut MutableAppContext) -> Vec> { - self.selections_in_range( + self.intersecting_selections( self.selection_set_id, DisplayPoint::zero()..self.max_point(cx), cx,