From c8003c0697df7cc2352f912a0df27b2757fffdbf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 30 Oct 2024 15:21:51 +0100 Subject: [PATCH] Take a mutable context when resolving selections (#19948) This is a behavior-preserving change, but lays the groundwork for expanding selections when the cursor lands inside of a "replace" block. Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 67 ++--- crates/assistant/src/inline_assistant.rs | 31 ++- crates/collab/src/tests/following_tests.rs | 7 +- crates/diagnostics/src/items.rs | 9 +- crates/editor/src/editor.rs | 19 +- crates/editor/src/element.rs | 248 +++++++++--------- crates/editor/src/linked_editing_ranges.rs | 2 +- crates/editor/src/selections_collection.rs | 37 ++- crates/go_to_line/src/cursor_position.rs | 50 ++-- crates/go_to_line/src/go_to_line.rs | 4 +- crates/language_tools/src/syntax_tree_view.rs | 14 +- .../src/markdown_preview_view.rs | 4 +- crates/outline/src/outline.rs | 8 +- crates/outline_panel/src/outline_panel.rs | 8 +- crates/repl/src/repl_editor.rs | 20 +- crates/vim/src/state.rs | 2 +- crates/vim/src/vim.rs | 15 +- 17 files changed, 288 insertions(+), 257 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index f0b5a5d442..19d92bbc02 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -1677,8 +1677,10 @@ impl ContextEditor { }); } - fn cursors(&self, cx: &AppContext) -> Vec { - let selections = self.editor.read(cx).selections.all::(cx); + fn cursors(&self, cx: &mut WindowContext) -> Vec { + let selections = self + .editor + .update(cx, |editor, cx| editor.selections.all::(cx)); selections .into_iter() .map(|selection| selection.head()) @@ -2385,7 +2387,9 @@ impl ContextEditor { } fn update_active_patch(&mut self, cx: &mut ViewContext) { - let newest_cursor = self.editor.read(cx).selections.newest::(cx).head(); + let newest_cursor = self.editor.update(cx, |editor, cx| { + editor.selections.newest::(cx).head() + }); let context = self.context.read(cx); let new_patch = context.patch_containing(newest_cursor, cx).cloned(); @@ -2792,39 +2796,40 @@ impl ContextEditor { ) -> Option<(String, bool)> { const CODE_FENCE_DELIMITER: &'static str = "```"; - let context_editor = context_editor_view.read(cx).editor.read(cx); + let context_editor = context_editor_view.read(cx).editor.clone(); + context_editor.update(cx, |context_editor, cx| { + if context_editor.selections.newest::(cx).is_empty() { + let snapshot = context_editor.buffer().read(cx).snapshot(cx); + let (_, _, snapshot) = snapshot.as_singleton()?; - if context_editor.selections.newest::(cx).is_empty() { - let snapshot = context_editor.buffer().read(cx).snapshot(cx); - let (_, _, snapshot) = snapshot.as_singleton()?; + let head = context_editor.selections.newest::(cx).head(); + let offset = snapshot.point_to_offset(head); - let head = context_editor.selections.newest::(cx).head(); - let offset = snapshot.point_to_offset(head); + let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?; + let mut text = snapshot + .text_for_range(surrounding_code_block_range) + .collect::(); - let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?; - let mut text = snapshot - .text_for_range(surrounding_code_block_range) - .collect::(); + // If there is no newline trailing the closing three-backticks, then + // tree-sitter-md extends the range of the content node to include + // the backticks. + if text.ends_with(CODE_FENCE_DELIMITER) { + text.drain((text.len() - CODE_FENCE_DELIMITER.len())..); + } - // If there is no newline trailing the closing three-backticks, then - // tree-sitter-md extends the range of the content node to include - // the backticks. - if text.ends_with(CODE_FENCE_DELIMITER) { - text.drain((text.len() - CODE_FENCE_DELIMITER.len())..); + (!text.is_empty()).then_some((text, true)) + } else { + let anchor = context_editor.selections.newest_anchor(); + let text = context_editor + .buffer() + .read(cx) + .read(cx) + .text_for_range(anchor.range()) + .collect::(); + + (!text.is_empty()).then_some((text, false)) } - - (!text.is_empty()).then_some((text, true)) - } else { - let anchor = context_editor.selections.newest_anchor(); - let text = context_editor - .buffer() - .read(cx) - .read(cx) - .text_for_range(anchor.range()) - .collect::(); - - (!text.is_empty()).then_some((text, false)) - } + }) } fn insert_selection( diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 4c79662cf1..fdf00c8b04 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -189,11 +189,16 @@ impl InlineAssistant { initial_prompt: Option, cx: &mut WindowContext, ) { - let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx); + let (snapshot, initial_selections) = editor.update(cx, |editor, cx| { + ( + editor.buffer().read(cx).snapshot(cx), + editor.selections.all::(cx), + ) + }); let mut selections = Vec::>::new(); let mut newest_selection = None; - for mut selection in editor.read(cx).selections.all::(cx) { + for mut selection in initial_selections { if selection.end > selection.start { selection.start.column = 0; // If the selection ends at the start of the line, we don't want to include it. @@ -566,10 +571,13 @@ impl InlineAssistant { return; }; - let editor = editor.read(cx); - if editor.selections.count() == 1 { - let selection = editor.selections.newest::(cx); - let buffer = editor.buffer().read(cx).snapshot(cx); + if editor.read(cx).selections.count() == 1 { + let (selection, buffer) = editor.update(cx, |editor, cx| { + ( + editor.selections.newest::(cx), + editor.buffer().read(cx).snapshot(cx), + ) + }); for assist_id in &editor_assists.assist_ids { let assist = &self.assists[assist_id]; let assist_range = assist.range.to_offset(&buffer); @@ -594,10 +602,13 @@ impl InlineAssistant { return; }; - let editor = editor.read(cx); - if editor.selections.count() == 1 { - let selection = editor.selections.newest::(cx); - let buffer = editor.buffer().read(cx).snapshot(cx); + if editor.read(cx).selections.count() == 1 { + let (selection, buffer) = editor.update(cx, |editor, cx| { + ( + editor.selections.newest::(cx), + editor.buffer().read(cx).snapshot(cx), + ) + }); let mut closest_assist_fallback = None; for assist_id in &editor_assists.assist_ids { let assist = &self.assists[assist_id]; diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index 1367bf49c0..d708194f58 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -1957,9 +1957,10 @@ async fn test_following_to_channel_notes_without_a_shared_project( }); channel_notes_1_b.update(cx_b, |notes, cx| { assert_eq!(notes.channel(cx).unwrap().name, "channel-1"); - let editor = notes.editor.read(cx); - assert_eq!(editor.text(cx), "Hello from A."); - assert_eq!(editor.selections.ranges::(cx), &[3..4]); + notes.editor.update(cx, |editor, cx| { + assert_eq!(editor.text(cx), "Hello from A."); + assert_eq!(editor.selections.ranges::(cx), &[3..4]); + }) }); // Client A opens the notes for channel 2. diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 72a4ac9bcf..2c580c44de 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -136,11 +136,12 @@ impl DiagnosticIndicator { } fn update(&mut self, editor: View, cx: &mut ViewContext) { - let editor = editor.read(cx); - let buffer = editor.buffer().read(cx); - let cursor_position = editor.selections.newest::(cx).head(); + let (buffer, cursor_position) = editor.update(cx, |editor, cx| { + let buffer = editor.buffer().read(cx).snapshot(cx); + let cursor_position = editor.selections.newest::(cx).head(); + (buffer, cursor_position) + }); let new_diagnostic = buffer - .snapshot(cx) .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false) .filter(|entry| !entry.range.is_empty()) .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 50d367d730..2e88df6b92 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -9629,8 +9629,8 @@ impl Editor { let Some(provider) = self.semantics_provider.clone() else { return Task::ready(Ok(Navigated::No)); }; - let buffer = self.buffer.read(cx); let head = self.selections.newest::(cx).head(); + let buffer = self.buffer.read(cx); let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) { text_anchor } else { @@ -9937,8 +9937,8 @@ impl Editor { _: &FindAllReferences, cx: &mut ViewContext, ) -> Option>> { - let multi_buffer = self.buffer.read(cx); let selection = self.selections.newest::(cx); + let multi_buffer = self.buffer.read(cx); let head = selection.head(); let multi_buffer_snapshot = multi_buffer.snapshot(cx); @@ -10345,8 +10345,9 @@ impl Editor { self.show_local_selections = true; if moving_cursor { - let rename_editor = rename.editor.read(cx); - let cursor_in_rename_editor = rename_editor.selections.newest::(cx).head(); + let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| { + editor.selections.newest::(cx).head() + }); // Update the selection to match the position of the selection inside // the rename editor. @@ -11592,9 +11593,9 @@ impl Editor { } pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext) { + let selection = self.selections.newest::(cx).start.row + 1; if let Some(file) = self.target_file(cx) { if let Some(path) = file.path().to_str() { - let selection = self.selections.newest::(cx).start.row + 1; cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}"))); } } @@ -12370,9 +12371,10 @@ impl Editor { return; }; + let selections = self.selections.all::(cx); let buffer = self.buffer.read(cx); let mut new_selections_by_buffer = HashMap::default(); - for selection in self.selections.all::(cx) { + for selection in selections { for (buffer, range, _) in buffer.range_to_buffer_ranges(selection.start..selection.end, cx) { @@ -12417,6 +12419,7 @@ impl Editor { } fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext) { + let selections = self.selections.all::(cx); let buffer = self.buffer.read(cx); if buffer.is_singleton() { cx.propagate(); @@ -12429,7 +12432,7 @@ impl Editor { }; let mut new_selections_by_buffer = HashMap::default(); - for selection in self.selections.all::(cx) { + for selection in selections { for (mut buffer_handle, mut range, _) in buffer.range_to_buffer_ranges(selection.range(), cx) { @@ -12545,7 +12548,7 @@ impl Editor { fn selection_replacement_ranges( &self, range: Range, - cx: &AppContext, + cx: &mut AppContext, ) -> Vec> { let selections = self.selections.all::(cx); let newest_selection = selections diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 489fe4c5ed..ac4d5d2340 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -824,129 +824,131 @@ impl EditorElement { let mut selections: Vec<(PlayerColor, Vec)> = Vec::new(); let mut active_rows = BTreeMap::new(); let mut newest_selection_head = None; - let editor = self.editor.read(cx); + self.editor.update(cx, |editor, cx| { + if editor.show_local_selections { + let mut local_selections: Vec> = editor + .selections + .disjoint_in_range(start_anchor..end_anchor, cx); + local_selections.extend(editor.selections.pending(cx)); + let mut layouts = Vec::new(); + let newest = editor.selections.newest(cx); + for selection in local_selections.drain(..) { + let is_empty = selection.start == selection.end; + let is_newest = selection == newest; - if editor.show_local_selections { - let mut local_selections: Vec> = editor - .selections - .disjoint_in_range(start_anchor..end_anchor, cx); - local_selections.extend(editor.selections.pending(cx)); - let mut layouts = Vec::new(); - let newest = editor.selections.newest(cx); - for selection in local_selections.drain(..) { - let is_empty = selection.start == selection.end; - let is_newest = selection == newest; + let layout = SelectionLayout::new( + selection, + editor.selections.line_mode, + editor.cursor_shape, + &snapshot.display_snapshot, + is_newest, + editor.leader_peer_id.is_none(), + None, + ); + if is_newest { + newest_selection_head = Some(layout.head); + } - let layout = SelectionLayout::new( - selection, - editor.selections.line_mode, - editor.cursor_shape, - &snapshot.display_snapshot, - is_newest, - editor.leader_peer_id.is_none(), - None, - ); - if is_newest { - newest_selection_head = Some(layout.head); + for row in cmp::max(layout.active_rows.start.0, start_row.0) + ..=cmp::min(layout.active_rows.end.0, end_row.0) + { + let contains_non_empty_selection = + active_rows.entry(DisplayRow(row)).or_insert(!is_empty); + *contains_non_empty_selection |= !is_empty; + } + layouts.push(layout); } - for row in cmp::max(layout.active_rows.start.0, start_row.0) - ..=cmp::min(layout.active_rows.end.0, end_row.0) - { - let contains_non_empty_selection = - active_rows.entry(DisplayRow(row)).or_insert(!is_empty); - *contains_non_empty_selection |= !is_empty; - } - layouts.push(layout); + let player = if editor.read_only(cx) { + cx.theme().players().read_only() + } else { + self.style.local_player + }; + + selections.push((player, layouts)); } - let player = if editor.read_only(cx) { - cx.theme().players().read_only() - } else { - self.style.local_player - }; - - selections.push((player, layouts)); - } - - if let Some(collaboration_hub) = &editor.collaboration_hub { - // When following someone, render the local selections in their color. - if let Some(leader_id) = editor.leader_peer_id { - if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) { - if let Some(participant_index) = collaboration_hub - .user_participant_indices(cx) - .get(&collaborator.user_id) + if let Some(collaboration_hub) = &editor.collaboration_hub { + // When following someone, render the local selections in their color. + if let Some(leader_id) = editor.leader_peer_id { + if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) { - if let Some((local_selection_style, _)) = selections.first_mut() { - *local_selection_style = cx - .theme() - .players() - .color_for_participant(participant_index.0); + if let Some(participant_index) = collaboration_hub + .user_participant_indices(cx) + .get(&collaborator.user_id) + { + if let Some((local_selection_style, _)) = selections.first_mut() { + *local_selection_style = cx + .theme() + .players() + .color_for_participant(participant_index.0); + } } } } - } - let mut remote_selections = HashMap::default(); - for selection in snapshot.remote_selections_in_range( - &(start_anchor..end_anchor), - collaboration_hub.as_ref(), - cx, - ) { - let selection_style = Self::get_participant_color(selection.participant_index, cx); + let mut remote_selections = HashMap::default(); + for selection in snapshot.remote_selections_in_range( + &(start_anchor..end_anchor), + collaboration_hub.as_ref(), + cx, + ) { + let selection_style = + Self::get_participant_color(selection.participant_index, cx); - // Don't re-render the leader's selections, since the local selections - // match theirs. - if Some(selection.peer_id) == editor.leader_peer_id { - continue; + // Don't re-render the leader's selections, since the local selections + // match theirs. + if Some(selection.peer_id) == editor.leader_peer_id { + continue; + } + let key = HoveredCursor { + replica_id: selection.replica_id, + selection_id: selection.selection.id, + }; + + let is_shown = + editor.show_cursor_names || editor.hovered_cursors.contains_key(&key); + + remote_selections + .entry(selection.replica_id) + .or_insert((selection_style, Vec::new())) + .1 + .push(SelectionLayout::new( + selection.selection, + selection.line_mode, + selection.cursor_shape, + &snapshot.display_snapshot, + false, + false, + if is_shown { selection.user_name } else { None }, + )); } - let key = HoveredCursor { - replica_id: selection.replica_id, - selection_id: selection.selection.id, + + selections.extend(remote_selections.into_values()); + } else if !editor.is_focused(cx) && editor.show_cursor_when_unfocused { + let player = if editor.read_only(cx) { + cx.theme().players().read_only() + } else { + self.style.local_player }; - - let is_shown = - editor.show_cursor_names || editor.hovered_cursors.contains_key(&key); - - remote_selections - .entry(selection.replica_id) - .or_insert((selection_style, Vec::new())) - .1 - .push(SelectionLayout::new( - selection.selection, - selection.line_mode, - selection.cursor_shape, - &snapshot.display_snapshot, - false, - false, - if is_shown { selection.user_name } else { None }, - )); + let layouts = snapshot + .buffer_snapshot + .selections_in_range(&(start_anchor..end_anchor), true) + .map(move |(_, line_mode, cursor_shape, selection)| { + SelectionLayout::new( + selection, + line_mode, + cursor_shape, + &snapshot.display_snapshot, + false, + false, + None, + ) + }) + .collect::>(); + selections.push((player, layouts)); } - - selections.extend(remote_selections.into_values()); - } else if !editor.is_focused(cx) && editor.show_cursor_when_unfocused { - let player = if editor.read_only(cx) { - cx.theme().players().read_only() - } else { - self.style.local_player - }; - let layouts = snapshot - .buffer_snapshot - .selections_in_range(&(start_anchor..end_anchor), true) - .map(move |(_, line_mode, cursor_shape, selection)| { - SelectionLayout::new( - selection, - line_mode, - cursor_shape, - &snapshot.display_snapshot, - false, - false, - None, - ) - }) - .collect::>(); - selections.push((player, layouts)); - } + }); (selections, active_rows, newest_selection_head) } @@ -1848,23 +1850,25 @@ impl EditorElement { return Vec::new(); } - let editor = self.editor.read(cx); - let newest_selection_head = newest_selection_head.unwrap_or_else(|| { - let newest = editor.selections.newest::(cx); - SelectionLayout::new( - newest, - editor.selections.line_mode, - editor.cursor_shape, - &snapshot.display_snapshot, - true, - true, - None, - ) - .head + let (newest_selection_head, is_relative) = self.editor.update(cx, |editor, cx| { + let newest_selection_head = newest_selection_head.unwrap_or_else(|| { + let newest = editor.selections.newest::(cx); + SelectionLayout::new( + newest, + editor.selections.line_mode, + editor.cursor_shape, + &snapshot.display_snapshot, + true, + true, + None, + ) + .head + }); + let is_relative = editor.should_use_relative_line_numbers(cx); + (newest_selection_head, is_relative) }); let font_size = self.style.text.font_size.to_pixels(cx.rem_size()); - let is_relative = editor.should_use_relative_line_numbers(cx); let relative_to = if is_relative { Some(newest_selection_head.row()) } else { diff --git a/crates/editor/src/linked_editing_ranges.rs b/crates/editor/src/linked_editing_ranges.rs index d3e4002173..853f014ddb 100644 --- a/crates/editor/src/linked_editing_ranges.rs +++ b/crates/editor/src/linked_editing_ranges.rs @@ -41,9 +41,9 @@ pub(super) fn refresh_linked_ranges(this: &mut Editor, cx: &mut ViewContext(cx); let buffer = this.buffer.read(cx); let mut applicable_selections = vec![]; - let selections = this.selections.all::(cx); let snapshot = buffer.snapshot(cx); for selection in selections { let cursor_position = selection.head(); diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index c85e60fdaa..8e1c12b8cd 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -8,14 +8,14 @@ use std::{ use collections::HashMap; use gpui::{AppContext, Model, Pixels}; use itertools::Itertools; -use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint}; +use language::{Bias, Point, Selection, SelectionGoal, TextDimension}; use util::post_inc; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, movement::TextLayoutDetails, Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, - ToOffset, + ToOffset, ToPoint, }; #[derive(Debug, Clone)] @@ -96,7 +96,7 @@ impl SelectionsCollection { pub fn pending>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Option> { self.pending_anchor() .as_ref() @@ -107,7 +107,7 @@ impl SelectionsCollection { self.pending.as_ref().map(|pending| pending.mode.clone()) } - pub fn all<'a, D>(&self, cx: &AppContext) -> Vec> + pub fn all<'a, D>(&self, cx: &mut AppContext) -> Vec> where D: 'a + TextDimension + Ord + Sub, { @@ -194,7 +194,7 @@ impl SelectionsCollection { pub fn disjoint_in_range<'a, D>( &self, range: Range, - cx: &AppContext, + cx: &mut AppContext, ) -> Vec> where D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, @@ -239,9 +239,10 @@ impl SelectionsCollection { pub fn newest>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Selection { - resolve(self.newest_anchor(), &self.buffer(cx)) + let buffer = self.buffer(cx); + self.newest_anchor().map(|p| p.summary::(&buffer)) } pub fn newest_display(&self, cx: &mut AppContext) -> Selection { @@ -262,9 +263,10 @@ impl SelectionsCollection { pub fn oldest>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Selection { - resolve(self.oldest_anchor(), &self.buffer(cx)) + let buffer = self.buffer(cx); + self.oldest_anchor().map(|p| p.summary::(&buffer)) } pub fn first_anchor(&self) -> Selection { @@ -276,14 +278,14 @@ impl SelectionsCollection { pub fn first>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Selection { self.all(cx).first().unwrap().clone() } pub fn last>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Selection { self.all(cx).last().unwrap().clone() } @@ -298,7 +300,7 @@ impl SelectionsCollection { #[cfg(any(test, feature = "test-support"))] pub fn ranges + std::fmt::Debug>( &self, - cx: &AppContext, + cx: &mut AppContext, ) -> Vec> { self.all::(cx) .iter() @@ -475,7 +477,7 @@ impl<'a> MutableSelectionsCollection<'a> { where T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub + std::marker::Copy, { - let mut selections = self.all(self.cx); + let mut selections = self.collection.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 { @@ -649,6 +651,7 @@ impl<'a> MutableSelectionsCollection<'a> { let mut changed = false; let display_map = self.display_map(); let selections = self + .collection .all::(self.cx) .into_iter() .map(|selection| { @@ -676,6 +679,7 @@ impl<'a> MutableSelectionsCollection<'a> { let mut changed = false; let snapshot = self.buffer().clone(); let selections = self + .collection .all::(self.cx) .into_iter() .map(|selection| { @@ -869,10 +873,3 @@ where goal: s.goal, }) } - -fn resolve>( - selection: &Selection, - buffer: &MultiBufferSnapshot, -) -> Selection { - selection.map(|p| p.summary::(buffer)) -} diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index 63e0f2b079..80be035770 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -37,34 +37,34 @@ impl CursorPosition { } fn update_position(&mut self, editor: View, cx: &mut ViewContext) { - let editor = editor.read(cx); - let buffer = editor.buffer().read(cx).snapshot(cx); + editor.update(cx, |editor, cx| { + let buffer = editor.buffer().read(cx).snapshot(cx); - self.selected_count = Default::default(); - self.selected_count.selections = editor.selections.count(); - let mut last_selection: Option> = None; - for selection in editor.selections.all::(cx) { - self.selected_count.characters += buffer - .text_for_range(selection.start..selection.end) - .map(|t| t.chars().count()) - .sum::(); - if last_selection - .as_ref() - .map_or(true, |last_selection| selection.id > last_selection.id) - { - last_selection = Some(selection); - } - } - for selection in editor.selections.all::(cx) { - if selection.end != selection.start { - self.selected_count.lines += (selection.end.row - selection.start.row) as usize; - if selection.end.column != 0 { - self.selected_count.lines += 1; + self.selected_count = Default::default(); + self.selected_count.selections = editor.selections.count(); + let mut last_selection: Option> = None; + for selection in editor.selections.all::(cx) { + self.selected_count.characters += buffer + .text_for_range(selection.start..selection.end) + .map(|t| t.chars().count()) + .sum::(); + if last_selection + .as_ref() + .map_or(true, |last_selection| selection.id > last_selection.id) + { + last_selection = Some(selection); } } - } - self.position = last_selection.map(|s| s.head().to_point(&buffer)); - + for selection in editor.selections.all::(cx) { + if selection.end != selection.start { + self.selected_count.lines += (selection.end.row - selection.start.row) as usize; + if selection.end.column != 0 { + self.selected_count.lines += 1; + } + } + } + self.position = last_selection.map(|s| s.head().to_point(&buffer)); + }); cx.notify(); } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 0e9482b759..805c1f0d52 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -56,8 +56,8 @@ impl GoToLine { } pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { - let editor = active_editor.read(cx); - let cursor = editor.selections.last::(cx).head(); + let cursor = + active_editor.update(cx, |editor, cx| editor.selections.last::(cx).head()); let line = cursor.row + 1; let column = cursor.column + 1; diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index e2c4903e19..b9c960c9c3 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -128,12 +128,14 @@ impl SyntaxTreeView { fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext) -> Option<()> { // Find which excerpt the cursor is in, and the position within that excerpted buffer. let editor_state = self.editor.as_mut()?; - let editor = &editor_state.editor.read(cx); - let selection_range = editor.selections.last::(cx).range(); - let multibuffer = editor.buffer().read(cx); - let (buffer, range, excerpt_id) = multibuffer - .range_to_buffer_ranges(selection_range, cx) - .pop()?; + let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| { + let selection_range = editor.selections.last::(cx).range(); + editor + .buffer() + .read(cx) + .range_to_buffer_ranges(selection_range, cx) + .pop() + })?; // If the cursor has moved into a different excerpt, retrieve a new syntax layer // from that buffer. diff --git a/crates/markdown_preview/src/markdown_preview_view.rs b/crates/markdown_preview/src/markdown_preview_view.rs index 81145afa3f..7e8cc42dcf 100644 --- a/crates/markdown_preview/src/markdown_preview_view.rs +++ b/crates/markdown_preview/src/markdown_preview_view.rs @@ -301,8 +301,8 @@ impl MarkdownPreviewView { this.parse_markdown_from_active_editor(true, cx); } EditorEvent::SelectionsChanged { .. } => { - let editor = editor.read(cx); - let selection_range = editor.selections.last::(cx).range(); + let selection_range = + editor.update(cx, |editor, cx| editor.selections.last::(cx).range()); this.selected_block = this.get_block_index_under_cursor(selection_range); this.list_state.scroll_to_reveal_item(this.selected_block); cx.notify(); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 1d82d06ad8..18965fe048 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -194,9 +194,11 @@ impl PickerDelegate for OutlineViewDelegate { }) .collect(); - let editor = self.active_editor.read(cx); - let cursor_offset = editor.selections.newest::(cx).head(); - let buffer = editor.buffer().read(cx).snapshot(cx); + let (buffer, cursor_offset) = self.active_editor.update(cx, |editor, cx| { + let buffer = editor.buffer().read(cx).snapshot(cx); + let cursor_offset = editor.selections.newest::(cx).head(); + (buffer, cursor_offset) + }); selected_index = self .outline .items diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 83eb7347ce..10ca2b0712 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -2410,11 +2410,9 @@ impl OutlinePanel { editor: &View, cx: &mut ViewContext, ) -> Option { - let selection = editor - .read(cx) - .selections - .newest::(cx) - .head(); + let selection = editor.update(cx, |editor, cx| { + editor.selections.newest::(cx).head() + }); let editor_snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)); let multi_buffer = editor.read(cx).buffer(); let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); diff --git a/crates/repl/src/repl_editor.rs b/crates/repl/src/repl_editor.rs index 6c86257f30..e07958d0e4 100644 --- a/crates/repl/src/repl_editor.rs +++ b/crates/repl/src/repl_editor.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use anyhow::{Context, Result}; use editor::Editor; -use gpui::{prelude::*, AppContext, Entity, View, WeakView, WindowContext}; +use gpui::{prelude::*, Entity, View, WeakView, WindowContext}; use language::{BufferSnapshot, Language, LanguageName, Point}; use crate::repl_store::ReplStore; @@ -103,7 +103,7 @@ pub enum SessionSupport { Unsupported, } -pub fn session(editor: WeakView, cx: &mut AppContext) -> SessionSupport { +pub fn session(editor: WeakView, cx: &mut WindowContext) -> SessionSupport { let store = ReplStore::global(cx); let entity_id = editor.entity_id(); @@ -311,17 +311,21 @@ fn language_supported(language: &Arc) -> bool { } } -fn get_language(editor: WeakView, cx: &mut AppContext) -> Option> { - let editor = editor.upgrade()?; - let selection = editor.read(cx).selections.newest::(cx); - let buffer = editor.read(cx).buffer().read(cx).snapshot(cx); - buffer.language_at(selection.head()).cloned() +fn get_language(editor: WeakView, cx: &mut WindowContext) -> Option> { + editor + .update(cx, |editor, cx| { + let selection = editor.selections.newest::(cx); + let buffer = editor.buffer().read(cx).snapshot(cx); + buffer.language_at(selection.head()).cloned() + }) + .ok() + .flatten() } #[cfg(test)] mod tests { use super::*; - use gpui::Context; + use gpui::{AppContext, Context}; use indoc::indoc; use language::{Buffer, Language, LanguageConfig, LanguageRegistry}; diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index b61cb405e1..f9dfcdd2c3 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -281,7 +281,7 @@ impl VimGlobals { &mut self, register: Option, editor: Option<&mut Editor>, - cx: &ViewContext, + cx: &mut ViewContext, ) -> Option { let Some(register) = register.filter(|reg| *reg != '"') else { let setting = VimSettings::get_global(cx).use_system_clipboard; diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 86a52aca25..6ec708d8b8 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -620,9 +620,11 @@ impl Vim { let Some(editor) = self.editor() else { return; }; + let newest_selection_empty = editor.update(cx, |editor, cx| { + editor.selections.newest::(cx).is_empty() + }); let editor = editor.read(cx); let editor_mode = editor.mode(); - let newest_selection_empty = editor.selections.newest::(cx).is_empty(); if editor_mode == EditorMode::Full && !newest_selection_empty @@ -717,11 +719,12 @@ impl Vim { globals.recorded_count = None; let selections = self.editor().map(|editor| { - let editor = editor.read(cx); - ( - editor.selections.oldest::(cx), - editor.selections.newest::(cx), - ) + editor.update(cx, |editor, cx| { + ( + editor.selections.oldest::(cx), + editor.selections.newest::(cx), + ) + }) }); if let Some((oldest, newest)) = selections {