@@ -3172,7 +3172,7 @@ impl Editor {
self.refresh_code_actions(window, cx);
self.refresh_document_highlights(cx);
self.refresh_selected_text_highlights(false, window, cx);
- refresh_matching_bracket_highlights(self, window, cx);
+ refresh_matching_bracket_highlights(self, cx);
self.update_visible_edit_prediction(window, cx);
self.edit_prediction_requires_modifier_in_indent_conflict = true;
linked_editing_ranges::refresh_linked_ranges(self, window, cx);
@@ -6607,26 +6607,32 @@ impl Editor {
&self.context_menu
}
- fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
- let newest_selection = self.selections.newest_anchor().clone();
- let newest_selection_adjusted = self.selections.newest_adjusted(cx);
- let buffer = self.buffer.read(cx);
- if newest_selection.head().diff_base_anchor.is_some() {
- return None;
- }
- let (start_buffer, start) =
- buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
- let (end_buffer, end) =
- buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
- if start_buffer != end_buffer {
- return None;
- }
-
+ fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
cx.background_executor()
.timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
.await;
+ let (start_buffer, start, _, end, newest_selection) = this
+ .update(cx, |this, cx| {
+ let newest_selection = this.selections.newest_anchor().clone();
+ if newest_selection.head().diff_base_anchor.is_some() {
+ return None;
+ }
+ let newest_selection_adjusted = this.selections.newest_adjusted(cx);
+ let buffer = this.buffer.read(cx);
+
+ let (start_buffer, start) =
+ buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
+ let (end_buffer, end) =
+ buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
+
+ Some((start_buffer, start, end_buffer, end, newest_selection))
+ })?
+ .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
+ .context(
+ "Expected selection to lie in a single buffer when refreshing code actions",
+ )?;
let (providers, tasks) = this.update_in(cx, |this, window, cx| {
let providers = this.code_action_providers.clone();
let tasks = this
@@ -6667,7 +6673,6 @@ impl Editor {
cx.notify();
})
}));
- None
}
fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -6917,19 +6922,24 @@ impl Editor {
if self.selections.count() != 1 || self.selections.line_mode() {
return None;
}
- let selection = self.selections.newest::<Point>(cx);
- if selection.is_empty() || selection.start.row != selection.end.row {
+ let selection = self.selections.newest_anchor();
+ let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
+ let selection_point_range = selection.start.to_point(&multi_buffer_snapshot)
+ ..selection.end.to_point(&multi_buffer_snapshot);
+ // If the selection spans multiple rows OR it is empty
+ if selection_point_range.start.row != selection_point_range.end.row
+ || selection_point_range.start.column == selection_point_range.end.column
+ {
return None;
}
- let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
- let selection_anchor_range = selection.range().to_anchors(&multi_buffer_snapshot);
+
let query = multi_buffer_snapshot
- .text_for_range(selection_anchor_range.clone())
+ .text_for_range(selection.range())
.collect::<String>();
if query.trim().is_empty() {
return None;
}
- Some((query, selection_anchor_range))
+ Some((query, selection.range()))
}
fn update_selection_occurrence_highlights(
@@ -20805,7 +20815,7 @@ impl Editor {
self.refresh_code_actions(window, cx);
self.refresh_selected_text_highlights(true, window, cx);
self.refresh_single_line_folds(window, cx);
- refresh_matching_bracket_highlights(self, window, cx);
+ refresh_matching_bracket_highlights(self, cx);
if self.has_active_edit_prediction() {
self.update_visible_edit_prediction(window, cx);
}
@@ -1,47 +1,46 @@
use crate::{Editor, RangeToAnchorExt};
-use gpui::{Context, HighlightStyle, Window};
+use gpui::{Context, HighlightStyle};
use language::CursorShape;
+use multi_buffer::ToOffset;
use theme::ActiveTheme;
enum MatchingBracketHighlight {}
-pub fn refresh_matching_bracket_highlights(
- editor: &mut Editor,
- window: &mut Window,
- cx: &mut Context<Editor>,
-) {
+pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut Context<Editor>) {
editor.clear_highlights::<MatchingBracketHighlight>(cx);
- let newest_selection = editor.selections.newest::<usize>(cx);
+ let buffer_snapshot = editor.buffer.read(cx).snapshot(cx);
+ let newest_selection = editor
+ .selections
+ .newest_anchor()
+ .map(|anchor| anchor.to_offset(&buffer_snapshot));
// Don't highlight brackets if the selection isn't empty
if !newest_selection.is_empty() {
return;
}
- let snapshot = editor.snapshot(window, cx);
let head = newest_selection.head();
- if head > snapshot.buffer_snapshot().len() {
+ if head > buffer_snapshot.len() {
log::error!("bug: cursor offset is out of range while refreshing bracket highlights");
return;
}
let mut tail = head;
if (editor.cursor_shape == CursorShape::Block || editor.cursor_shape == CursorShape::Hollow)
- && head < snapshot.buffer_snapshot().len()
+ && head < buffer_snapshot.len()
{
- if let Some(tail_ch) = snapshot.buffer_snapshot().chars_at(tail).next() {
+ if let Some(tail_ch) = buffer_snapshot.chars_at(tail).next() {
tail += tail_ch.len_utf8();
}
}
- if let Some((opening_range, closing_range)) = snapshot
- .buffer_snapshot()
- .innermost_enclosing_bracket_ranges(head..tail, None)
+ if let Some((opening_range, closing_range)) =
+ buffer_snapshot.innermost_enclosing_bracket_ranges(head..tail, None)
{
editor.highlight_text::<MatchingBracketHighlight>(
vec![
- opening_range.to_anchors(&snapshot.buffer_snapshot()),
- closing_range.to_anchors(&snapshot.buffer_snapshot()),
+ opening_range.to_anchors(&buffer_snapshot),
+ closing_range.to_anchors(&buffer_snapshot),
],
HighlightStyle {
background_color: Some(
@@ -184,6 +184,27 @@ impl SelectionsCollection {
selections
}
+ /// Returns all of the selections, adjusted to take into account the selection line_mode. Uses a provided snapshot to resolve selections.
+ pub fn all_adjusted_with_snapshot(
+ &self,
+ snapshot: &MultiBufferSnapshot,
+ ) -> Vec<Selection<Point>> {
+ let mut selections = self
+ .disjoint
+ .iter()
+ .chain(self.pending_anchor())
+ .map(|anchor| anchor.map(|anchor| anchor.to_point(&snapshot)))
+ .collect::<Vec<_>>();
+ if self.line_mode {
+ for selection in &mut selections {
+ let new_range = snapshot.expand_to_line(selection.range());
+ selection.start = new_range.start;
+ selection.end = new_range.end;
+ }
+ }
+ selections
+ }
+
/// Returns the newest selection, adjusted to take into account the selection line_mode
pub fn newest_adjusted(&self, cx: &mut App) -> Selection<Point> {
let mut selection = self.newest::<Point>(cx);
@@ -6385,6 +6385,17 @@ impl MultiBufferSnapshot {
debug_ranges.insert(key, text_ranges, format!("{value:?}").into())
});
}
+
+ // used by line_mode selections and tries to match vim behavior
+ pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
+ let new_start = MultiBufferPoint::new(range.start.row, 0);
+ let new_end = if range.end.column > 0 {
+ MultiBufferPoint::new(range.end.row, self.line_len(MultiBufferRow(range.end.row)))
+ } else {
+ range.end
+ };
+ new_start..new_end
+ }
}
#[cfg(any(test, feature = "test-support"))]