diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c4a9addc0b14a1ca3a25351e23ea810af66392b5..ae9bf372172965a97bb2bc009891d1f0c7617596 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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) -> 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.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) { @@ -6917,19 +6922,24 @@ impl Editor { if self.selections.count() != 1 || self.selections.line_mode() { return None; } - let selection = self.selections.newest::(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::(); 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); } diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index 0457e457c1006dea7fe14b908255f8614659e2ab..da0f847fe10b7365ac3f4686d183a5d83d17e73a 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -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, -) { +pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut Context) { editor.clear_highlights::(cx); - let newest_selection = editor.selections.newest::(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::( 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( diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index e9272e9e2055a98327ac726ac2d334e9c60b106a..5ab6d25eb9abcfa0846a176f83e2f2620245bb47 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -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> { + let mut selections = self + .disjoint + .iter() + .chain(self.pending_anchor()) + .map(|anchor| anchor.map(|anchor| anchor.to_point(&snapshot))) + .collect::>(); + 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 { let mut selection = self.newest::(cx); diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index ee95d1181d1f61fb95a9bbff5c7402aa2c9a1694..0d17e746701759aef4a0521f1fb0afcb578eb02a 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -113,7 +113,9 @@ impl CursorPosition { let mut last_selection = None::>; let snapshot = editor.buffer().read(cx).snapshot(cx); if snapshot.excerpts().count() > 0 { - for selection in editor.selections.all_adjusted(cx) { + for selection in + editor.selections.all_adjusted_with_snapshot(&snapshot) + { let selection_summary = snapshot .text_summary_for_range::( selection.start..selection.end, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 067c9829a3d5493e118ad362b88f3399b5436c57..31ffde7bb9494ab0835626cdce88e99a9ad86f3c 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -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) -> Range { + 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"))]