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
This commit is contained in:
Antonio Scandurra 2024-10-30 15:21:51 +01:00 committed by GitHub
parent 83e2889d63
commit c8003c0697
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 288 additions and 257 deletions

View file

@ -1677,8 +1677,10 @@ impl ContextEditor {
}); });
} }
fn cursors(&self, cx: &AppContext) -> Vec<usize> { fn cursors(&self, cx: &mut WindowContext) -> Vec<usize> {
let selections = self.editor.read(cx).selections.all::<usize>(cx); let selections = self
.editor
.update(cx, |editor, cx| editor.selections.all::<usize>(cx));
selections selections
.into_iter() .into_iter()
.map(|selection| selection.head()) .map(|selection| selection.head())
@ -2385,7 +2387,9 @@ impl ContextEditor {
} }
fn update_active_patch(&mut self, cx: &mut ViewContext<Self>) { fn update_active_patch(&mut self, cx: &mut ViewContext<Self>) {
let newest_cursor = self.editor.read(cx).selections.newest::<Point>(cx).head(); let newest_cursor = self.editor.update(cx, |editor, cx| {
editor.selections.newest::<Point>(cx).head()
});
let context = self.context.read(cx); let context = self.context.read(cx);
let new_patch = context.patch_containing(newest_cursor, cx).cloned(); let new_patch = context.patch_containing(newest_cursor, cx).cloned();
@ -2792,39 +2796,40 @@ impl ContextEditor {
) -> Option<(String, bool)> { ) -> Option<(String, bool)> {
const CODE_FENCE_DELIMITER: &'static str = "```"; 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::<Point>(cx).is_empty() {
let snapshot = context_editor.buffer().read(cx).snapshot(cx);
let (_, _, snapshot) = snapshot.as_singleton()?;
if context_editor.selections.newest::<Point>(cx).is_empty() { let head = context_editor.selections.newest::<Point>(cx).head();
let snapshot = context_editor.buffer().read(cx).snapshot(cx); let offset = snapshot.point_to_offset(head);
let (_, _, snapshot) = snapshot.as_singleton()?;
let head = context_editor.selections.newest::<Point>(cx).head(); let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?;
let offset = snapshot.point_to_offset(head); let mut text = snapshot
.text_for_range(surrounding_code_block_range)
.collect::<String>();
let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?; // If there is no newline trailing the closing three-backticks, then
let mut text = snapshot // tree-sitter-md extends the range of the content node to include
.text_for_range(surrounding_code_block_range) // the backticks.
.collect::<String>(); 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 (!text.is_empty()).then_some((text, true))
// tree-sitter-md extends the range of the content node to include } else {
// the backticks. let anchor = context_editor.selections.newest_anchor();
if text.ends_with(CODE_FENCE_DELIMITER) { let text = context_editor
text.drain((text.len() - CODE_FENCE_DELIMITER.len())..); .buffer()
.read(cx)
.read(cx)
.text_for_range(anchor.range())
.collect::<String>();
(!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::<String>();
(!text.is_empty()).then_some((text, false))
}
} }
fn insert_selection( fn insert_selection(

View file

@ -189,11 +189,16 @@ impl InlineAssistant {
initial_prompt: Option<String>, initial_prompt: Option<String>,
cx: &mut WindowContext, 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::<Point>(cx),
)
});
let mut selections = Vec::<Selection<Point>>::new(); let mut selections = Vec::<Selection<Point>>::new();
let mut newest_selection = None; let mut newest_selection = None;
for mut selection in editor.read(cx).selections.all::<Point>(cx) { for mut selection in initial_selections {
if selection.end > selection.start { if selection.end > selection.start {
selection.start.column = 0; selection.start.column = 0;
// If the selection ends at the start of the line, we don't want to include it. // If the selection ends at the start of the line, we don't want to include it.
@ -566,10 +571,13 @@ impl InlineAssistant {
return; return;
}; };
let editor = editor.read(cx); if editor.read(cx).selections.count() == 1 {
if editor.selections.count() == 1 { let (selection, buffer) = editor.update(cx, |editor, cx| {
let selection = editor.selections.newest::<usize>(cx); (
let buffer = editor.buffer().read(cx).snapshot(cx); editor.selections.newest::<usize>(cx),
editor.buffer().read(cx).snapshot(cx),
)
});
for assist_id in &editor_assists.assist_ids { for assist_id in &editor_assists.assist_ids {
let assist = &self.assists[assist_id]; let assist = &self.assists[assist_id];
let assist_range = assist.range.to_offset(&buffer); let assist_range = assist.range.to_offset(&buffer);
@ -594,10 +602,13 @@ impl InlineAssistant {
return; return;
}; };
let editor = editor.read(cx); if editor.read(cx).selections.count() == 1 {
if editor.selections.count() == 1 { let (selection, buffer) = editor.update(cx, |editor, cx| {
let selection = editor.selections.newest::<usize>(cx); (
let buffer = editor.buffer().read(cx).snapshot(cx); editor.selections.newest::<usize>(cx),
editor.buffer().read(cx).snapshot(cx),
)
});
let mut closest_assist_fallback = None; let mut closest_assist_fallback = None;
for assist_id in &editor_assists.assist_ids { for assist_id in &editor_assists.assist_ids {
let assist = &self.assists[assist_id]; let assist = &self.assists[assist_id];

View file

@ -1957,9 +1957,10 @@ async fn test_following_to_channel_notes_without_a_shared_project(
}); });
channel_notes_1_b.update(cx_b, |notes, cx| { channel_notes_1_b.update(cx_b, |notes, cx| {
assert_eq!(notes.channel(cx).unwrap().name, "channel-1"); assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
let editor = notes.editor.read(cx); notes.editor.update(cx, |editor, cx| {
assert_eq!(editor.text(cx), "Hello from A."); assert_eq!(editor.text(cx), "Hello from A.");
assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]); assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
})
}); });
// Client A opens the notes for channel 2. // Client A opens the notes for channel 2.

View file

@ -136,11 +136,12 @@ impl DiagnosticIndicator {
} }
fn update(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) { fn update(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
let editor = editor.read(cx); let (buffer, cursor_position) = editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx); let buffer = editor.buffer().read(cx).snapshot(cx);
let cursor_position = editor.selections.newest::<usize>(cx).head(); let cursor_position = editor.selections.newest::<usize>(cx).head();
(buffer, cursor_position)
});
let new_diagnostic = buffer let new_diagnostic = buffer
.snapshot(cx)
.diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false) .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
.filter(|entry| !entry.range.is_empty()) .filter(|entry| !entry.range.is_empty())
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))

View file

@ -9629,8 +9629,8 @@ impl Editor {
let Some(provider) = self.semantics_provider.clone() else { let Some(provider) = self.semantics_provider.clone() else {
return Task::ready(Ok(Navigated::No)); return Task::ready(Ok(Navigated::No));
}; };
let buffer = self.buffer.read(cx);
let head = self.selections.newest::<usize>(cx).head(); let head = self.selections.newest::<usize>(cx).head();
let buffer = self.buffer.read(cx);
let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) { let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
text_anchor text_anchor
} else { } else {
@ -9937,8 +9937,8 @@ impl Editor {
_: &FindAllReferences, _: &FindAllReferences,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<Task<Result<Navigated>>> { ) -> Option<Task<Result<Navigated>>> {
let multi_buffer = self.buffer.read(cx);
let selection = self.selections.newest::<usize>(cx); let selection = self.selections.newest::<usize>(cx);
let multi_buffer = self.buffer.read(cx);
let head = selection.head(); let head = selection.head();
let multi_buffer_snapshot = multi_buffer.snapshot(cx); let multi_buffer_snapshot = multi_buffer.snapshot(cx);
@ -10345,8 +10345,9 @@ impl Editor {
self.show_local_selections = true; self.show_local_selections = true;
if moving_cursor { if moving_cursor {
let rename_editor = rename.editor.read(cx); let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head(); editor.selections.newest::<usize>(cx).head()
});
// Update the selection to match the position of the selection inside // Update the selection to match the position of the selection inside
// the rename editor. // the rename editor.
@ -11592,9 +11593,9 @@ impl Editor {
} }
pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) { pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
let selection = self.selections.newest::<Point>(cx).start.row + 1;
if let Some(file) = self.target_file(cx) { if let Some(file) = self.target_file(cx) {
if let Some(path) = file.path().to_str() { if let Some(path) = file.path().to_str() {
let selection = self.selections.newest::<Point>(cx).start.row + 1;
cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}"))); cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
} }
} }
@ -12370,9 +12371,10 @@ impl Editor {
return; return;
}; };
let selections = self.selections.all::<usize>(cx);
let buffer = self.buffer.read(cx); let buffer = self.buffer.read(cx);
let mut new_selections_by_buffer = HashMap::default(); let mut new_selections_by_buffer = HashMap::default();
for selection in self.selections.all::<usize>(cx) { for selection in selections {
for (buffer, range, _) in for (buffer, range, _) in
buffer.range_to_buffer_ranges(selection.start..selection.end, cx) 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<Self>) { fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
let selections = self.selections.all::<usize>(cx);
let buffer = self.buffer.read(cx); let buffer = self.buffer.read(cx);
if buffer.is_singleton() { if buffer.is_singleton() {
cx.propagate(); cx.propagate();
@ -12429,7 +12432,7 @@ impl Editor {
}; };
let mut new_selections_by_buffer = HashMap::default(); let mut new_selections_by_buffer = HashMap::default();
for selection in self.selections.all::<usize>(cx) { for selection in selections {
for (mut buffer_handle, mut range, _) in for (mut buffer_handle, mut range, _) in
buffer.range_to_buffer_ranges(selection.range(), cx) buffer.range_to_buffer_ranges(selection.range(), cx)
{ {
@ -12545,7 +12548,7 @@ impl Editor {
fn selection_replacement_ranges( fn selection_replacement_ranges(
&self, &self,
range: Range<OffsetUtf16>, range: Range<OffsetUtf16>,
cx: &AppContext, cx: &mut AppContext,
) -> Vec<Range<OffsetUtf16>> { ) -> Vec<Range<OffsetUtf16>> {
let selections = self.selections.all::<OffsetUtf16>(cx); let selections = self.selections.all::<OffsetUtf16>(cx);
let newest_selection = selections let newest_selection = selections

View file

@ -824,129 +824,131 @@ impl EditorElement {
let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new(); let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
let mut active_rows = BTreeMap::new(); let mut active_rows = BTreeMap::new();
let mut newest_selection_head = None; 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<Selection<Point>> = 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 layout = SelectionLayout::new(
let mut local_selections: Vec<Selection<Point>> = editor selection,
.selections editor.selections.line_mode,
.disjoint_in_range(start_anchor..end_anchor, cx); editor.cursor_shape,
local_selections.extend(editor.selections.pending(cx)); &snapshot.display_snapshot,
let mut layouts = Vec::new(); is_newest,
let newest = editor.selections.newest(cx); editor.leader_peer_id.is_none(),
for selection in local_selections.drain(..) { None,
let is_empty = selection.start == selection.end; );
let is_newest = selection == newest; if is_newest {
newest_selection_head = Some(layout.head);
}
let layout = SelectionLayout::new( for row in cmp::max(layout.active_rows.start.0, start_row.0)
selection, ..=cmp::min(layout.active_rows.end.0, end_row.0)
editor.selections.line_mode, {
editor.cursor_shape, let contains_non_empty_selection =
&snapshot.display_snapshot, active_rows.entry(DisplayRow(row)).or_insert(!is_empty);
is_newest, *contains_non_empty_selection |= !is_empty;
editor.leader_peer_id.is_none(), }
None, layouts.push(layout);
);
if is_newest {
newest_selection_head = Some(layout.head);
} }
for row in cmp::max(layout.active_rows.start.0, start_row.0) let player = if editor.read_only(cx) {
..=cmp::min(layout.active_rows.end.0, end_row.0) cx.theme().players().read_only()
{ } else {
let contains_non_empty_selection = self.style.local_player
active_rows.entry(DisplayRow(row)).or_insert(!is_empty); };
*contains_non_empty_selection |= !is_empty;
} selections.push((player, layouts));
layouts.push(layout);
} }
let player = if editor.read_only(cx) { if let Some(collaboration_hub) = &editor.collaboration_hub {
cx.theme().players().read_only() // When following someone, render the local selections in their color.
} else { if let Some(leader_id) = editor.leader_peer_id {
self.style.local_player if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id)
};
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((local_selection_style, _)) = selections.first_mut() { if let Some(participant_index) = collaboration_hub
*local_selection_style = cx .user_participant_indices(cx)
.theme() .get(&collaborator.user_id)
.players() {
.color_for_participant(participant_index.0); 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(); let mut remote_selections = HashMap::default();
for selection in snapshot.remote_selections_in_range( for selection in snapshot.remote_selections_in_range(
&(start_anchor..end_anchor), &(start_anchor..end_anchor),
collaboration_hub.as_ref(), collaboration_hub.as_ref(),
cx, cx,
) { ) {
let selection_style = Self::get_participant_color(selection.participant_index, cx); let selection_style =
Self::get_participant_color(selection.participant_index, cx);
// Don't re-render the leader's selections, since the local selections // Don't re-render the leader's selections, since the local selections
// match theirs. // match theirs.
if Some(selection.peer_id) == editor.leader_peer_id { if Some(selection.peer_id) == editor.leader_peer_id {
continue; 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, selections.extend(remote_selections.into_values());
selection_id: selection.selection.id, } 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
let is_shown = .buffer_snapshot
editor.show_cursor_names || editor.hovered_cursors.contains_key(&key); .selections_in_range(&(start_anchor..end_anchor), true)
.map(move |(_, line_mode, cursor_shape, selection)| {
remote_selections SelectionLayout::new(
.entry(selection.replica_id) selection,
.or_insert((selection_style, Vec::new())) line_mode,
.1 cursor_shape,
.push(SelectionLayout::new( &snapshot.display_snapshot,
selection.selection, false,
selection.line_mode, false,
selection.cursor_shape, None,
&snapshot.display_snapshot, )
false, })
false, .collect::<Vec<_>>();
if is_shown { selection.user_name } else { None }, 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::<Vec<_>>();
selections.push((player, layouts));
}
(selections, active_rows, newest_selection_head) (selections, active_rows, newest_selection_head)
} }
@ -1848,23 +1850,25 @@ impl EditorElement {
return Vec::new(); return Vec::new();
} }
let editor = self.editor.read(cx); let (newest_selection_head, is_relative) = self.editor.update(cx, |editor, cx| {
let newest_selection_head = newest_selection_head.unwrap_or_else(|| { let newest_selection_head = newest_selection_head.unwrap_or_else(|| {
let newest = editor.selections.newest::<Point>(cx); let newest = editor.selections.newest::<Point>(cx);
SelectionLayout::new( SelectionLayout::new(
newest, newest,
editor.selections.line_mode, editor.selections.line_mode,
editor.cursor_shape, editor.cursor_shape,
&snapshot.display_snapshot, &snapshot.display_snapshot,
true, true,
true, true,
None, None,
) )
.head .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 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 { let relative_to = if is_relative {
Some(newest_selection_head.row()) Some(newest_selection_head.row())
} else { } else {

View file

@ -41,9 +41,9 @@ pub(super) fn refresh_linked_ranges(this: &mut Editor, cx: &mut ViewContext<Edit
return None; return None;
} }
let project = this.project.clone()?; let project = this.project.clone()?;
let selections = this.selections.all::<usize>(cx);
let buffer = this.buffer.read(cx); let buffer = this.buffer.read(cx);
let mut applicable_selections = vec![]; let mut applicable_selections = vec![];
let selections = this.selections.all::<usize>(cx);
let snapshot = buffer.snapshot(cx); let snapshot = buffer.snapshot(cx);
for selection in selections { for selection in selections {
let cursor_position = selection.head(); let cursor_position = selection.head();

View file

@ -8,14 +8,14 @@ use std::{
use collections::HashMap; use collections::HashMap;
use gpui::{AppContext, Model, Pixels}; use gpui::{AppContext, Model, Pixels};
use itertools::Itertools; use itertools::Itertools;
use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint}; use language::{Bias, Point, Selection, SelectionGoal, TextDimension};
use util::post_inc; use util::post_inc;
use crate::{ use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
movement::TextLayoutDetails, movement::TextLayoutDetails,
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
ToOffset, ToOffset, ToPoint,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -96,7 +96,7 @@ impl SelectionsCollection {
pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>( pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Option<Selection<D>> { ) -> Option<Selection<D>> {
self.pending_anchor() self.pending_anchor()
.as_ref() .as_ref()
@ -107,7 +107,7 @@ impl SelectionsCollection {
self.pending.as_ref().map(|pending| pending.mode.clone()) self.pending.as_ref().map(|pending| pending.mode.clone())
} }
pub fn all<'a, D>(&self, cx: &AppContext) -> Vec<Selection<D>> pub fn all<'a, D>(&self, cx: &mut AppContext) -> Vec<Selection<D>>
where where
D: 'a + TextDimension + Ord + Sub<D, Output = D>, D: 'a + TextDimension + Ord + Sub<D, Output = D>,
{ {
@ -194,7 +194,7 @@ impl SelectionsCollection {
pub fn disjoint_in_range<'a, D>( pub fn disjoint_in_range<'a, D>(
&self, &self,
range: Range<Anchor>, range: Range<Anchor>,
cx: &AppContext, cx: &mut AppContext,
) -> Vec<Selection<D>> ) -> Vec<Selection<D>>
where where
D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug, D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
@ -239,9 +239,10 @@ impl SelectionsCollection {
pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>( pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Selection<D> { ) -> Selection<D> {
resolve(self.newest_anchor(), &self.buffer(cx)) let buffer = self.buffer(cx);
self.newest_anchor().map(|p| p.summary::<D>(&buffer))
} }
pub fn newest_display(&self, cx: &mut AppContext) -> Selection<DisplayPoint> { pub fn newest_display(&self, cx: &mut AppContext) -> Selection<DisplayPoint> {
@ -262,9 +263,10 @@ impl SelectionsCollection {
pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>( pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Selection<D> { ) -> Selection<D> {
resolve(self.oldest_anchor(), &self.buffer(cx)) let buffer = self.buffer(cx);
self.oldest_anchor().map(|p| p.summary::<D>(&buffer))
} }
pub fn first_anchor(&self) -> Selection<Anchor> { pub fn first_anchor(&self) -> Selection<Anchor> {
@ -276,14 +278,14 @@ impl SelectionsCollection {
pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>( pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Selection<D> { ) -> Selection<D> {
self.all(cx).first().unwrap().clone() self.all(cx).first().unwrap().clone()
} }
pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>( pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Selection<D> { ) -> Selection<D> {
self.all(cx).last().unwrap().clone() self.all(cx).last().unwrap().clone()
} }
@ -298,7 +300,7 @@ impl SelectionsCollection {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>( pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
&self, &self,
cx: &AppContext, cx: &mut AppContext,
) -> Vec<Range<D>> { ) -> Vec<Range<D>> {
self.all::<D>(cx) self.all::<D>(cx)
.iter() .iter()
@ -475,7 +477,7 @@ impl<'a> MutableSelectionsCollection<'a> {
where where
T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy, T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + 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 start = range.start.to_offset(&self.buffer());
let mut end = range.end.to_offset(&self.buffer()); let mut end = range.end.to_offset(&self.buffer());
let reversed = if start > end { let reversed = if start > end {
@ -649,6 +651,7 @@ impl<'a> MutableSelectionsCollection<'a> {
let mut changed = false; let mut changed = false;
let display_map = self.display_map(); let display_map = self.display_map();
let selections = self let selections = self
.collection
.all::<Point>(self.cx) .all::<Point>(self.cx)
.into_iter() .into_iter()
.map(|selection| { .map(|selection| {
@ -676,6 +679,7 @@ impl<'a> MutableSelectionsCollection<'a> {
let mut changed = false; let mut changed = false;
let snapshot = self.buffer().clone(); let snapshot = self.buffer().clone();
let selections = self let selections = self
.collection
.all::<usize>(self.cx) .all::<usize>(self.cx)
.into_iter() .into_iter()
.map(|selection| { .map(|selection| {
@ -869,10 +873,3 @@ where
goal: s.goal, goal: s.goal,
}) })
} }
fn resolve<D: TextDimension + Ord + Sub<D, Output = D>>(
selection: &Selection<Anchor>,
buffer: &MultiBufferSnapshot,
) -> Selection<D> {
selection.map(|p| p.summary::<D>(buffer))
}

View file

@ -37,34 +37,34 @@ impl CursorPosition {
} }
fn update_position(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) { fn update_position(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
let editor = editor.read(cx); editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).snapshot(cx); let buffer = editor.buffer().read(cx).snapshot(cx);
self.selected_count = Default::default(); self.selected_count = Default::default();
self.selected_count.selections = editor.selections.count(); self.selected_count.selections = editor.selections.count();
let mut last_selection: Option<Selection<usize>> = None; let mut last_selection: Option<Selection<usize>> = None;
for selection in editor.selections.all::<usize>(cx) { for selection in editor.selections.all::<usize>(cx) {
self.selected_count.characters += buffer self.selected_count.characters += buffer
.text_for_range(selection.start..selection.end) .text_for_range(selection.start..selection.end)
.map(|t| t.chars().count()) .map(|t| t.chars().count())
.sum::<usize>(); .sum::<usize>();
if last_selection if last_selection
.as_ref() .as_ref()
.map_or(true, |last_selection| selection.id > last_selection.id) .map_or(true, |last_selection| selection.id > last_selection.id)
{ {
last_selection = Some(selection); last_selection = Some(selection);
}
}
for selection in editor.selections.all::<Point>(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;
} }
} }
} for selection in editor.selections.all::<Point>(cx) {
self.position = last_selection.map(|s| s.head().to_point(&buffer)); 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(); cx.notify();
} }

View file

@ -56,8 +56,8 @@ impl GoToLine {
} }
pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self { pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
let editor = active_editor.read(cx); let cursor =
let cursor = editor.selections.last::<Point>(cx).head(); active_editor.update(cx, |editor, cx| editor.selections.last::<Point>(cx).head());
let line = cursor.row + 1; let line = cursor.row + 1;
let column = cursor.column + 1; let column = cursor.column + 1;

View file

@ -128,12 +128,14 @@ impl SyntaxTreeView {
fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> { fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
// Find which excerpt the cursor is in, and the position within that excerpted buffer. // Find which excerpt the cursor is in, and the position within that excerpted buffer.
let editor_state = self.editor.as_mut()?; let editor_state = self.editor.as_mut()?;
let editor = &editor_state.editor.read(cx); let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| {
let selection_range = editor.selections.last::<usize>(cx).range(); let selection_range = editor.selections.last::<usize>(cx).range();
let multibuffer = editor.buffer().read(cx); editor
let (buffer, range, excerpt_id) = multibuffer .buffer()
.range_to_buffer_ranges(selection_range, cx) .read(cx)
.pop()?; .range_to_buffer_ranges(selection_range, cx)
.pop()
})?;
// If the cursor has moved into a different excerpt, retrieve a new syntax layer // If the cursor has moved into a different excerpt, retrieve a new syntax layer
// from that buffer. // from that buffer.

View file

@ -301,8 +301,8 @@ impl MarkdownPreviewView {
this.parse_markdown_from_active_editor(true, cx); this.parse_markdown_from_active_editor(true, cx);
} }
EditorEvent::SelectionsChanged { .. } => { EditorEvent::SelectionsChanged { .. } => {
let editor = editor.read(cx); let selection_range =
let selection_range = editor.selections.last::<usize>(cx).range(); editor.update(cx, |editor, cx| editor.selections.last::<usize>(cx).range());
this.selected_block = this.get_block_index_under_cursor(selection_range); this.selected_block = this.get_block_index_under_cursor(selection_range);
this.list_state.scroll_to_reveal_item(this.selected_block); this.list_state.scroll_to_reveal_item(this.selected_block);
cx.notify(); cx.notify();

View file

@ -194,9 +194,11 @@ impl PickerDelegate for OutlineViewDelegate {
}) })
.collect(); .collect();
let editor = self.active_editor.read(cx); let (buffer, cursor_offset) = self.active_editor.update(cx, |editor, cx| {
let cursor_offset = editor.selections.newest::<usize>(cx).head(); let buffer = editor.buffer().read(cx).snapshot(cx);
let buffer = editor.buffer().read(cx).snapshot(cx); let cursor_offset = editor.selections.newest::<usize>(cx).head();
(buffer, cursor_offset)
});
selected_index = self selected_index = self
.outline .outline
.items .items

View file

@ -2410,11 +2410,9 @@ impl OutlinePanel {
editor: &View<Editor>, editor: &View<Editor>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<PanelEntry> { ) -> Option<PanelEntry> {
let selection = editor let selection = editor.update(cx, |editor, cx| {
.read(cx) editor.selections.newest::<language::Point>(cx).head()
.selections });
.newest::<language::Point>(cx)
.head();
let editor_snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx)); let editor_snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx));
let multi_buffer = editor.read(cx).buffer(); let multi_buffer = editor.read(cx).buffer();
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);

View file

@ -5,7 +5,7 @@ use std::sync::Arc;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use editor::Editor; 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 language::{BufferSnapshot, Language, LanguageName, Point};
use crate::repl_store::ReplStore; use crate::repl_store::ReplStore;
@ -103,7 +103,7 @@ pub enum SessionSupport {
Unsupported, Unsupported,
} }
pub fn session(editor: WeakView<Editor>, cx: &mut AppContext) -> SessionSupport { pub fn session(editor: WeakView<Editor>, cx: &mut WindowContext) -> SessionSupport {
let store = ReplStore::global(cx); let store = ReplStore::global(cx);
let entity_id = editor.entity_id(); let entity_id = editor.entity_id();
@ -311,17 +311,21 @@ fn language_supported(language: &Arc<Language>) -> bool {
} }
} }
fn get_language(editor: WeakView<Editor>, cx: &mut AppContext) -> Option<Arc<Language>> { fn get_language(editor: WeakView<Editor>, cx: &mut WindowContext) -> Option<Arc<Language>> {
let editor = editor.upgrade()?; editor
let selection = editor.read(cx).selections.newest::<usize>(cx); .update(cx, |editor, cx| {
let buffer = editor.read(cx).buffer().read(cx).snapshot(cx); let selection = editor.selections.newest::<usize>(cx);
buffer.language_at(selection.head()).cloned() let buffer = editor.buffer().read(cx).snapshot(cx);
buffer.language_at(selection.head()).cloned()
})
.ok()
.flatten()
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use gpui::Context; use gpui::{AppContext, Context};
use indoc::indoc; use indoc::indoc;
use language::{Buffer, Language, LanguageConfig, LanguageRegistry}; use language::{Buffer, Language, LanguageConfig, LanguageRegistry};

View file

@ -281,7 +281,7 @@ impl VimGlobals {
&mut self, &mut self,
register: Option<char>, register: Option<char>,
editor: Option<&mut Editor>, editor: Option<&mut Editor>,
cx: &ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Option<Register> { ) -> Option<Register> {
let Some(register) = register.filter(|reg| *reg != '"') else { let Some(register) = register.filter(|reg| *reg != '"') else {
let setting = VimSettings::get_global(cx).use_system_clipboard; let setting = VimSettings::get_global(cx).use_system_clipboard;

View file

@ -620,9 +620,11 @@ impl Vim {
let Some(editor) = self.editor() else { let Some(editor) = self.editor() else {
return; return;
}; };
let newest_selection_empty = editor.update(cx, |editor, cx| {
editor.selections.newest::<usize>(cx).is_empty()
});
let editor = editor.read(cx); let editor = editor.read(cx);
let editor_mode = editor.mode(); let editor_mode = editor.mode();
let newest_selection_empty = editor.selections.newest::<usize>(cx).is_empty();
if editor_mode == EditorMode::Full if editor_mode == EditorMode::Full
&& !newest_selection_empty && !newest_selection_empty
@ -717,11 +719,12 @@ impl Vim {
globals.recorded_count = None; globals.recorded_count = None;
let selections = self.editor().map(|editor| { let selections = self.editor().map(|editor| {
let editor = editor.read(cx); editor.update(cx, |editor, cx| {
( (
editor.selections.oldest::<Point>(cx), editor.selections.oldest::<Point>(cx),
editor.selections.newest::<Point>(cx), editor.selections.newest::<Point>(cx),
) )
})
}); });
if let Some((oldest, newest)) = selections { if let Some((oldest, newest)) = selections {