diff --git a/crates/buffer/src/anchor.rs b/crates/buffer/src/anchor.rs index ceee746c6a..7b765a948b 100644 --- a/crates/buffer/src/anchor.rs +++ b/crates/buffer/src/anchor.rs @@ -194,6 +194,66 @@ impl AnchorRangeMap { .iter() .map(|(range, value)| (range.start.0..range.end.0, value)) } + + pub fn min_by_key<'a, C, D, F, K>( + &self, + content: C, + mut extract_key: F, + ) -> Option<(Range, &T)> + where + C: Into>, + D: 'a + TextDimension<'a>, + F: FnMut(&T) -> K, + K: Ord, + { + let content = content.into(); + self.entries + .iter() + .min_by_key(|(_, value)| extract_key(value)) + .map(|(range, value)| (self.resolve_range(range, &content), value)) + } + + pub fn max_by_key<'a, C, D, F, K>( + &self, + content: C, + mut extract_key: F, + ) -> Option<(Range, &T)> + where + C: Into>, + D: 'a + TextDimension<'a>, + F: FnMut(&T) -> K, + K: Ord, + { + let content = content.into(); + self.entries + .iter() + .max_by_key(|(_, value)| extract_key(value)) + .map(|(range, value)| (self.resolve_range(range, &content), value)) + } + + fn resolve_range<'a, D>( + &self, + range: &Range<(FullOffset, Bias)>, + content: &Content<'a>, + ) -> Range + where + D: 'a + TextDimension<'a>, + { + let (start, start_bias) = range.start; + let mut anchor = Anchor { + full_offset: start, + bias: start_bias, + version: self.version.clone(), + }; + let start = content.summary_for_anchor(&anchor); + + let (end, end_bias) = range.end; + anchor.full_offset = end; + anchor.bias = end_bias; + let end = content.summary_for_anchor(&anchor); + + start..end + } } impl PartialEq for AnchorRangeMap { diff --git a/crates/buffer/src/selection.rs b/crates/buffer/src/selection.rs index dfd4522368..b90a6fa105 100644 --- a/crates/buffer/src/selection.rs +++ b/crates/buffer/src/selection.rs @@ -116,4 +116,36 @@ impl SelectionSet { goal: state.goal, }) } + + pub fn oldest_selection<'a, D, C>(&'a self, content: C) -> Option> + where + D: 'a + TextDimension<'a>, + C: 'a + Into>, + { + self.selections + .min_by_key(content, |selection| selection.id) + .map(|(range, state)| Selection { + id: state.id, + start: range.start, + end: range.end, + reversed: state.reversed, + goal: state.goal, + }) + } + + pub fn newest_selection<'a, D, C>(&'a self, content: C) -> Option> + where + D: 'a + TextDimension<'a>, + C: 'a + Into>, + { + self.selections + .max_by_key(content, |selection| selection.id) + .map(|(range, state)| Selection { + id: state.id, + start: range.start, + end: range.end, + reversed: state.reversed, + goal: state.goal, + }) + } } diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 7d4f05dba6..ffaf5e6cf7 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -707,16 +707,8 @@ impl Editor { self.update_selections(vec![pending_selection], true, cx); } } else { - let selections = self.selections::(cx); - let mut selection_count = 0; - let mut oldest_selection = selections - .min_by_key(|s| { - selection_count += 1; - s.id - }) - .unwrap() - .clone(); - if selection_count == 1 { + let mut oldest_selection = self.oldest_selection::(cx); + if self.selection_count(cx) == 1 { oldest_selection.start = oldest_selection.head().clone(); oldest_selection.end = oldest_selection.head().clone(); } @@ -2294,9 +2286,8 @@ impl Editor { ) -> impl 'a + Iterator> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx); - let selections = buffer - .selection_set(set_id) - .unwrap() + let selections = self + .selection_set(cx) .selections::(buffer) .collect::>(); let start = range.start.to_point(&display_map); @@ -2343,18 +2334,8 @@ impl Editor { D: 'a + TextDimension<'a> + Ord, { let buffer = self.buffer.read(cx); - let mut selections = buffer - .selection_set(self.selection_set_id) - .unwrap() - .selections::(buffer) - .peekable(); - let mut pending_selection = self.pending_selection.clone().map(|selection| Selection { - id: selection.id, - start: selection.start.summary::(buffer), - end: selection.end.summary::(buffer), - reversed: selection.reversed, - goal: selection.goal, - }); + let mut selections = self.selection_set(cx).selections::(buffer).peekable(); + let mut pending_selection = self.pending_selection(cx); iter::from_fn(move || { if let Some(pending) = pending_selection.as_mut() { while let Some(next_selection) = selections.peek() { @@ -2380,6 +2361,56 @@ impl Editor { }) } + fn pending_selection<'a, D>(&self, cx: &'a AppContext) -> Option> + where + D: 'a + TextDimension<'a>, + { + let buffer = self.buffer.read(cx); + self.pending_selection.as_ref().map(|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, cx: &'a AppContext) -> usize { + let mut selection_count = self.selection_set(cx).len(); + if self.pending_selection.is_some() { + selection_count += 1; + } + selection_count + } + + pub fn oldest_selection<'a, T>(&self, cx: &'a AppContext) -> Selection + where + T: 'a + TextDimension<'a>, + { + let buffer = self.buffer.read(cx); + self.selection_set(cx) + .oldest_selection(buffer) + .or_else(|| self.pending_selection(cx)) + .unwrap() + } + + pub fn newest_selection<'a, T>(&self, cx: &'a AppContext) -> Selection + where + T: 'a + TextDimension<'a>, + { + let buffer = self.buffer.read(cx); + self.pending_selection(cx) + .or_else(|| self.selection_set(cx).newest_selection(buffer)) + .unwrap() + } + + fn selection_set<'a>(&self, cx: &'a AppContext) -> &'a SelectionSet { + self.buffer + .read(cx) + .selection_set(self.selection_set_id) + .unwrap() + } + fn update_selections( &mut self, mut selections: Vec>, diff --git a/crates/workspace/src/items.rs b/crates/workspace/src/items.rs index 9a14379c7a..60e2e9d767 100644 --- a/crates/workspace/src/items.rs +++ b/crates/workspace/src/items.rs @@ -258,11 +258,7 @@ impl DiagnosticMessage { fn update(&mut self, editor: ViewHandle, cx: &mut ViewContext) { let editor = editor.read(cx); - let cursor_position = editor - .selections::(cx) - .max_by_key(|selection| selection.id) - .unwrap() - .head(); + let cursor_position = editor.newest_selection(cx).head(); let new_diagnostic = editor .buffer() .read(cx)