Reduce display_map snapshot creation (#39354)

Re-applies https://github.com/zed-industries/zed/pull/30840

This PR re-applies the initial
[PR](https://github.com/zed-industries/zed/pull/30840). As it was closed
because it was hard to land, because of the many conflicts. This PR
re-applies the changes for it.

In several cases we were creating multiple display_map
snapshots within the same root-level function call.
Creating a display_map snapshot is quite slow, and in some
cases we were creating the snapshot multiple times.

Release Notes:

- N/A
This commit is contained in:
Remco Smits 2025-10-17 21:56:57 +02:00 committed by GitHub
parent ef5b8c6fed
commit 7e97fcaacb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 853 additions and 426 deletions

View file

@ -652,7 +652,9 @@ impl ContextPickerCompletionProvider {
.active_item(cx)
.and_then(|item| item.downcast::<Editor>())
.is_some_and(|editor| {
editor.update(cx, |editor, cx| editor.has_non_empty_selection(cx))
editor.update(cx, |editor, cx| {
editor.has_non_empty_selection(&editor.display_snapshot(cx))
})
});
if has_selection {
entries.push(ContextPickerEntry::Action(

View file

@ -452,7 +452,10 @@ fn update_editor_selection(
window: &mut Window,
cx: &mut Context<Editor>,
) {
let newest_cursor = editor.selections.newest::<Point>(cx).head();
let newest_cursor = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.head();
if !diff_hunks.iter().any(|hunk| {
hunk.row_range
@ -1895,7 +1898,9 @@ mod tests {
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(1, 0)..Point::new(1, 0)
);
@ -1909,7 +1914,9 @@ mod tests {
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -1930,7 +1937,9 @@ mod tests {
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -1962,7 +1971,9 @@ mod tests {
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -2119,7 +2130,9 @@ mod tests {
);
assert_eq!(
editor1
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(1, 0)..Point::new(1, 0)
);
@ -2160,7 +2173,9 @@ mod tests {
);
assert_eq!(
editor1
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -2181,7 +2196,9 @@ mod tests {
);
assert_eq!(
editor1
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -2207,7 +2224,9 @@ mod tests {
);
assert_eq!(
editor1
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
@ -2240,7 +2259,9 @@ mod tests {
);
assert_eq!(
editor2
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.update(cx, |editor, cx| editor
.selections
.newest::<Point>(&editor.display_snapshot(cx)))
.range(),
Point::new(0, 0)..Point::new(0, 0)
);

View file

@ -606,7 +606,11 @@ pub(crate) fn available_context_picker_entries(
.read(cx)
.active_item(cx)
.and_then(|item| item.downcast::<Editor>())
.is_some_and(|editor| editor.update(cx, |editor, cx| editor.has_non_empty_selection(cx)));
.is_some_and(|editor| {
editor.update(cx, |editor, cx| {
editor.has_non_empty_selection(&editor.display_snapshot(cx))
})
});
if has_selection {
entries.push(ContextPickerEntry::Action(
ContextPickerAction::AddSelections,
@ -725,7 +729,7 @@ pub(crate) fn selection_ranges(
};
editor.update(cx, |editor, cx| {
let selections = editor.selections.all_adjusted(cx);
let selections = editor.selections.all_adjusted(&editor.display_snapshot(cx));
let buffer = editor.buffer().clone().read(cx);
let snapshot = buffer.snapshot(cx);

View file

@ -363,9 +363,12 @@ impl InlineAssistant {
cx: &mut App,
) {
let (snapshot, initial_selections, newest_selection) = editor.update(cx, |editor, cx| {
let selections = editor.selections.all::<Point>(cx);
let newest_selection = editor.selections.newest::<Point>(cx);
(editor.snapshot(window, cx), selections, newest_selection)
let snapshot = editor.snapshot(window, cx);
let selections = editor.selections.all::<Point>(&snapshot.display_snapshot);
let newest_selection = editor
.selections
.newest::<Point>(&snapshot.display_snapshot);
(snapshot, selections, newest_selection)
});
// Check if there is already an inline assistant that contains the
@ -798,7 +801,9 @@ impl InlineAssistant {
if editor.read(cx).selections.count() == 1 {
let (selection, buffer) = editor.update(cx, |editor, cx| {
(
editor.selections.newest::<usize>(cx),
editor
.selections
.newest::<usize>(&editor.display_snapshot(cx)),
editor.buffer().read(cx).snapshot(cx),
)
});
@ -829,7 +834,9 @@ impl InlineAssistant {
if editor.read(cx).selections.count() == 1 {
let (selection, buffer) = editor.update(cx, |editor, cx| {
(
editor.selections.newest::<usize>(cx),
editor
.selections
.newest::<usize>(&editor.display_snapshot(cx)),
editor.buffer().read(cx).snapshot(cx),
)
});

View file

@ -431,9 +431,9 @@ impl TextThreadEditor {
}
fn cursors(&self, cx: &mut App) -> Vec<usize> {
let selections = self
.editor
.update(cx, |editor, cx| editor.selections.all::<usize>(cx));
let selections = self.editor.update(cx, |editor, cx| {
editor.selections.all::<usize>(&editor.display_snapshot(cx))
});
selections
.into_iter()
.map(|selection| selection.head())
@ -446,7 +446,10 @@ impl TextThreadEditor {
editor.transact(window, cx, |editor, window, cx| {
editor.change_selections(Default::default(), window, cx, |s| s.try_cancel());
let snapshot = editor.buffer().read(cx).snapshot(cx);
let newest_cursor = editor.selections.newest::<Point>(cx).head();
let newest_cursor = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.head();
if newest_cursor.column > 0
|| snapshot
.chars_at(newest_cursor)
@ -1248,11 +1251,19 @@ impl TextThreadEditor {
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 display_map = context_editor.display_snapshot(cx);
if context_editor
.selections
.newest::<Point>(&display_map)
.is_empty()
{
let snapshot = context_editor.buffer().read(cx).snapshot(cx);
let (_, _, snapshot) = snapshot.as_singleton()?;
let head = context_editor.selections.newest::<Point>(cx).head();
let head = context_editor
.selections
.newest::<Point>(&display_map)
.head();
let offset = snapshot.point_to_offset(head);
let surrounding_code_block_range = find_surrounding_code_block(snapshot, offset)?;
@ -1269,7 +1280,7 @@ impl TextThreadEditor {
(!text.is_empty()).then_some((text, true))
} else {
let selection = context_editor.selections.newest_adjusted(cx);
let selection = context_editor.selections.newest_adjusted(&display_map);
let buffer = context_editor.buffer().read(cx).snapshot(cx);
let selected_text = buffer.text_for_range(selection.range()).collect::<String>();
@ -1457,7 +1468,7 @@ impl TextThreadEditor {
let selections = editor.update(cx, |editor, cx| {
editor
.selections
.all_adjusted(cx)
.all_adjusted(&editor.display_snapshot(cx))
.into_iter()
.filter_map(|s| {
(!s.is_empty())
@ -1489,7 +1500,10 @@ impl TextThreadEditor {
self.editor.update(cx, |editor, cx| {
editor.insert("\n", window, cx);
for (text, crease_title) in creases {
let point = editor.selections.newest::<Point>(cx).head();
let point = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.head();
let start_row = MultiBufferRow(point.row);
editor.insert(&text, window, cx);
@ -1561,7 +1575,9 @@ impl TextThreadEditor {
cx: &mut Context<Self>,
) -> (String, CopyMetadata, Vec<text::Selection<usize>>) {
let (mut selection, creases) = self.editor.update(cx, |editor, cx| {
let mut selection = editor.selections.newest_adjusted(cx);
let mut selection = editor
.selections
.newest_adjusted(&editor.display_snapshot(cx));
let snapshot = editor.buffer().read(cx).snapshot(cx);
selection.goal = SelectionGoal::None;
@ -1680,7 +1696,10 @@ impl TextThreadEditor {
if images.is_empty() {
self.editor.update(cx, |editor, cx| {
let paste_position = editor.selections.newest::<usize>(cx).head();
let paste_position = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.head();
editor.paste(action, window, cx);
if let Some(metadata) = metadata {
@ -1727,13 +1746,13 @@ impl TextThreadEditor {
editor.transact(window, cx, |editor, _window, cx| {
let edits = editor
.selections
.all::<usize>(cx)
.all::<usize>(&editor.display_snapshot(cx))
.into_iter()
.map(|selection| (selection.start..selection.end, "\n"));
editor.edit(edits, cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
for selection in editor.selections.all::<usize>(cx) {
for selection in editor.selections.all::<usize>(&editor.display_snapshot(cx)) {
image_positions.push(snapshot.anchor_before(selection.end));
}
});

View file

@ -79,7 +79,7 @@ impl SlashCommand for SelectionCommand {
editor.update(cx, |editor, cx| {
let selection_ranges = editor
.selections
.all_adjusted(cx)
.all_adjusted(&editor.display_snapshot(cx))
.iter()
.map(|selection| selection.range())
.collect::<Vec<_>>();

View file

@ -877,7 +877,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
6..9
);
rename.editor.update(cx, |rename_editor, cx| {
let rename_selection = rename_editor.selections.newest::<usize>(cx);
let rename_selection = rename_editor.selections.newest::<usize>(&rename_editor.display_snapshot(cx));
assert_eq!(
rename_selection.range(),
0..3,
@ -924,7 +924,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
let lsp_rename_end = rename.range.end.to_offset(&buffer);
assert_eq!(lsp_rename_start..lsp_rename_end, 6..9);
rename.editor.update(cx, |rename_editor, cx| {
let rename_selection = rename_editor.selections.newest::<usize>(cx);
let rename_selection = rename_editor.selections.newest::<usize>(&rename_editor.display_snapshot(cx));
assert_eq!(
rename_selection.range(),
1..2,

View file

@ -122,13 +122,19 @@ async fn test_basic_following(
editor.handle_input("b", window, cx);
editor.handle_input("c", window, cx);
editor.select_left(&Default::default(), window, cx);
assert_eq!(editor.selections.ranges(cx), vec![3..2]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![3..2]
);
});
editor_a2.update_in(cx_a, |editor, window, cx| {
editor.handle_input("d", window, cx);
editor.handle_input("e", window, cx);
editor.select_left(&Default::default(), window, cx);
assert_eq!(editor.selections.ranges(cx), vec![2..1]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![2..1]
);
});
// When client B starts following client A, only the active view state is replicated to client B.
@ -149,11 +155,15 @@ async fn test_basic_following(
Some((worktree_id, rel_path("2.txt")).into())
);
assert_eq!(
editor_b2.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
editor_b2.update(cx_b, |editor, cx| editor
.selections
.ranges(&editor.display_snapshot(cx))),
vec![2..1]
);
assert_eq!(
editor_b1.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
editor_b1.update(cx_b, |editor, cx| editor
.selections
.ranges(&editor.display_snapshot(cx))),
vec![3..3]
);
@ -384,7 +394,10 @@ async fn test_basic_following(
cx_b.background_executor.run_until_parked();
editor_b1.update(cx_b, |editor, cx| {
assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
&[1..1, 2..2]
);
});
editor_a1.update_in(cx_a, |editor, window, cx| {
@ -402,7 +415,10 @@ async fn test_basic_following(
executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
executor.run_until_parked();
editor_b1.update(cx_b, |editor, cx| {
assert_eq!(editor.selections.ranges(cx), &[3..3]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
&[3..3]
);
});
// After unfollowing, client B stops receiving updates from client A.
@ -1679,7 +1695,10 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
cx_a.run_until_parked();
editor_b.update(cx_b, |editor, cx| {
assert_eq!(editor.selections.ranges(cx), vec![1..1])
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![1..1]
)
});
// a unshares the project
@ -1701,7 +1720,10 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
cx_a.run_until_parked();
editor_b.update(cx_b, |editor, cx| {
assert_eq!(editor.selections.ranges(cx), vec![1..1])
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![1..1]
)
});
cx_b.update(|_, cx| {
let room = ActiveCall::global(cx).read(cx).room().unwrap().read(cx);
@ -1799,13 +1821,19 @@ async fn test_following_into_excluded_file(
editor.handle_input("b", window, cx);
editor.handle_input("c", window, cx);
editor.select_left(&Default::default(), window, cx);
assert_eq!(editor.selections.ranges(cx), vec![3..2]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![3..2]
);
});
editor_for_excluded_a.update_in(cx_a, |editor, window, cx| {
editor.select_all(&Default::default(), window, cx);
editor.handle_input("new commit message", window, cx);
editor.select_left(&Default::default(), window, cx);
assert_eq!(editor.selections.ranges(cx), vec![18..17]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![18..17]
);
});
// When client B starts following client A, currently visible file is replicated
@ -1827,7 +1855,9 @@ async fn test_following_into_excluded_file(
Some((worktree_id, rel_path(".git/COMMIT_EDITMSG")).into())
);
assert_eq!(
editor_for_excluded_b.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
editor_for_excluded_b.update(cx_b, |editor, cx| editor
.selections
.ranges(&editor.display_snapshot(cx))),
vec![18..17]
);
@ -2037,7 +2067,12 @@ async fn test_following_to_channel_notes_without_a_shared_project(
assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
notes.editor.update(cx, |editor, cx| {
assert_eq!(editor.text(cx), "Hello from A.");
assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
assert_eq!(
editor
.selections
.ranges::<usize>(&editor.display_snapshot(cx)),
&[3..4]
);
})
});

View file

@ -287,9 +287,12 @@ impl ChannelView {
}
fn copy_link(&mut self, _: &CopyLink, window: &mut Window, cx: &mut Context<Self>) {
let position = self
.editor
.update(cx, |editor, cx| editor.selections.newest_display(cx).start);
let position = self.editor.update(cx, |editor, cx| {
editor
.selections
.newest_display(&editor.display_snapshot(cx))
.start
});
self.copy_link_for_position(position, window, cx)
}

View file

@ -698,7 +698,11 @@ mod tests {
editor.update_in(cx, |editor, window, cx| {
assert!(editor.focus_handle(cx).is_focused(window));
assert_eq!(
editor.selections.last::<Point>(cx).range().start,
editor
.selections
.last::<Point>(&editor.display_snapshot(cx))
.range()
.start,
Point::new(2, 0)
);
});

View file

@ -341,8 +341,10 @@ pub fn init(cx: &mut App) {
maybe!({
let (buffer, position, _) = editor
.update(cx, |editor, cx| {
let cursor_point: language::Point =
editor.selections.newest(cx).head();
let cursor_point: language::Point = editor
.selections
.newest(&editor.display_snapshot(cx))
.head();
editor
.buffer()
@ -392,7 +394,10 @@ pub fn init(cx: &mut App) {
let text = editor
.update(cx, |editor, cx| {
editor.text_for_range(
editor.selections.newest(cx).range(),
editor
.selections
.newest(&editor.display_snapshot(cx))
.range(),
&mut None,
window,
cx,

View file

@ -963,8 +963,12 @@ mod tests {
) {
cx.set_state(input);
let buffer_position =
cx.editor(|editor, _, cx| editor.selections.newest::<Point>(cx).start);
let buffer_position = cx.editor(|editor, _, cx| {
editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.start
});
let snapshot = &cx.buffer_snapshot();

View file

@ -55,7 +55,10 @@ impl StackTraceView {
cx.subscribe_in(&editor, window, |this, editor, event, window, cx| {
if let EditorEvent::SelectionsChanged { local: true } = event {
let excerpt_id = editor.update(cx, |editor, cx| {
let position: Point = editor.selections.newest(cx).head();
let position: Point = editor
.selections
.newest(&editor.display_snapshot(cx))
.head();
editor
.snapshot(window, cx)

View file

@ -231,7 +231,10 @@ async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.newest::<Point>(cx).head(),
editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.head(),
Point::new(5, 2)
)
});

View file

@ -170,7 +170,10 @@ impl DiagnosticIndicator {
fn update(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) {
let (buffer, cursor_position) = editor.update(cx, |editor, 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>(&editor.display_snapshot(cx))
.head();
(buffer, cursor_position)
});
let new_diagnostic = buffer

View file

@ -2323,21 +2323,22 @@ impl Editor {
}
EditorEvent::Edited { .. } => {
if !vim_enabled(cx) {
let (map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
let pop_state = editor
.change_list
.last()
.map(|previous| {
previous.len() == selections.len()
&& previous.iter().enumerate().all(|(ix, p)| {
p.to_display_point(&map).row()
p.to_display_point(&display_map).row()
== selections[ix].head().row()
})
})
.unwrap_or(false);
let new_positions = selections
.into_iter()
.map(|s| map.display_point_to_anchor(s.head(), Bias::Left))
.map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
.collect();
editor
.change_list
@ -2408,6 +2409,10 @@ impl Editor {
editor
}
pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
self.selections.display_map(cx)
}
pub fn deploy_mouse_context_menu(
&mut self,
position: gpui::Point<Pixels>,
@ -2443,7 +2448,7 @@ impl Editor {
}
self.selections
.disjoint_in_range::<usize>(range.clone(), cx)
.disjoint_in_range::<usize>(range.clone(), &self.display_snapshot(cx))
.into_iter()
.any(|selection| {
// This is needed to cover a corner case, if we just check for an existing
@ -3039,7 +3044,7 @@ impl Editor {
// Copy selections to primary selection buffer
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if local {
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
let buffer_handle = self.buffer.read(cx).read(cx);
let mut text = String::new();
@ -3491,7 +3496,7 @@ impl Editor {
cx: &mut Context<Self>,
) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let tail = self.selections.newest::<usize>(cx).tail();
let tail = self.selections.newest::<usize>(&display_map).tail();
let click_count = click_count.max(match self.selections.select_mode() {
SelectMode::Character => 1,
SelectMode::Word(_) => 2,
@ -3610,7 +3615,7 @@ impl Editor {
let point_to_delete: Option<usize> = {
let selected_points: Vec<Selection<Point>> =
self.selections.disjoint_in_range(start..end, cx);
self.selections.disjoint_in_range(start..end, &display_map);
if !add || click_count > 1 {
None
@ -3686,7 +3691,7 @@ impl Editor {
);
};
let tail = self.selections.newest::<Point>(cx).tail();
let tail = self.selections.newest::<Point>(&display_map).tail();
let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
self.columnar_selection_state = match mode {
ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
@ -3813,7 +3818,7 @@ impl Editor {
fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.columnar_selection_state.take();
if let Some(pending_mode) = self.selections.pending_mode() {
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select(selections);
s.clear_pending();
@ -3902,9 +3907,9 @@ impl Editor {
cx.notify();
}
pub fn has_non_empty_selection(&self, cx: &mut App) -> bool {
pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
self.selections
.all_adjusted(cx)
.all_adjusted(snapshot)
.iter()
.any(|selection| !selection.is_empty())
}
@ -4053,7 +4058,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
let mut bracket_inserted = false;
let mut edits = Vec::new();
let mut linked_edits = HashMap::<_, Vec<_>>::default();
@ -4403,7 +4408,7 @@ impl Editor {
let trigger_in_words =
this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
if this.hard_wrap.is_some() {
let latest: Range<Point> = this.selections.newest(cx).range();
let latest: Range<Point> = this.selections.newest(&map).range();
if latest.is_empty()
&& this
.buffer()
@ -4479,7 +4484,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
self.transact(window, cx, |this, window, cx| {
let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
let multi_buffer = this.buffer.read(cx);
let buffer = multi_buffer.snapshot(cx);
selections
@ -4771,7 +4776,12 @@ impl Editor {
let mut edits = Vec::new();
let mut rows = Vec::new();
for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
for (rows_inserted, selection) in self
.selections
.all_adjusted(&self.display_snapshot(cx))
.into_iter()
.enumerate()
{
let cursor = selection.head();
let row = cursor.row;
@ -4831,7 +4841,7 @@ impl Editor {
let mut rows = Vec::new();
let mut rows_inserted = 0;
for selection in self.selections.all_adjusted(cx) {
for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
let cursor = selection.head();
let row = cursor.row;
@ -4903,7 +4913,7 @@ impl Editor {
let text: Arc<str> = text.into();
self.transact(window, cx, |this, window, cx| {
let old_selections = this.selections.all_adjusted(cx);
let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
let selection_anchors = this.buffer.update(cx, |buffer, cx| {
let anchors = {
let snapshot = buffer.read(cx);
@ -5013,7 +5023,7 @@ impl Editor {
/// If any empty selections is touching the start of its innermost containing autoclose
/// region, expand it to select the brackets.
fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
let buffer = self.buffer.read(cx).read(cx);
let new_selections = self
.selections_with_autoclose_regions(selections, &buffer)
@ -6010,7 +6020,7 @@ impl Editor {
let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
let suffix = &old_text[lookbehind.min(old_text.len())..];
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
let mut ranges = Vec::new();
let mut linked_edits = HashMap::<_, Vec<_>>::default();
@ -6162,7 +6172,10 @@ impl Editor {
Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
DisplayPoint::new(*row, 0).to_point(&snapshot)
}
_ => self.selections.newest::<Point>(cx).head(),
_ => self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head(),
};
let Some((buffer, buffer_row)) = snapshot
.buffer_snapshot()
@ -6624,7 +6637,9 @@ impl Editor {
if newest_selection.head().diff_base_anchor.is_some() {
return None;
}
let newest_selection_adjusted = this.selections.newest_adjusted(cx);
let display_snapshot = this.display_snapshot(cx);
let newest_selection_adjusted =
this.selections.newest_adjusted(&display_snapshot);
let buffer = this.buffer.read(cx);
let (start_buffer, start) =
@ -6699,7 +6714,10 @@ impl Editor {
pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
let snapshot = self.snapshot(window, cx);
let cursor = self.selections.newest::<Point>(cx).head();
let cursor = self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head();
let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
else {
return;
@ -7572,7 +7590,10 @@ impl Editor {
// Find an insertion that starts at the cursor position.
let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor_offset = self.selections.newest::<usize>(cx).head();
let cursor_offset = self
.selections
.newest::<usize>(&self.display_snapshot(cx))
.head();
let insertion = edits.iter().find_map(|(range, text)| {
let range = range.to_offset(&snapshot);
if range.is_empty() && range.start == cursor_offset {
@ -8528,7 +8549,11 @@ impl Editor {
&mut self,
cx: &mut Context<Self>,
) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
let cursor_row = self.selections.newest_adjusted(cx).head().row;
let cursor_row = self
.selections
.newest_adjusted(&self.display_snapshot(cx))
.head()
.row;
let ((buffer_id, row), tasks) = self
.tasks
@ -8545,7 +8570,10 @@ impl Editor {
cx: &mut Context<Self>,
) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
let snapshot = self.buffer.read(cx).snapshot(cx);
let offset = self.selections.newest::<usize>(cx).head();
let offset = self
.selections
.newest::<usize>(&self.display_snapshot(cx))
.head();
let excerpt = snapshot.excerpt_containing(offset..offset)?;
let buffer_id = excerpt.buffer().remote_id();
@ -9862,8 +9890,7 @@ impl Editor {
// Check whether the just-entered snippet ends with an auto-closable bracket.
if self.autoclose_regions.is_empty() {
let snapshot = self.buffer.read(cx).snapshot(cx);
let mut all_selections = self.selections.all::<Point>(cx);
for selection in &mut all_selections {
for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
let selection_head = selection.head();
let Some(scope) = snapshot.language_scope_at(selection_head) else {
continue;
@ -10001,9 +10028,12 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
self.transact(window, cx, |this, window, cx| {
this.select_autoclose_pair(window, cx);
let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut linked_ranges = HashMap::<_, Vec<_>>::default();
if !this.linked_edit_ranges.is_empty() {
let selections = this.selections.all::<MultiBufferPoint>(cx);
let selections = this.selections.all::<MultiBufferPoint>(&display_map);
let snapshot = this.buffer.read(cx).snapshot(cx);
for selection in selections.iter() {
@ -10022,8 +10052,7 @@ impl Editor {
}
}
let mut selections = this.selections.all::<MultiBufferPoint>(cx);
let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
for selection in &mut selections {
if selection.is_empty() {
let old_head = selection.head();
@ -10138,7 +10167,7 @@ impl Editor {
return;
}
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let mut selections = self.selections.all_adjusted(cx);
let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
let buffer = self.buffer.read(cx);
let snapshot = buffer.snapshot(cx);
let rows_iter = selections.iter().map(|s| s.head().row);
@ -10254,7 +10283,7 @@ impl Editor {
}
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let mut selections = self.selections.all::<Point>(cx);
let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
let mut prev_edited_row = 0;
let mut row_delta = 0;
let mut edits = Vec::new();
@ -10363,7 +10392,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut deletion_ranges = Vec::new();
let mut last_outdent = None;
{
@ -10424,7 +10453,7 @@ impl Editor {
cx,
);
});
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
this.change_selections(Default::default(), window, cx, |s| s.select(selections));
});
}
@ -10441,7 +10470,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let selections = self
.selections
.all::<usize>(cx)
.all::<usize>(&self.display_snapshot(cx))
.into_iter()
.map(|s| s.range());
@ -10449,7 +10478,7 @@ impl Editor {
this.buffer.update(cx, |buffer, cx| {
buffer.autoindent_ranges(selections, cx);
});
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
this.change_selections(Default::default(), window, cx, |s| s.select(selections));
});
}
@ -10457,7 +10486,7 @@ impl Editor {
pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut new_cursors = Vec::new();
let mut edit_ranges = Vec::new();
@ -10551,7 +10580,7 @@ impl Editor {
return;
}
let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
for selection in self.selections.all::<Point>(cx) {
for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
let start = MultiBufferRow(selection.start.row);
// Treat single line selections as if they include the next line. Otherwise this action
// would do nothing for single line selections individual cursors.
@ -10694,7 +10723,11 @@ impl Editor {
let mut edits = Vec::new();
let mut boundaries = Vec::new();
for selection in self.selections.all::<Point>(cx).iter() {
for selection in self
.selections
.all::<Point>(&self.display_snapshot(cx))
.iter()
{
let Some(wrap_config) = snapshot
.language_at(selection.start)
.and_then(|lang| lang.config().wrap_characters.clone())
@ -10764,7 +10797,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let mut buffer_ids = HashSet::default();
let snapshot = self.buffer().read(cx).snapshot(cx);
for selection in self.selections.all::<usize>(cx) {
for selection in self.selections.all::<usize>(&self.display_snapshot(cx)) {
buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
}
@ -10781,7 +10814,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let selections = self
.selections
.all(cx)
.all(&self.display_snapshot(cx))
.into_iter()
.map(|s| s.range())
.collect();
@ -11189,7 +11222,7 @@ impl Editor {
let mut edits = Vec::new();
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut selections = selections.iter().peekable();
let mut contiguous_row_selections = Vec::new();
let mut new_selections = Vec::new();
@ -11591,7 +11624,7 @@ impl Editor {
let mut edits = Vec::new();
let mut selection_adjustment = 0i32;
for selection in self.selections.all_adjusted(cx) {
for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
let selection_is_empty = selection.is_empty();
let (start, end) = if selection_is_empty {
@ -11683,7 +11716,7 @@ impl Editor {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = display_map.buffer_snapshot();
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut edits = Vec::new();
let mut selections_iter = selections.iter().peekable();
@ -11791,7 +11824,7 @@ impl Editor {
let mut unfold_ranges = Vec::new();
let mut refold_creases = Vec::new();
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut selections = selections.iter().peekable();
let mut contiguous_row_selections = Vec::new();
let mut new_selections = Vec::new();
@ -11902,7 +11935,7 @@ impl Editor {
let mut unfold_ranges = Vec::new();
let mut refold_creases = Vec::new();
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut selections = selections.iter().peekable();
let mut contiguous_row_selections = Vec::new();
let mut new_selections = Vec::new();
@ -12035,7 +12068,7 @@ impl Editor {
});
this.buffer
.update(cx, |buffer, cx| buffer.edit(edits, None, cx));
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
this.change_selections(Default::default(), window, cx, |s| {
s.select(selections);
});
@ -12054,7 +12087,7 @@ impl Editor {
pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
let buffer = self.buffer.read(cx).snapshot(cx);
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
#[derive(Clone, Debug, PartialEq)]
enum CommentFormat {
@ -12430,7 +12463,7 @@ impl Editor {
) -> ClipboardItem {
let mut text = String::new();
let buffer = self.buffer.read(cx).snapshot(cx);
let mut selections = self.selections.all::<Point>(cx);
let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
let mut clipboard_selections = Vec::with_capacity(selections.len());
{
let max_point = buffer.max_point();
@ -12526,7 +12559,7 @@ impl Editor {
}
fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
let buffer = self.buffer.read(cx).read(cx);
let mut text = String::new();
@ -12637,8 +12670,9 @@ impl Editor {
self.transact(window, cx, |this, window, cx| {
let had_active_edit_prediction = this.has_active_edit_prediction();
let old_selections = this.selections.all::<usize>(cx);
let cursor_offset = this.selections.last::<usize>(cx).head();
let display_map = this.display_snapshot(cx);
let old_selections = this.selections.all::<usize>(&display_map);
let cursor_offset = this.selections.last::<usize>(&display_map).head();
if let Some(mut clipboard_selections) = clipboard_selections {
let all_selections_were_entire_line =
@ -12719,7 +12753,7 @@ impl Editor {
);
});
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
this.change_selections(Default::default(), window, cx, |s| s.select(selections));
} else {
let url = url::Url::parse(&clipboard_text).ok();
@ -12784,7 +12818,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
if selections.is_empty() {
log::warn!("There should always be at least one selection in Zed. This is a bug.");
@ -14047,7 +14081,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let mut selection = self.selections.last::<Point>(cx);
let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
selection.set_head(Point::zero(), SelectionGoal::None);
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
self.change_selections(Default::default(), window, cx, |s| {
@ -14126,7 +14160,7 @@ impl Editor {
pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let buffer = self.buffer.read(cx).snapshot(cx);
let mut selection = self.selections.first::<usize>(cx);
let mut selection = self.selections.first::<usize>(&self.display_snapshot(cx));
selection.set_head(buffer.len(), SelectionGoal::None);
self.change_selections(Default::default(), window, cx, |s| {
s.select(vec![selection]);
@ -14144,7 +14178,7 @@ impl Editor {
pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut selections = self.selections.all::<Point>(cx);
let mut selections = self.selections.all::<Point>(&display_map);
let max_point = display_map.buffer_snapshot().max_point();
for selection in &mut selections {
let rows = selection.spanned_rows(true, &display_map);
@ -14165,7 +14199,7 @@ impl Editor {
) {
let selections = self
.selections
.all::<Point>(cx)
.all::<Point>(&self.display_snapshot(cx))
.into_iter()
.map(|selection| selection.start..selection.end)
.collect::<Vec<_>>();
@ -14244,7 +14278,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let all_selections = self.selections.all::<Point>(cx);
let all_selections = self.selections.all::<Point>(&display_map);
let text_layout_details = self.text_layout_details(window);
let (mut columnar_selections, new_selections_to_columnarize) = {
@ -14378,7 +14412,7 @@ impl Editor {
let final_selection_ids: HashSet<_> = self
.selections
.all::<Point>(cx)
.all::<Point>(&display_map)
.iter()
.map(|s| s.id)
.collect();
@ -14436,7 +14470,7 @@ impl Editor {
cx: &mut Context<Self>,
) -> Result<()> {
let buffer = display_map.buffer_snapshot();
let mut selections = self.selections.all::<usize>(cx);
let mut selections = self.selections.all::<usize>(&display_map);
if let Some(mut select_next_state) = self.select_next_state.take() {
let query = &select_next_state.query;
if !select_next_state.done {
@ -14610,7 +14644,7 @@ impl Editor {
let mut new_selections = Vec::new();
let reversed = self.selections.oldest::<usize>(cx).reversed;
let reversed = self.selections.oldest::<usize>(&display_map).reversed;
let buffer = display_map.buffer_snapshot();
let query_matches = select_next_state
.query
@ -14674,7 +14708,7 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = display_map.buffer_snapshot();
let mut selections = self.selections.all::<usize>(cx);
let mut selections = self.selections.all::<usize>(&display_map);
if let Some(mut select_prev_state) = self.select_prev_state.take() {
let query = &select_prev_state.query;
if !select_prev_state.done {
@ -14862,7 +14896,9 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let text_layout_details = &self.text_layout_details(window);
self.transact(window, cx, |this, window, cx| {
let mut selections = this.selections.all::<MultiBufferPoint>(cx);
let mut selections = this
.selections
.all::<MultiBufferPoint>(&this.display_snapshot(cx));
let mut edits = Vec::new();
let mut selection_edit_ranges = Vec::new();
let mut last_toggled_row = None;
@ -15093,7 +15129,7 @@ impl Editor {
// Adjust selections so that they end before any comment suffixes that
// were inserted.
let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
let mut selections = this.selections.all::<Point>(cx);
let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
let snapshot = this.buffer.read(cx).read(cx);
for selection in &mut selections {
while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
@ -15119,7 +15155,7 @@ impl Editor {
drop(snapshot);
this.change_selections(Default::default(), window, cx, |s| s.select(selections));
let selections = this.selections.all::<Point>(cx);
let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
let selections_on_single_row = selections.windows(2).all(|selections| {
selections[0].start.row == selections[1].start.row
&& selections[0].end.row == selections[1].end.row
@ -15163,7 +15199,10 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let buffer = self.buffer.read(cx).snapshot(cx);
let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
let old_selections = self
.selections
.all::<usize>(&self.display_snapshot(cx))
.into_boxed_slice();
fn update_selection(
selection: &Selection<usize>,
@ -15218,7 +15257,10 @@ impl Editor {
let Some(visible_row_count) = self.visible_row_count() else {
return;
};
let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
let old_selections: Box<[_]> = self
.selections
.all::<usize>(&self.display_snapshot(cx))
.into();
if old_selections.is_empty() {
return;
}
@ -15376,7 +15418,7 @@ impl Editor {
let buffer = self.buffer.read(cx).snapshot(cx);
let selections = self
.selections
.all::<usize>(cx)
.all::<usize>(&self.display_snapshot(cx))
.into_iter()
// subtracting the offset requires sorting
.sorted_by_key(|i| i.start);
@ -15448,7 +15490,10 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
let old_selections: Box<[_]> = self
.selections
.all::<usize>(&self.display_snapshot(cx))
.into();
if old_selections.is_empty() {
return;
}
@ -15497,7 +15542,10 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
let old_selections: Box<[_]> = self
.selections
.all::<usize>(&self.display_snapshot(cx))
.into();
if old_selections.is_empty() {
return;
}
@ -16046,7 +16094,7 @@ impl Editor {
cx: &mut Context<Self>,
) {
let buffer = self.buffer.read(cx).snapshot(cx);
let selection = self.selections.newest::<usize>(cx);
let selection = self.selections.newest::<usize>(&self.display_snapshot(cx));
let mut active_group_id = None;
if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
@ -16127,7 +16175,7 @@ impl Editor {
pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let snapshot = self.snapshot(window, cx);
let selection = self.selections.newest::<Point>(cx);
let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
self.go_to_hunk_before_or_after_position(
&snapshot,
selection.head(),
@ -16188,7 +16236,7 @@ impl Editor {
) {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let snapshot = self.snapshot(window, cx);
let selection = self.selections.newest::<Point>(cx);
let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
self.go_to_hunk_before_or_after_position(
&snapshot,
selection.head(),
@ -16278,7 +16326,10 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let snapshot = self.snapshot(window, cx);
let buffer = &snapshot.buffer_snapshot();
let position = self.selections.newest::<Point>(cx).head();
let position = self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head();
let anchor_position = buffer.anchor_after(position);
// Get all document highlights (both read and write)
@ -16461,7 +16512,10 @@ impl Editor {
let Some(provider) = self.semantics_provider.clone() else {
return Task::ready(Ok(Navigated::No));
};
let head = self.selections.newest::<usize>(cx).head();
let head = self
.selections
.newest::<usize>(&self.display_snapshot(cx))
.head();
let buffer = self.buffer.read(cx);
let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
return Task::ready(Ok(Navigated::No));
@ -16792,7 +16846,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<Task<Result<Navigated>>> {
let selection = self.selections.newest::<usize>(cx);
let selection = self.selections.newest::<usize>(&self.display_snapshot(cx));
let multi_buffer = self.buffer.read(cx);
let head = selection.head();
@ -17267,7 +17321,10 @@ impl Editor {
if moving_cursor {
let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
editor.selections.newest::<usize>(cx).head()
editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.head()
});
// Update the selection to match the position of the selection inside
@ -17330,7 +17387,7 @@ impl Editor {
let ranges = self
.selections
.all_adjusted(cx)
.all_adjusted(&self.display_snapshot(cx))
.into_iter()
.map(|selection| selection.range())
.collect_vec();
@ -18029,9 +18086,9 @@ impl Editor {
cx: &mut Context<Self>,
) {
if self.buffer_kind(cx) == ItemBufferKind::Singleton {
let selection = self.selections.newest::<Point>(cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selection = self.selections.newest::<Point>(&display_map);
let range = if selection.is_empty() {
let point = selection.head().to_display_point(&display_map);
let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
@ -18074,7 +18131,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let selection = self.selections.newest::<Point>(cx);
let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let range = if selection.is_empty() {
@ -18097,7 +18154,7 @@ impl Editor {
if self.buffer_kind(cx) == ItemBufferKind::Singleton {
let mut to_fold = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all_adjusted(&display_map);
for selection in selections {
let range = selection.range().sorted();
@ -18204,7 +18261,7 @@ impl Editor {
let row_ranges_to_keep: Vec<Range<u32>> = self
.selections
.all::<Point>(cx)
.all::<Point>(&self.display_snapshot(cx))
.into_iter()
.map(|sel| sel.start.row..sel.end.row)
.collect();
@ -18379,7 +18436,7 @@ impl Editor {
) {
let mut to_fold = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all_adjusted(&display_map);
for selection in selections {
let range = selection.range().sorted();
@ -18423,7 +18480,7 @@ impl Editor {
if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
let autoscroll = self
.selections
.all::<Point>(cx)
.all::<Point>(&display_map)
.iter()
.any(|selection| crease.range().overlaps(&selection.range()));
@ -18435,7 +18492,7 @@ impl Editor {
if self.buffer_kind(cx) == ItemBufferKind::Singleton {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = display_map.buffer_snapshot();
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let ranges = selections
.iter()
.map(|s| {
@ -18469,7 +18526,7 @@ impl Editor {
cx: &mut Context<Self>,
) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let ranges = selections
.iter()
.map(|s| {
@ -18501,7 +18558,7 @@ impl Editor {
let autoscroll = self
.selections
.all::<Point>(cx)
.all::<Point>(&display_map)
.iter()
.any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
@ -18536,8 +18593,8 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let selections = self.selections.all_adjusted(cx);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(&display_map);
let ranges = selections
.into_iter()
.map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
@ -18870,7 +18927,10 @@ impl Editor {
self.stage_or_unstage_diff_hunks(stage, ranges, cx);
let snapshot = self.snapshot(window, cx);
let position = self.selections.newest::<Point>(cx).head();
let position = self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head();
let mut row = snapshot
.buffer_snapshot()
.diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
@ -19018,7 +19078,7 @@ impl Editor {
let snapshot = self.snapshot(window, cx);
let hunks = snapshot.hunks_for_ranges(
self.selections
.all(cx)
.all(&snapshot.display_snapshot)
.into_iter()
.map(|selection| selection.range()),
);
@ -19754,7 +19814,10 @@ impl Editor {
) -> Option<()> {
let blame = self.blame.as_ref()?;
let snapshot = self.snapshot(window, cx);
let cursor = self.selections.newest::<Point>(cx).head();
let cursor = self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head();
let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
let (_, blame_entry) = blame
.update(cx, |blame, cx| {
@ -19896,7 +19959,7 @@ impl Editor {
fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
let buffer_and_selection = maybe!({
let selection = self.selections.newest::<Point>(cx);
let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
let selection_range = selection.range();
let multi_buffer = self.buffer().read(cx);
@ -19974,7 +20037,12 @@ impl Editor {
_: &mut Window,
cx: &mut Context<Self>,
) {
let selection = self.selections.newest::<Point>(cx).start.row + 1;
let selection = self
.selections
.newest::<Point>(&self.display_snapshot(cx))
.start
.row
+ 1;
if let Some(file) = self.target_file(cx) {
let path = file.path().display(file.path_style(cx));
cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
@ -20045,7 +20113,7 @@ impl Editor {
self.transact(window, cx, |this, window, cx| {
let edits = this
.selections
.all::<Point>(cx)
.all::<Point>(&this.display_snapshot(cx))
.into_iter()
.map(|selection| {
let uuid = match version {
@ -21131,7 +21199,7 @@ impl Editor {
return;
};
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
let multi_buffer = self.buffer.read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
let mut new_selections_by_buffer = HashMap::default();
@ -21255,7 +21323,7 @@ impl Editor {
}
}
None => {
let selections = self.selections.all::<usize>(cx);
let selections = self.selections.all::<usize>(&self.display_snapshot(cx));
let multi_buffer = self.buffer.read(cx);
for selection in selections {
for (snapshot, range, _, anchor) in multi_buffer
@ -21393,7 +21461,9 @@ impl Editor {
range: Range<OffsetUtf16>,
cx: &mut App,
) -> Vec<Range<OffsetUtf16>> {
let selections = self.selections.all::<OffsetUtf16>(cx);
let selections = self
.selections
.all::<OffsetUtf16>(&self.display_snapshot(cx));
let newest_selection = selections
.iter()
.max_by_key(|selection| selection.id)
@ -21556,7 +21626,10 @@ impl Editor {
cx: &mut Context<Self>,
) {
self.request_autoscroll(Autoscroll::newest(), cx);
let position = self.selections.newest_display(cx).start;
let position = self
.selections
.newest_display(&self.display_snapshot(cx))
.start;
mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
}
@ -21576,7 +21649,9 @@ impl Editor {
return;
}
if let Some(relative_utf16_range) = relative_utf16_range {
let selections = self.selections.all::<OffsetUtf16>(cx);
let selections = self
.selections
.all::<OffsetUtf16>(&self.display_snapshot(cx));
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
let new_ranges = selections.into_iter().map(|range| {
let start = OffsetUtf16(
@ -21716,7 +21791,7 @@ impl Editor {
}
let transaction =
self.transact(window, cx, |this, window, cx| {
let selections = this.selections.all::<usize>(cx);
let selections = this.selections.all::<usize>(&this.display_snapshot(cx));
let edits = selections
.iter()
.map(|selection| (selection.end..selection.end, pending.clone()));
@ -21735,7 +21810,7 @@ impl Editor {
let snapshot = self.snapshot(window, cx);
let ranges = self
.selections
.all::<usize>(cx)
.all::<usize>(&snapshot.display_snapshot)
.into_iter()
.map(|selection| {
snapshot.buffer_snapshot().anchor_after(selection.end)
@ -23867,7 +23942,9 @@ impl EntityInputHandler for Editor {
return None;
}
let selection = self.selections.newest::<OffsetUtf16>(cx);
let selection = self
.selections
.newest::<OffsetUtf16>(&self.display_snapshot(cx));
let range = selection.range();
Some(UTF16Selection {
@ -23910,7 +23987,7 @@ impl EntityInputHandler for Editor {
let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
let newest_selection_id = this.selections.newest_anchor().id;
this.selections
.all::<OffsetUtf16>(cx)
.all::<OffsetUtf16>(&this.display_snapshot(cx))
.iter()
.zip(ranges_to_replace.iter())
.find_map(|(selection, range)| {
@ -23985,7 +24062,7 @@ impl EntityInputHandler for Editor {
let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
let newest_selection_id = this.selections.newest_anchor().id;
this.selections
.all::<OffsetUtf16>(cx)
.all::<OffsetUtf16>(&this.display_snapshot(cx))
.iter()
.zip(ranges_to_replace.iter())
.find_map(|(selection, range)| {

View file

@ -219,7 +219,10 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
editor.insert("cd", window, cx);
editor.end_transaction_at(now, cx);
assert_eq!(editor.text(cx), "12cd56");
assert_eq!(editor.selections.ranges(cx), vec![4..4]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![4..4]
);
editor.start_transaction_at(now, window, cx);
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
@ -228,7 +231,10 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
editor.insert("e", window, cx);
editor.end_transaction_at(now, cx);
assert_eq!(editor.text(cx), "12cde6");
assert_eq!(editor.selections.ranges(cx), vec![5..5]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![5..5]
);
now += group_interval + Duration::from_millis(1);
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
@ -244,30 +250,45 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
});
assert_eq!(editor.text(cx), "ab2cde6");
assert_eq!(editor.selections.ranges(cx), vec![3..3]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![3..3]
);
// Last transaction happened past the group interval in a different editor.
// Undo it individually and don't restore selections.
editor.undo(&Undo, window, cx);
assert_eq!(editor.text(cx), "12cde6");
assert_eq!(editor.selections.ranges(cx), vec![2..2]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![2..2]
);
// First two transactions happened within the group interval in this editor.
// Undo them together and restore selections.
editor.undo(&Undo, window, cx);
editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
assert_eq!(editor.text(cx), "123456");
assert_eq!(editor.selections.ranges(cx), vec![0..0]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![0..0]
);
// Redo the first two transactions together.
editor.redo(&Redo, window, cx);
assert_eq!(editor.text(cx), "12cde6");
assert_eq!(editor.selections.ranges(cx), vec![5..5]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![5..5]
);
// Redo the last transaction on its own.
editor.redo(&Redo, window, cx);
assert_eq!(editor.text(cx), "ab2cde6");
assert_eq!(editor.selections.ranges(cx), vec![6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
vec![6..6]
);
// Test empty transactions.
editor.start_transaction_at(now, window, cx);
@ -770,10 +791,14 @@ fn test_clone(cx: &mut TestAppContext) {
);
assert_set_eq!(
cloned_editor
.update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
.update(cx, |editor, _, cx| editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)))
.unwrap(),
editor
.update(cx, |editor, _, cx| editor.selections.ranges(cx))
.update(cx, |editor, _, cx| editor
.selections
.ranges(&editor.display_snapshot(cx)))
.unwrap()
);
assert_set_eq!(
@ -3161,7 +3186,7 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
);
});
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
&[
Point::new(1, 2)..Point::new(1, 2),
Point::new(2, 2)..Point::new(2, 2),
@ -3183,7 +3208,7 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
// The selections are moved after the inserted newlines
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
&[
Point::new(2, 0)..Point::new(2, 0),
Point::new(4, 0)..Point::new(4, 0),
@ -3673,13 +3698,19 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
});
assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
&[2..2, 7..7, 12..12],
);
editor.insert("Z", window, cx);
assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
// The selections are moved after the inserted characters
assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
&[3..3, 9..9, 15..15],
);
});
}
@ -4439,7 +4470,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
let buffer = buffer.read(cx).as_singleton().unwrap();
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
&[Point::new(0, 0)..Point::new(0, 0)]
);
@ -4447,7 +4480,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.join_lines(&JoinLines, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
&[Point::new(0, 3)..Point::new(0, 3)]
);
@ -4458,7 +4493,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.join_lines(&JoinLines, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
&[Point::new(0, 11)..Point::new(0, 11)]
);
@ -4466,7 +4503,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.undo(&Undo, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
&[Point::new(0, 5)..Point::new(2, 2)]
);
@ -4477,7 +4516,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.join_lines(&JoinLines, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[Point::new(2, 3)..Point::new(2, 3)]
);
@ -4485,7 +4526,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.join_lines(&JoinLines, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[Point::new(2, 3)..Point::new(2, 3)]
);
@ -4493,7 +4536,9 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
editor.join_lines(&JoinLines, window, cx);
assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[Point::new(2, 3)..Point::new(2, 3)]
);
@ -4550,7 +4595,9 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[
Point::new(0, 7)..Point::new(0, 7),
Point::new(1, 3)..Point::new(1, 3)
@ -5908,15 +5955,24 @@ fn test_transpose(cx: &mut TestAppContext) {
});
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bac");
assert_eq!(editor.selections.ranges(cx), [2..2]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[2..2]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bca");
assert_eq!(editor.selections.ranges(cx), [3..3]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[3..3]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bac");
assert_eq!(editor.selections.ranges(cx), [3..3]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[3..3]
);
editor
});
@ -5929,22 +5985,34 @@ fn test_transpose(cx: &mut TestAppContext) {
});
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "acb\nde");
assert_eq!(editor.selections.ranges(cx), [3..3]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[3..3]
);
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([4..4])
});
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "acbd\ne");
assert_eq!(editor.selections.ranges(cx), [5..5]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[5..5]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "acbde\n");
assert_eq!(editor.selections.ranges(cx), [6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[6..6]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "acbd\ne");
assert_eq!(editor.selections.ranges(cx), [6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[6..6]
);
editor
});
@ -5957,23 +6025,38 @@ fn test_transpose(cx: &mut TestAppContext) {
});
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bacd\ne");
assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[2..2, 3..3, 5..5]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bcade\n");
assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[3..3, 4..4, 6..6]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bcda\ne");
assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[4..4, 6..6]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bcade\n");
assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[4..4, 6..6]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "bcaed\n");
assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[5..5, 6..6]
);
editor
});
@ -5986,15 +6069,24 @@ fn test_transpose(cx: &mut TestAppContext) {
});
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "🏀🍐✋");
assert_eq!(editor.selections.ranges(cx), [8..8]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[8..8]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "🏀✋🍐");
assert_eq!(editor.selections.ranges(cx), [11..11]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[11..11]
);
editor.transpose(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "🏀🍐✋");
assert_eq!(editor.selections.ranges(cx), [11..11]);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[11..11]
);
editor
});
@ -9540,7 +9632,7 @@ async fn test_autoindent(cx: &mut TestAppContext) {
editor.newline(&Newline, window, cx);
assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
&[
Point::new(1, 4)..Point::new(1, 4),
Point::new(3, 4)..Point::new(3, 4),
@ -9616,7 +9708,7 @@ async fn test_autoindent_disabled(cx: &mut TestAppContext) {
)
);
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
&[
Point::new(1, 0)..Point::new(1, 0),
Point::new(3, 0)..Point::new(3, 0),
@ -10255,7 +10347,9 @@ async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
// Precondition: different languages are active at different locations.
cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let cursors = editor.selections.ranges::<usize>(cx);
let cursors = editor
.selections
.ranges::<usize>(&editor.display_snapshot(cx));
let languages = cursors
.iter()
.map(|c| snapshot.language_at(c.start).unwrap().name())
@ -10700,7 +10794,9 @@ async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
.unindent()
);
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[
Point::new(0, 4)..Point::new(0, 4),
Point::new(1, 4)..Point::new(1, 4),
@ -10720,7 +10816,9 @@ async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
.unindent()
);
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[
Point::new(0, 2)..Point::new(0, 2),
Point::new(1, 2)..Point::new(1, 2),
@ -10739,7 +10837,9 @@ async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
.unindent()
);
assert_eq!(
editor.selections.ranges::<Point>(cx),
editor
.selections
.ranges::<Point>(&editor.display_snapshot(cx)),
[
Point::new(0, 1)..Point::new(0, 1),
Point::new(1, 1)..Point::new(1, 1),
@ -10945,7 +11045,12 @@ async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
assert_eq!(editor.text(cx), expected_text);
assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
assert_eq!(
editor
.selections
.ranges::<usize>(&editor.display_snapshot(cx)),
selection_ranges
);
}
assert(
@ -10976,7 +11081,7 @@ async fn test_snippets(cx: &mut TestAppContext) {
let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
let insertion_ranges = editor
.selections
.all(cx)
.all(&editor.display_snapshot(cx))
.iter()
.map(|s| s.range())
.collect::<Vec<_>>();
@ -11056,7 +11161,7 @@ async fn test_snippet_indentation(cx: &mut TestAppContext) {
.unwrap();
let insertion_ranges = editor
.selections
.all(cx)
.all(&editor.display_snapshot(cx))
.iter()
.map(|s| s.range())
.collect::<Vec<_>>();
@ -15945,7 +16050,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
editor.handle_input("X", window, cx);
assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[
Point::new(0, 1)..Point::new(0, 1),
Point::new(1, 1)..Point::new(1, 1),
@ -15959,7 +16064,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
editor.backspace(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "Xa\nbbb");
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(1, 0)..Point::new(1, 0)]
);
@ -15969,7 +16074,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
editor.backspace(&Default::default(), window, cx);
assert_eq!(editor.text(cx), "X\nbb");
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(0, 1)..Point::new(0, 1)]
);
});
@ -16027,7 +16132,10 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
false,
);
assert_eq!(editor.text(cx), expected_text);
assert_eq!(editor.selections.ranges(cx), expected_selections);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
expected_selections
);
editor.newline(&Newline, window, cx);
let (expected_text, expected_selections) = marked_text_ranges(
@ -16044,7 +16152,10 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
false,
);
assert_eq!(editor.text(cx), expected_text);
assert_eq!(editor.selections.ranges(cx), expected_selections);
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
expected_selections
);
});
}
@ -16085,7 +16196,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
cx,
);
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[
Point::new(1, 3)..Point::new(1, 3),
Point::new(2, 1)..Point::new(2, 1),
@ -16098,7 +16209,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
_ = editor.update(cx, |editor, window, cx| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[
Point::new(1, 3)..Point::new(1, 3),
Point::new(2, 1)..Point::new(2, 1),
@ -16112,7 +16223,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
_ = editor.update(cx, |editor, window, cx| {
// Removing an excerpt causes the first selection to become degenerate.
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[
Point::new(0, 0)..Point::new(0, 0),
Point::new(0, 1)..Point::new(0, 1)
@ -16123,7 +16234,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
// location.
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[
Point::new(0, 1)..Point::new(0, 1),
Point::new(0, 3)..Point::new(0, 3)
@ -16167,7 +16278,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
cx,
);
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(1, 3)..Point::new(1, 3)]
);
editor
@ -16178,14 +16289,14 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
});
_ = editor.update(cx, |editor, window, cx| {
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(0, 0)..Point::new(0, 0)]
);
// Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(0, 3)..Point::new(0, 3)]
);
assert!(editor.selections.pending_anchor().is_some());
@ -16435,7 +16546,10 @@ async fn test_following(cx: &mut TestAppContext) {
.await
.unwrap();
_ = follower.update(cx, |follower, _, cx| {
assert_eq!(follower.selections.ranges(cx), vec![1..1]);
assert_eq!(
follower.selections.ranges(&follower.display_snapshot(cx)),
vec![1..1]
);
});
assert!(*is_still_following.borrow());
assert_eq!(*follower_edit_event_count.borrow(), 0);
@ -16488,7 +16602,10 @@ async fn test_following(cx: &mut TestAppContext) {
.unwrap();
_ = follower.update(cx, |follower, _, cx| {
assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
assert_eq!(follower.selections.ranges(cx), vec![0..0]);
assert_eq!(
follower.selections.ranges(&follower.display_snapshot(cx)),
vec![0..0]
);
});
assert!(*is_still_following.borrow());
@ -16512,7 +16629,10 @@ async fn test_following(cx: &mut TestAppContext) {
.await
.unwrap();
_ = follower.update(cx, |follower, _, cx| {
assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
assert_eq!(
follower.selections.ranges(&follower.display_snapshot(cx)),
vec![0..0, 1..1]
);
});
assert!(*is_still_following.borrow());
@ -16533,7 +16653,10 @@ async fn test_following(cx: &mut TestAppContext) {
.await
.unwrap();
_ = follower.update(cx, |follower, _, cx| {
assert_eq!(follower.selections.ranges(cx), vec![0..2]);
assert_eq!(
follower.selections.ranges(&follower.display_snapshot(cx)),
vec![0..2]
);
});
// Scrolling locally breaks the follow
@ -22668,11 +22791,11 @@ fn add_log_breakpoint_at_cursor(
.first()
.and_then(|(anchor, bp)| bp.as_ref().map(|bp| (*anchor, bp.clone())))
.unwrap_or_else(|| {
let cursor_position: Point = editor.selections.newest(cx).head();
let snapshot = editor.snapshot(window, cx);
let cursor_position: Point =
editor.selections.newest(&snapshot.display_snapshot).head();
let breakpoint_position = editor
.snapshot(window, cx)
.display_snapshot
let breakpoint_position = snapshot
.buffer_snapshot()
.anchor_before(Point::new(cursor_position.row, 0));
@ -23619,7 +23742,7 @@ println!("5");
assert_eq!(
editor
.selections
.all::<Point>(cx)
.all::<Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.range())
.collect::<Vec<_>>(),
@ -23662,7 +23785,7 @@ println!("5");
assert_eq!(
editor
.selections
.all::<Point>(cx)
.all::<Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.range())
.collect::<Vec<_>>(),
@ -23788,7 +23911,7 @@ println!("5");
assert_eq!(
editor
.selections
.all::<Point>(cx)
.all::<Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.range())
.collect::<Vec<_>>(),
@ -23814,7 +23937,7 @@ println!("5");
assert_eq!(
editor
.selections
.all::<Point>(cx)
.all::<Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.range())
.collect::<Vec<_>>(),
@ -25211,7 +25334,7 @@ fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Cont
let (text, ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), text);
assert_eq!(
editor.selections.ranges(cx),
editor.selections.ranges(&editor.display_snapshot(cx)),
ranges,
"Assert selections are {}",
marked_text

View file

@ -1377,7 +1377,7 @@ impl EditorElement {
editor_with_selections.update(cx, |editor, cx| {
if editor.show_local_selections {
let mut layouts = Vec::new();
let newest = editor.selections.newest(cx);
let newest = editor.selections.newest(&editor.display_snapshot(cx));
for selection in local_selections.iter().cloned() {
let is_empty = selection.start == selection.end;
let is_newest = selection == newest;
@ -3195,7 +3195,9 @@ impl EditorElement {
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::<Point>(cx);
let newest = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx));
SelectionLayout::new(
newest,
editor.selections.line_mode(),
@ -8793,7 +8795,8 @@ impl Element for EditorElement {
.editor_with_selections(cx)
.map(|editor| {
editor.update(cx, |editor, cx| {
let all_selections = editor.selections.all::<Point>(cx);
let all_selections =
editor.selections.all::<Point>(&snapshot.display_snapshot);
let selected_buffer_ids =
if editor.buffer_kind(cx) == ItemBufferKind::Singleton {
Vec::new()
@ -8815,10 +8818,12 @@ impl Element for EditorElement {
selected_buffer_ids
};
let mut selections = editor
.selections
.disjoint_in_range(start_anchor..end_anchor, cx);
selections.extend(editor.selections.pending(cx));
let mut selections = editor.selections.disjoint_in_range(
start_anchor..end_anchor,
&snapshot.display_snapshot,
);
selections
.extend(editor.selections.pending(&snapshot.display_snapshot));
(selections, selected_buffer_ids)
})

View file

@ -69,7 +69,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Editor>,
) -> Option<HashSet<usize>> {
let selection = self.selections.newest::<Point>(cx);
let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
let cursor_row = MultiBufferRow(selection.head().row);
let state = &mut self.active_indent_guides_state;

View file

@ -594,7 +594,7 @@ impl Item for Editor {
cx: &mut Context<Self>,
) -> bool {
if let Ok(data) = data.downcast::<NavigationData>() {
let newest_selection = self.selections.newest::<Point>(cx);
let newest_selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
let buffer = self.buffer.read(cx).read(cx);
let offset = if buffer.can_resolve(&data.cursor_anchor) {
data.cursor_anchor.to_point(&buffer)
@ -1539,13 +1539,13 @@ impl SearchableItem for Editor {
fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
let snapshot = self.snapshot(window, cx);
let snapshot = snapshot.buffer_snapshot();
let selection = self.selections.newest_adjusted(cx);
let selection = self.selections.newest_adjusted(&snapshot.display_snapshot);
let buffer_snapshot = snapshot.buffer_snapshot();
match setting {
SeedQuerySetting::Never => String::new(),
SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
let text: String = snapshot
let text: String = buffer_snapshot
.text_for_range(selection.start..selection.end)
.collect();
if text.contains('\n') {
@ -1556,10 +1556,10 @@ impl SearchableItem for Editor {
}
SeedQuerySetting::Selection => String::new(),
SeedQuerySetting::Always => {
let (range, kind) =
snapshot.surrounding_word(selection.start, Some(CharScopeContext::Completion));
let (range, kind) = buffer_snapshot
.surrounding_word(selection.start, Some(CharScopeContext::Completion));
if kind == Some(CharKind::Word) {
let text: String = snapshot.text_for_range(range).collect();
let text: String = buffer_snapshot.text_for_range(range).collect();
if !text.trim().is_empty() {
return text;
}

View file

@ -59,7 +59,7 @@ pub(super) fn refresh_linked_ranges(
let mut applicable_selections = Vec::new();
editor
.update(cx, |editor, cx| {
let selections = editor.selections.all::<usize>(cx);
let selections = editor.selections.all::<usize>(&editor.display_snapshot(cx));
let snapshot = editor.buffer.read(cx).snapshot(cx);
let buffer = editor.buffer.read(cx);
for selection in selections {

View file

@ -154,7 +154,7 @@ pub fn deploy_context_menu(
return;
}
let display_map = editor.selections.display_map(cx);
let display_map = editor.display_snapshot(cx);
let source_anchor = display_map.display_point_to_anchor(point, text::Bias::Right);
let context_menu = if let Some(custom) = editor.custom_context_menu.take() {
let menu = custom(editor, point, window, cx);
@ -169,8 +169,8 @@ pub fn deploy_context_menu(
return;
};
let display_map = editor.selections.display_map(cx);
let snapshot = editor.snapshot(window, cx);
let display_map = editor.display_snapshot(cx);
let buffer = snapshot.buffer_snapshot();
let anchor = buffer.anchor_before(point.to_point(&display_map));
if !display_ranges(&display_map, &editor.selections).any(|r| r.contains(&point)) {
@ -185,7 +185,7 @@ pub fn deploy_context_menu(
let has_reveal_target = editor.target_file(cx).is_some();
let has_selections = editor
.selections
.all::<PointUtf16>(cx)
.all::<PointUtf16>(&display_map)
.into_iter()
.any(|s| !s.is_empty());
let has_git_repo = buffer

View file

@ -72,7 +72,12 @@ impl Editor {
cx: &mut Context<Editor>,
) {
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
let new_screen_top = self.selections.newest_display(cx).head().row().0;
let new_screen_top = self
.selections
.newest_display(&self.display_snapshot(cx))
.head()
.row()
.0;
let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows);
self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx);
}
@ -86,7 +91,12 @@ impl Editor {
let Some(visible_rows) = self.visible_line_count().map(|count| count as u32) else {
return;
};
let new_screen_top = self.selections.newest_display(cx).head().row().0;
let new_screen_top = self
.selections
.newest_display(&self.display_snapshot(cx))
.head()
.row()
.0;
let new_screen_top = new_screen_top.saturating_sub(visible_rows / 2);
self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx);
}
@ -101,7 +111,12 @@ impl Editor {
let Some(visible_rows) = self.visible_line_count().map(|count| count as u32) else {
return;
};
let new_screen_top = self.selections.newest_display(cx).head().row().0;
let new_screen_top = self
.selections
.newest_display(&self.display_snapshot(cx))
.head()
.row()
.0;
let new_screen_top =
new_screen_top.saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx);

View file

@ -148,7 +148,7 @@ impl Editor {
target_top = first_highlighted_row.as_f64();
target_bottom = target_top + 1.;
} else {
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
target_top = selections
.first()
@ -293,7 +293,7 @@ impl Editor {
let scroll_width = ScrollOffset::from(scroll_width);
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::<Point>(cx);
let selections = self.selections.all::<Point>(&display_map);
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
let mut target_left;

View file

@ -110,7 +110,7 @@ impl SelectionsCollection {
if self.pending.is_none() {
self.disjoint_anchors_arc()
} else {
let all_offset_selections = self.all::<usize>(cx);
let all_offset_selections = self.all::<usize>(&self.display_map(cx));
let buffer = self.buffer(cx);
all_offset_selections
.into_iter()
@ -129,25 +129,23 @@ impl SelectionsCollection {
pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &mut App,
snapshot: &DisplaySnapshot,
) -> Option<Selection<D>> {
let map = self.display_map(cx);
resolve_selections(self.pending_anchor(), &map).next()
resolve_selections(self.pending_anchor(), &snapshot).next()
}
pub(crate) fn pending_mode(&self) -> Option<SelectMode> {
self.pending.as_ref().map(|pending| pending.mode.clone())
}
pub fn all<'a, D>(&self, cx: &mut App) -> Vec<Selection<D>>
pub fn all<'a, D>(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<D>>
where
D: 'a + TextDimension + Ord + Sub<D, Output = D>,
{
let map = self.display_map(cx);
let disjoint_anchors = &self.disjoint;
let mut disjoint = resolve_selections::<D, _>(disjoint_anchors.iter(), &map).peekable();
let mut pending_opt = self.pending::<D>(cx);
let mut disjoint =
resolve_selections::<D, _>(disjoint_anchors.iter(), &snapshot).peekable();
let mut pending_opt = self.pending::<D>(&snapshot);
iter::from_fn(move || {
if let Some(pending) = pending_opt.as_mut() {
while let Some(next_selection) = disjoint.peek() {
@ -175,12 +173,11 @@ impl SelectionsCollection {
}
/// Returns all of the selections, adjusted to take into account the selection line_mode
pub fn all_adjusted(&self, cx: &mut App) -> Vec<Selection<Point>> {
let mut selections = self.all::<Point>(cx);
pub fn all_adjusted(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<Point>> {
let mut selections = self.all::<Point>(&snapshot);
if self.line_mode {
let map = self.display_map(cx);
for selection in &mut selections {
let new_range = map.expand_to_line(selection.range());
let new_range = snapshot.expand_to_line(selection.range());
selection.start = new_range.start;
selection.end = new_range.end;
}
@ -210,11 +207,10 @@ impl SelectionsCollection {
}
/// 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);
pub fn newest_adjusted(&self, snapshot: &DisplaySnapshot) -> Selection<Point> {
let mut selection = self.newest::<Point>(&snapshot);
if self.line_mode {
let map = self.display_map(cx);
let new_range = map.expand_to_line(selection.range());
let new_range = snapshot.expand_to_line(selection.range());
selection.start = new_range.start;
selection.end = new_range.end;
}
@ -223,53 +219,55 @@ impl SelectionsCollection {
pub fn all_adjusted_display(
&self,
cx: &mut App,
) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
display_map: &DisplaySnapshot,
) -> Vec<Selection<DisplayPoint>> {
if self.line_mode {
let selections = self.all::<Point>(cx);
let map = self.display_map(cx);
let selections = self.all::<Point>(&display_map);
let result = selections
.into_iter()
.map(|mut selection| {
let new_range = map.expand_to_line(selection.range());
let new_range = display_map.expand_to_line(selection.range());
selection.start = new_range.start;
selection.end = new_range.end;
selection.map(|point| point.to_display_point(&map))
selection.map(|point| point.to_display_point(&display_map))
})
.collect();
(map, result)
result
} else {
self.all_display(cx)
self.all_display(display_map)
}
}
pub fn disjoint_in_range<'a, D>(&self, range: Range<Anchor>, cx: &mut App) -> Vec<Selection<D>>
pub fn disjoint_in_range<'a, D>(
&self,
range: Range<Anchor>,
snapshot: &DisplaySnapshot,
) -> Vec<Selection<D>>
where
D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
{
let map = self.display_map(cx);
let start_ix = match self
.disjoint
.binary_search_by(|probe| probe.end.cmp(&range.start, map.buffer_snapshot()))
.binary_search_by(|probe| probe.end.cmp(&range.start, snapshot.buffer_snapshot()))
{
Ok(ix) | Err(ix) => ix,
};
let end_ix = match self
.disjoint
.binary_search_by(|probe| probe.start.cmp(&range.end, map.buffer_snapshot()))
.binary_search_by(|probe| probe.start.cmp(&range.end, snapshot.buffer_snapshot()))
{
Ok(ix) => ix + 1,
Err(ix) => ix,
};
resolve_selections(&self.disjoint[start_ix..end_ix], &map).collect()
resolve_selections(&self.disjoint[start_ix..end_ix], snapshot).collect()
}
pub fn all_display(&self, cx: &mut App) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
let map = self.display_map(cx);
pub fn all_display(&self, snapshot: &DisplaySnapshot) -> Vec<Selection<DisplayPoint>> {
let disjoint_anchors = &self.disjoint;
let mut disjoint = resolve_selections_display(disjoint_anchors.iter(), &map).peekable();
let mut pending_opt = resolve_selections_display(self.pending_anchor(), &map).next();
let selections = iter::from_fn(move || {
let mut disjoint =
resolve_selections_display(disjoint_anchors.iter(), &snapshot).peekable();
let mut pending_opt = resolve_selections_display(self.pending_anchor(), &snapshot).next();
iter::from_fn(move || {
if let Some(pending) = pending_opt.as_mut() {
while let Some(next_selection) = disjoint.peek() {
if pending.start <= next_selection.end && pending.end >= next_selection.start {
@ -292,8 +290,7 @@ impl SelectionsCollection {
disjoint.next()
}
})
.collect();
(map, selections)
.collect()
}
pub fn newest_anchor(&self) -> &Selection<Anchor> {
@ -306,19 +303,15 @@ impl SelectionsCollection {
pub fn newest<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &mut App,
snapshot: &DisplaySnapshot,
) -> Selection<D> {
let map = self.display_map(cx);
resolve_selections([self.newest_anchor()], &map)
resolve_selections([self.newest_anchor()], &snapshot)
.next()
.unwrap()
}
pub fn newest_display(&self, cx: &mut App) -> Selection<DisplayPoint> {
let map = self.display_map(cx);
resolve_selections_display([self.newest_anchor()], &map)
pub fn newest_display(&self, snapshot: &DisplaySnapshot) -> Selection<DisplayPoint> {
resolve_selections_display([self.newest_anchor()], &snapshot)
.next()
.unwrap()
}
@ -333,11 +326,9 @@ impl SelectionsCollection {
pub fn oldest<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &mut App,
snapshot: &DisplaySnapshot,
) -> Selection<D> {
let map = self.display_map(cx);
resolve_selections([self.oldest_anchor()], &map)
resolve_selections([self.oldest_anchor()], &snapshot)
.next()
.unwrap()
}
@ -349,12 +340,18 @@ impl SelectionsCollection {
.unwrap_or_else(|| self.disjoint.first().cloned().unwrap())
}
pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(&self, cx: &mut App) -> Selection<D> {
self.all(cx).first().unwrap().clone()
pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
snapshot: &DisplaySnapshot,
) -> Selection<D> {
self.all(snapshot).first().unwrap().clone()
}
pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(&self, cx: &mut App) -> Selection<D> {
self.all(cx).last().unwrap().clone()
pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
snapshot: &DisplaySnapshot,
) -> Selection<D> {
self.all(snapshot).last().unwrap().clone()
}
/// Returns a list of (potentially backwards!) ranges representing the selections.
@ -362,9 +359,9 @@ impl SelectionsCollection {
#[cfg(any(test, feature = "test-support"))]
pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &mut App,
snapshot: &DisplaySnapshot,
) -> Vec<Range<D>> {
self.all::<D>(cx)
self.all::<D>(snapshot)
.iter()
.map(|s| {
if s.reversed {
@ -596,7 +593,8 @@ impl<'a> MutableSelectionsCollection<'a> {
where
T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
{
let mut selections = self.collection.all(self.cx);
let display_map = self.display_map();
let mut selections = self.collection.all(&display_map);
let mut start = range.start.to_offset(&self.buffer());
let mut end = range.end.to_offset(&self.buffer());
let reversed = if start > end {
@ -790,7 +788,7 @@ impl<'a> MutableSelectionsCollection<'a> {
) {
let mut changed = false;
let display_map = self.display_map();
let (_, selections) = self.collection.all_display(self.cx);
let selections = self.collection.all_display(&display_map);
let selections = selections
.into_iter()
.map(|selection| {
@ -814,9 +812,10 @@ impl<'a> MutableSelectionsCollection<'a> {
) {
let mut changed = false;
let snapshot = self.buffer().clone();
let display_map = self.display_map();
let selections = self
.collection
.all::<usize>(self.cx)
.all::<usize>(&display_map)
.into_iter()
.map(|selection| {
let mut moved_selection = selection.clone();

View file

@ -82,7 +82,7 @@ impl Editor {
if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) {
return false;
}
let newest_selection = self.selections.newest::<usize>(cx);
let newest_selection = self.selections.newest::<usize>(&self.display_snapshot(cx));
let head = newest_selection.head();
if !newest_selection.is_empty() && head != newest_selection.tail() {

View file

@ -14,7 +14,7 @@ impl Editor {
return Task::ready(None);
};
let (selection, buffer, editor_snapshot) = {
let selection = self.selections.newest_adjusted(cx);
let selection = self.selections.newest_adjusted(&self.display_snapshot(cx));
let Some((buffer, _)) = self
.buffer()
.read(cx)

View file

@ -108,7 +108,7 @@ pub fn assert_text_with_selections(
assert_eq!(editor.text(cx), unmarked_text, "text doesn't match");
let actual = generate_marked_text(
&editor.text(cx),
&editor.selections.ranges(cx),
&editor.selections.ranges(&editor.display_snapshot(cx)),
marked_text.contains("«"),
);
assert_eq!(actual, marked_text, "Selections don't match");

View file

@ -265,7 +265,10 @@ impl EditorTestContext {
pub fn pixel_position_for(&mut self, display_point: DisplayPoint) -> Point<Pixels> {
self.update_editor(|editor, window, cx| {
let newest_point = editor.selections.newest_display(cx).head();
let newest_point = editor
.selections
.newest_display(&editor.display_snapshot(cx))
.head();
let pixel_position = editor.pixel_position_of_newest_cursor.unwrap();
let line_height = editor
.style()
@ -590,7 +593,7 @@ impl EditorTestContext {
fn editor_selections(&mut self) -> Vec<Range<usize>> {
self.editor
.update(&mut self.cx, |editor, cx| {
editor.selections.all::<usize>(cx)
editor.selections.all::<usize>(&editor.display_snapshot(cx))
})
.into_iter()
.map(|s| {
@ -688,9 +691,12 @@ pub fn assert_state_with_diff(
expected_diff_text: &str,
) {
let (snapshot, selections) = editor.update_in(cx, |editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
(
editor.snapshot(window, cx).buffer_snapshot().clone(),
editor.selections.ranges::<usize>(cx),
snapshot.buffer_snapshot().clone(),
editor
.selections
.ranges::<usize>(&snapshot.display_snapshot),
)
});

View file

@ -490,7 +490,7 @@ async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
cx.executor().advance_clock(Duration::from_secs(2));
editor.update(cx, |editor, cx| {
let all_selections = editor.selections.all_adjusted(cx);
let all_selections = editor.selections.all_adjusted(&editor.display_snapshot(cx));
assert_eq!(
all_selections.len(),
1,
@ -565,7 +565,7 @@ async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) {
cx.executor().advance_clock(Duration::from_secs(2));
editor.update(cx, |editor, cx| {
let all_selections = editor.selections.all_adjusted(cx);
let all_selections = editor.selections.all_adjusted(&editor.display_snapshot(cx));
assert_eq!(
all_selections.len(),
1,

View file

@ -49,7 +49,7 @@ impl TextDiffView {
let selection_data = source_editor.update(cx, |editor, cx| {
let multibuffer = editor.buffer().read(cx);
let source_buffer = multibuffer.as_singleton()?;
let selections = editor.selections.all::<Point>(cx);
let selections = editor.selections.all::<Point>(&editor.display_snapshot(cx));
let buffer_snapshot = source_buffer.read(cx);
let first_selection = selections.first()?;
let max_point = buffer_snapshot.max_point();

View file

@ -74,7 +74,9 @@ impl GoToLine {
) -> Self {
let (user_caret, last_line, scroll_position) = active_editor.update(cx, |editor, cx| {
let user_caret = UserCaretPosition::at_selection_end(
&editor.selections.last::<Point>(cx),
&editor
.selections
.last::<Point>(&editor.display_snapshot(cx)),
&editor.buffer().read(cx).snapshot(cx),
);
@ -739,7 +741,7 @@ mod tests {
let selections = editor.update(cx, |editor, cx| {
editor
.selections
.all::<rope::Point>(cx)
.all::<rope::Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.start..s.end)
.collect::<Vec<_>>()

View file

@ -229,8 +229,11 @@ impl LspLogView {
log_view.editor.update(cx, |editor, cx| {
editor.set_read_only(false);
let last_offset = editor.buffer().read(cx).len(cx);
let newest_cursor_is_at_end =
editor.selections.newest::<usize>(cx).start >= last_offset;
let newest_cursor_is_at_end = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.start
>= last_offset;
editor.edit(
vec![
(last_offset..last_offset, text.as_str()),

View file

@ -252,7 +252,10 @@ impl SyntaxTreeView {
.editor
.update(cx, |editor, cx| editor.snapshot(window, 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>(&editor.display_snapshot(cx))
.range();
let multi_buffer = editor.buffer().read(cx);
let (buffer, range, excerpt_id) = snapshot
.buffer_snapshot()

View file

@ -278,8 +278,12 @@ impl MarkdownPreviewView {
this.parse_markdown_from_active_editor(true, window, cx);
}
EditorEvent::SelectionsChanged { .. } => {
let selection_range = editor
.update(cx, |editor, cx| editor.selections.last::<usize>(cx).range());
let selection_range = editor.update(cx, |editor, cx| {
editor
.selections
.last::<usize>(&editor.display_snapshot(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();

View file

@ -245,7 +245,10 @@ impl PickerDelegate for OutlineViewDelegate {
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::<usize>(cx).head();
let cursor_offset = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.head();
(buffer, cursor_offset)
});
selected_index = self
@ -673,7 +676,7 @@ mod tests {
let selections = editor.update(cx, |editor, cx| {
editor
.selections
.all::<rope::Point>(cx)
.all::<rope::Point>(&editor.display_snapshot(cx))
.into_iter()
.map(|s| s.start..s.end)
.collect::<Vec<_>>()

View file

@ -3099,7 +3099,10 @@ impl OutlinePanel {
cx: &mut Context<Self>,
) -> Option<PanelEntry> {
let selection = editor.update(cx, |editor, cx| {
editor.selections.newest::<language::Point>(cx).head()
editor
.selections
.newest::<language::Point>(&editor.display_snapshot(cx))
.head()
});
let editor_snapshot = editor.update(cx, |editor, cx| editor.snapshot(window, cx));
let multi_buffer = editor.read(cx).buffer();
@ -6957,13 +6960,13 @@ outline: struct OutlineEntryExcerpt
fn selected_row_text(editor: &Entity<Editor>, cx: &mut App) -> String {
editor.update(cx, |editor, cx| {
let selections = editor.selections.all::<language::Point>(cx);
assert_eq!(selections.len(), 1, "Active editor should have exactly one selection after any outline panel interactions");
let selection = selections.first().unwrap();
let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let line_start = language::Point::new(selection.start.row, 0);
let line_end = multi_buffer_snapshot.clip_point(language::Point::new(selection.end.row, u32::MAX), language::Bias::Right);
multi_buffer_snapshot.text_for_range(line_start..line_end).collect::<String>().trim().to_owned()
let selections = editor.selections.all::<language::Point>(&editor.display_snapshot(cx));
assert_eq!(selections.len(), 1, "Active editor should have exactly one selection after any outline panel interactions");
let selection = selections.first().unwrap();
let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let line_start = language::Point::new(selection.start.row, 0);
let line_end = multi_buffer_snapshot.clip_point(language::Point::new(selection.end.row, u32::MAX), language::Bias::Right);
multi_buffer_snapshot.text_for_range(line_start..line_end).collect::<String>().trim().to_owned()
})
}

View file

@ -657,7 +657,7 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) {
let confirm = panel.update_in(cx, |panel, window, cx| {
panel.filename_editor.update(cx, |editor, cx| {
let file_name_selections = editor.selections.all::<usize>(cx);
let file_name_selections = editor.selections.all::<usize>(&editor.display_snapshot(cx));
assert_eq!(
file_name_selections.len(),
1,
@ -731,7 +731,7 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) {
panel.update_in(cx, |panel, window, cx| {
panel.filename_editor.update(cx, |editor, cx| {
let file_name_selections = editor.selections.all::<usize>(cx);
let file_name_selections = editor.selections.all::<usize>(&editor.display_snapshot(cx));
assert_eq!(file_name_selections.len(), 1, "File editing should have a single selection, but got: {file_name_selections:?}");
let file_name_selection = &file_name_selections[0];
assert_eq!(file_name_selection.start, 0, "Should select the file name from the start");
@ -1214,7 +1214,7 @@ async fn test_copy_paste(cx: &mut gpui::TestAppContext) {
panel.update_in(cx, |panel, window, cx| {
panel.filename_editor.update(cx, |editor, cx| {
let file_name_selections = editor.selections.all::<usize>(cx);
let file_name_selections = editor.selections.all::<usize>(&editor.display_snapshot(cx));
assert_eq!(
file_name_selections.len(),
1,

View file

@ -85,7 +85,11 @@ pub fn run(
let editor = editor.upgrade().context("editor was dropped")?;
let selected_range = editor
.update(cx, |editor, cx| editor.selections.newest_adjusted(cx))
.update(cx, |editor, cx| {
editor
.selections
.newest_adjusted(&editor.display_snapshot(cx))
})
.range();
let multibuffer = editor.read(cx).buffer().clone();
let Some(buffer) = multibuffer.read(cx).as_singleton() else {
@ -473,7 +477,9 @@ fn language_supported(language: &Arc<Language>, cx: &mut App) -> bool {
fn get_language(editor: WeakEntity<Editor>, cx: &mut App) -> Option<Arc<Language>> {
editor
.update(cx, |editor, cx| {
let selection = editor.selections.newest::<usize>(cx);
let selection = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
let buffer = editor.buffer().read(cx).snapshot(cx);
buffer.language_at(selection.head()).cloned()
})

View file

@ -50,7 +50,8 @@ impl Vim {
pub(crate) fn push_to_change_list(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some((new_positions, buffer)) = self.update_editor(cx, |vim, editor, cx| {
let (map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
let buffer = editor.buffer().clone();
let pop_state = editor
@ -59,7 +60,7 @@ impl Vim {
.map(|previous| {
previous.len() == selections.len()
&& previous.iter().enumerate().all(|(ix, p)| {
p.to_display_point(&map).row() == selections[ix].head().row()
p.to_display_point(&display_map).row() == selections[ix].head().row()
})
})
.unwrap_or(false);
@ -68,11 +69,11 @@ impl Vim {
.into_iter()
.map(|s| {
let point = if vim.mode == Mode::Insert {
movement::saturating_left(&map, s.head())
movement::saturating_left(&display_map, s.head())
} else {
s.head()
};
map.display_point_to_anchor(point, Bias::Left)
display_map.display_point_to_anchor(point, Bias::Left)
})
.collect::<Vec<_>>();

View file

@ -606,7 +606,9 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
let result = vim.update_editor(cx, |vim, editor, cx| {
let snapshot = editor.snapshot(window, cx);
let buffer_row = action.range.head().buffer_row(vim, editor, window, cx)?;
let current = editor.selections.newest::<Point>(cx);
let current = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx));
let target = snapshot
.buffer_snapshot()
.clip_point(Point::new(buffer_row.0, current.head().column), Bias::Left);
@ -1903,7 +1905,9 @@ impl OnMatchingLines {
});
window.dispatch_action(action, cx);
cx.defer_in(window, move |editor, window, cx| {
let newest = editor.selections.newest::<Point>(cx);
let newest = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx));
editor.change_selections(
SelectionEffects::no_scroll(),
window,
@ -2000,7 +2004,9 @@ impl Vim {
};
let command = self.update_editor(cx, |_, editor, cx| {
let snapshot = editor.snapshot(window, cx);
let start = editor.selections.newest_display(cx);
let start = editor
.selections
.newest_display(&editor.display_snapshot(cx));
let text_layout_details = editor.text_layout_details(window);
let (mut range, _) = motion
.range(
@ -2047,7 +2053,9 @@ impl Vim {
};
let command = self.update_editor(cx, |_, editor, cx| {
let snapshot = editor.snapshot(window, cx);
let start = editor.selections.newest_display(cx);
let start = editor
.selections
.newest_display(&editor.display_snapshot(cx));
let range = object
.range(&snapshot, start.clone(), around, None)
.unwrap_or(start.range());
@ -2156,7 +2164,11 @@ impl ShellExec {
Point::new(range.start.0, 0)
..snapshot.clip_point(Point::new(range.end.0 + 1, 0), Bias::Right)
} else {
let mut end = editor.selections.newest::<Point>(cx).range().end;
let mut end = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx))
.range()
.end;
end = snapshot.clip_point(Point::new(end.row + 1, 0), Bias::Right);
needs_newline_prefix = end == snapshot.max_point();
end..end

View file

@ -345,7 +345,7 @@ impl Vim {
self.update_editor(cx, |vim, editor, cx| {
let has_selection = editor
.selections
.all_adjusted(cx)
.all_adjusted(&editor.display_snapshot(cx))
.iter()
.any(|selection| !selection.is_empty());
@ -478,19 +478,20 @@ impl Vim {
pub fn helix_replace(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
let (map, selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_display(&display_map);
// Store selection info for positioning after edit
let selection_info: Vec<_> = selections
.iter()
.map(|selection| {
let range = selection.range();
let start_offset = range.start.to_offset(&map, Bias::Left);
let end_offset = range.end.to_offset(&map, Bias::Left);
let start_offset = range.start.to_offset(&display_map, Bias::Left);
let end_offset = range.end.to_offset(&display_map, Bias::Left);
let was_empty = range.is_empty();
let was_reversed = selection.reversed;
(
map.buffer_snapshot().anchor_before(start_offset),
display_map.buffer_snapshot().anchor_before(start_offset),
end_offset - start_offset,
was_empty,
was_reversed,
@ -504,11 +505,11 @@ impl Vim {
// For empty selections, extend to replace one character
if range.is_empty() {
range.end = movement::saturating_right(&map, range.start);
range.end = movement::saturating_right(&display_map, range.start);
}
let byte_range = range.start.to_offset(&map, Bias::Left)
..range.end.to_offset(&map, Bias::Left);
let byte_range = range.start.to_offset(&display_map, Bias::Left)
..range.end.to_offset(&display_map, Bias::Left);
if !byte_range.is_empty() {
let replacement_text = text.repeat(byte_range.len());
@ -568,7 +569,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
editor.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
let display_map = editor.display_map.update(cx, |map, cx| map.snapshot(cx));
let mut selections = editor.selections.all::<Point>(cx);
let mut selections = editor.selections.all::<Point>(&display_map);
let max_point = display_map.buffer_snapshot().max_point();
let buffer_snapshot = &display_map.buffer_snapshot();
@ -606,7 +607,9 @@ impl Vim {
cx: &mut Context<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
let newest = editor.selections.newest::<usize>(cx);
let newest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
editor.change_selections(Default::default(), window, cx, |s| s.select(vec![newest]));
});
}
@ -633,7 +636,10 @@ impl Vim {
if yank {
vim.copy_selections_content(editor, MotionKind::Exclusive, window, cx);
}
let selections = editor.selections.all::<Point>(cx).into_iter();
let selections = editor
.selections
.all::<Point>(&editor.display_snapshot(cx))
.into_iter();
let edits = selections.map(|selection| (selection.start..selection.end, ""));
editor.edit(edits, cx);
});

View file

@ -56,7 +56,8 @@ impl Vim {
let times = times.unwrap_or(1);
self.update_editor(cx, |_, editor, cx| {
let mut selections = Vec::new();
let (map, mut original_selections) = editor.selections.all_display(cx);
let map = editor.display_snapshot(cx);
let mut original_selections = editor.selections.all_display(&map);
// The order matters, because it is recorded when the selections are added.
if above {
original_selections.reverse();

View file

@ -44,7 +44,8 @@ impl Vim {
return;
};
let (display_map, current_selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let current_selections = editor.selections.all_adjusted_display(&display_map);
// The clipboard can have multiple selections, and there can
// be multiple selections. Helix zips them together, so the first

View file

@ -84,7 +84,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let mut edits = Vec::new();
for selection in editor.selections.all::<Point>(cx) {
for selection in editor.selections.all::<Point>(&editor.display_snapshot(cx)) {
let point = selection.head();
let new_row = match direction {
Direction::Next => point.row + 1,

View file

@ -657,7 +657,7 @@ impl Vim {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
let selections = editor.selections.all::<Point>(cx);
let selections = editor.selections.all::<Point>(&editor.display_snapshot(cx));
let snapshot = editor.buffer().read(cx).snapshot(cx);
let selection_start_rows: BTreeSet<u32> = selections
@ -699,7 +699,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let selections = editor.selections.all::<Point>(cx);
let selections = editor.selections.all::<Point>(&editor.display_snapshot(cx));
let snapshot = editor.buffer().read(cx).snapshot(cx);
let selection_end_rows: BTreeSet<u32> = selections
@ -745,7 +745,7 @@ impl Vim {
Vim::take_forced_motion(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, _, cx| {
let selections = editor.selections.all::<Point>(cx);
let selections = editor.selections.all::<Point>(&editor.display_snapshot(cx));
let selection_start_rows: BTreeSet<u32> = selections
.into_iter()
@ -774,9 +774,10 @@ impl Vim {
Vim::take_forced_motion(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
let selections = editor.selections.all::<Point>(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all::<Point>(&display_map);
let snapshot = editor.buffer().read(cx).snapshot(cx);
let (_map, display_selections) = editor.selections.all_display(cx);
let display_selections = editor.selections.all_display(&display_map);
let original_positions = display_selections
.iter()
.map(|s| (s.id, s.head()))
@ -937,13 +938,14 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (map, display_selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let display_selections = editor.selections.all_display(&display_map);
let mut edits = Vec::new();
let mut edits = Vec::with_capacity(display_selections.len());
for selection in &display_selections {
let mut range = selection.range();
for _ in 0..count {
let new_point = movement::saturating_right(&map, range.end);
let new_point = movement::saturating_right(&display_map, range.end);
if range.end == new_point {
return;
}
@ -951,8 +953,8 @@ impl Vim {
}
edits.push((
range.start.to_offset(&map, Bias::Left)
..range.end.to_offset(&map, Bias::Left),
range.start.to_offset(&display_map, Bias::Left)
..range.end.to_offset(&display_map, Bias::Left),
text.repeat(if is_return_char { 0 } else { count }),
));
}
@ -976,16 +978,16 @@ impl Vim {
pub fn save_selection_starts(
&self,
editor: &Editor,
cx: &mut Context<Editor>,
) -> HashMap<usize, Anchor> {
let (map, selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_display(&display_map);
selections
.iter()
.map(|selection| {
(
selection.id,
map.display_point_to_anchor(selection.start, Bias::Right),
display_map.display_point_to_anchor(selection.start, Bias::Right),
)
})
.collect::<HashMap<_, _>>()

View file

@ -199,7 +199,7 @@ impl Vim {
let mut ranges = Vec::new();
let mut cursor_positions = Vec::new();
let snapshot = editor.buffer().read(cx).snapshot(cx);
for selection in editor.selections.all_adjusted(cx) {
for selection in editor.selections.all_adjusted(&editor.display_snapshot(cx)) {
match vim.mode {
Mode::Visual | Mode::VisualLine => {
ranges.push(selection.start..selection.end);

View file

@ -58,7 +58,7 @@ impl Vim {
let mut new_anchors = Vec::new();
let snapshot = editor.buffer().read(cx).snapshot(cx);
for selection in editor.selections.all_adjusted(cx) {
for selection in editor.selections.all_adjusted(&editor.display_snapshot(cx)) {
if !selection.is_empty()
&& (vim.mode != Mode::VisualBlock || new_anchors.is_empty())
{

View file

@ -50,16 +50,19 @@ impl Vim {
let mut reversed = vec![];
self.update_editor(cx, |vim, editor, cx| {
let (map, selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_display(&display_map);
for selection in selections {
let end = movement::saturating_left(&map, selection.end);
let end = movement::saturating_left(&display_map, selection.end);
ends.push(
map.buffer_snapshot()
.anchor_before(end.to_offset(&map, Bias::Left)),
display_map
.buffer_snapshot()
.anchor_before(end.to_offset(&display_map, Bias::Left)),
);
starts.push(
map.buffer_snapshot()
.anchor_before(selection.start.to_offset(&map, Bias::Left)),
display_map
.buffer_snapshot()
.anchor_before(selection.start.to_offset(&display_map, Bias::Left)),
);
reversed.push(selection.reversed)
}
@ -301,19 +304,21 @@ impl Vim {
name = "'";
}
if matches!(name, "{" | "}" | "(" | ")") {
let (map, selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_display(&display_map);
let anchors = selections
.into_iter()
.map(|selection| {
let point = match name {
"{" => movement::start_of_paragraph(&map, selection.head(), 1),
"}" => movement::end_of_paragraph(&map, selection.head(), 1),
"(" => motion::sentence_backwards(&map, selection.head(), 1),
")" => motion::sentence_forwards(&map, selection.head(), 1),
"{" => movement::start_of_paragraph(&display_map, selection.head(), 1),
"}" => movement::end_of_paragraph(&display_map, selection.head(), 1),
"(" => motion::sentence_backwards(&display_map, selection.head(), 1),
")" => motion::sentence_forwards(&display_map, selection.head(), 1),
_ => unreachable!(),
};
map.buffer_snapshot()
.anchor_before(point.to_offset(&map, Bias::Left))
display_map
.buffer_snapshot()
.anchor_before(point.to_offset(&display_map, Bias::Left))
})
.collect::<Vec<Anchor>>();
return Some(Mark::Local(anchors));

View file

@ -56,7 +56,8 @@ impl Vim {
vim.copy_selections_content(editor, MotionKind::for_mode(vim.mode), window, cx);
}
let (display_map, current_selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let current_selections = editor.selections.all_adjusted_display(&display_map);
// unlike zed, if you have a multi-cursor selection from vim block mode,
// pasting it will paste it on subsequent lines, even if you don't yet
@ -173,7 +174,7 @@ impl Vim {
original_indent_columns.push(original_indent_column);
}
let cursor_offset = editor.selections.last::<usize>(cx).head();
let cursor_offset = editor.selections.last::<usize>(&display_map).head();
if editor
.buffer()
.read(cx)

View file

@ -363,7 +363,10 @@ mod test {
point(0., 3.0)
);
assert_eq!(
editor.selections.newest(cx).range(),
editor
.selections
.newest(&editor.display_snapshot(cx))
.range(),
Point::new(6, 0)..Point::new(6, 0)
)
});
@ -380,7 +383,10 @@ mod test {
point(0., 3.0)
);
assert_eq!(
editor.selections.newest(cx).range(),
editor
.selections
.newest(&editor.display_snapshot(cx))
.range(),
Point::new(0, 0)..Point::new(6, 1)
)
});

View file

@ -94,7 +94,10 @@ impl Vim {
MotionKind::Exclusive
};
vim.copy_selections_content(editor, kind, window, cx);
let selections = editor.selections.all::<Point>(cx).into_iter();
let selections = editor
.selections
.all::<Point>(&editor.display_snapshot(cx))
.into_iter();
let edits = selections.map(|selection| (selection.start..selection.end, ""));
editor.edit(edits, cx);
});

View file

@ -106,7 +106,7 @@ impl Vim {
true,
editor
.selections
.all_adjusted(cx)
.all_adjusted(&editor.display_snapshot(cx))
.iter()
.map(|s| s.range())
.collect(),
@ -128,7 +128,7 @@ impl Vim {
false,
editor
.selections
.all_adjusted(cx)
.all_adjusted(&editor.display_snapshot(cx))
.iter()
.map(|s| s.range())
.collect(),

View file

@ -53,7 +53,7 @@ impl Vim {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let map = editor.snapshot(window, cx);
let display_selections = editor.selections.all::<Point>(cx);
let display_selections = editor.selections.all::<Point>(&map.display_snapshot);
// Handles all string that require manipulation, including inserts and replaces
let edits = display_selections
@ -98,7 +98,7 @@ impl Vim {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let map = editor.snapshot(window, cx);
let selections = editor.selections.all::<Point>(cx);
let selections = editor.selections.all::<Point>(&map.display_snapshot);
let mut new_selections = vec![];
let edits: Vec<(Range<Point>, String)> = selections
.into_iter()
@ -150,7 +150,9 @@ impl Vim {
self.stop_recording(cx);
self.update_editor(cx, |vim, editor, cx| {
editor.set_clip_at_line_ends(false, cx);
let mut selection = editor.selections.newest_display(cx);
let mut selection = editor
.selections
.newest_display(&editor.display_snapshot(cx));
let snapshot = editor.snapshot(window, cx);
object.expand_selection(&snapshot, &mut selection, around, None);
let start = snapshot
@ -196,7 +198,9 @@ impl Vim {
self.update_editor(cx, |vim, editor, cx| {
editor.set_clip_at_line_ends(false, cx);
let text_layout_details = editor.text_layout_details(window);
let mut selection = editor.selections.newest_display(cx);
let mut selection = editor
.selections
.newest_display(&editor.display_snapshot(cx));
let snapshot = editor.snapshot(window, cx);
motion.expand_selection(
&snapshot,

View file

@ -863,7 +863,9 @@ impl VimGlobals {
}
}
'%' => editor.and_then(|editor| {
let selection = editor.selections.newest::<Point>(cx);
let selection = editor
.selections
.newest::<Point>(&editor.display_snapshot(cx));
if let Some((_, buffer, _)) = editor
.buffer()
.read(cx)

View file

@ -45,7 +45,8 @@ impl Vim {
},
};
let surround = pair.end != surround_alias((*text).as_ref());
let (display_map, display_selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let display_selections = editor.selections.all_adjusted_display(&display_map);
let mut edits = Vec::new();
let mut anchors = Vec::new();
@ -144,7 +145,8 @@ impl Vim {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (display_map, display_selections) = editor.selections.all_display(cx);
let display_map = editor.display_snapshot(cx);
let display_selections = editor.selections.all_display(&display_map);
let mut edits = Vec::new();
let mut anchors = Vec::new();
@ -256,7 +258,8 @@ impl Vim {
let preserve_space =
will_replace_pair.start == will_replace_pair.end || !opening;
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
let mut edits = Vec::new();
let mut anchors = Vec::new();
@ -382,7 +385,8 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
let mut anchors = Vec::new();
for selection in &selections {
@ -500,7 +504,8 @@ impl Vim {
let mut min_range_size = usize::MAX;
let _ = self.editor.update(cx, |editor, cx| {
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
// Even if there's multiple cursors, we'll simply rely on
// the first one to understand what bracket pair to map to.
// I believe we could, if worth it, go one step above and

View file

@ -2295,7 +2295,10 @@ async fn test_clipping_on_mode_change(cx: &mut gpui::TestAppContext) {
let mut pixel_position = cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let current_head = editor.selections.newest_display(cx).end;
let current_head = editor
.selections
.newest_display(&snapshot.display_snapshot)
.end;
editor.last_bounds().unwrap().origin
+ editor
.display_to_pixel_point(current_head, &snapshot, window)

View file

@ -1359,7 +1359,10 @@ impl Vim {
return;
};
let newest_selection_empty = editor.update(cx, |editor, cx| {
editor.selections.newest::<usize>(cx).is_empty()
editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.is_empty()
});
let editor = editor.read(cx);
let editor_mode = editor.mode();
@ -1455,9 +1458,11 @@ impl Vim {
cx: &mut Context<Self>,
) -> Option<String> {
self.update_editor(cx, |_, editor, cx| {
let selection = editor.selections.newest::<usize>(cx);
let snapshot = &editor.snapshot(window, cx);
let selection = editor
.selections
.newest::<usize>(&snapshot.display_snapshot);
let snapshot = editor.snapshot(window, cx);
let snapshot = snapshot.buffer_snapshot();
let (range, kind) =
snapshot.surrounding_word(selection.start, Some(CharScopeContext::Completion));
@ -1484,9 +1489,11 @@ impl Vim {
let selections = self.editor().map(|editor| {
editor.update(cx, |editor, cx| {
let snapshot = editor.display_snapshot(cx);
(
editor.selections.oldest::<Point>(cx),
editor.selections.newest::<Point>(cx),
editor.selections.oldest::<Point>(&snapshot),
editor.selections.newest::<Point>(&snapshot),
)
})
});

View file

@ -747,7 +747,8 @@ impl Vim {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let display_map = editor.display_snapshot(cx);
let selections = editor.selections.all_adjusted_display(&display_map);
// Selections are biased right at the start. So we need to store
// anchors that are biased left so that we can restore the selections
@ -858,7 +859,9 @@ impl Vim {
});
}
self.update_editor(cx, |_, editor, cx| {
let latest = editor.selections.newest::<usize>(cx);
let latest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
start_selection = latest.start;
end_selection = latest.end;
});
@ -879,7 +882,9 @@ impl Vim {
return;
}
self.update_editor(cx, |_, editor, cx| {
let latest = editor.selections.newest::<usize>(cx);
let latest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
if vim_is_normal {
start_selection = latest.start;
end_selection = latest.end;

View file

@ -54,7 +54,8 @@ impl QuickActionBar {
.count()
.ne(&0)
.then(|| {
let latest = this.selections.newest_display(cx);
let snapshot = this.display_snapshot(cx);
let latest = this.selections.newest_display(&snapshot);
!latest.is_empty()
})
.unwrap_or_default()