Remove `into SelectionEffects` from .change_selections (#33554)

Conrad Irwin created

In #32656 I generalized the argument to change selections to allow
controling both the scroll and the nav history (and the completion
trigger).

To avoid conflicting with ongoing debugger cherry-picks I left the
argument as an `impl Into<>`, but I think it's clearer to make callers
specify what they want here.

I converted a lot of `None` arguments to `SelectionEffects::no_scroll()`
to be exactly compatible; but I think many people used none as an "i
don't care" value in which case Default::default() might be more
appropraite

Closes #ISSUE

Release Notes:

- N/A

Change summary

crates/agent_ui/src/active_thread.rs                            |  24 
crates/agent_ui/src/agent_diff.rs                               |  25 
crates/agent_ui/src/inline_assistant.rs                         |   3 
crates/agent_ui/src/text_thread_editor.rs                       |  13 
crates/assistant_tools/src/edit_file_tool.rs                    |   4 
crates/collab/src/tests/channel_buffer_tests.rs                 |  10 
crates/collab/src/tests/editor_tests.rs                         |  40 
crates/collab/src/tests/following_tests.rs                      |  22 
crates/collab_ui/src/channel_view.rs                            |  17 
crates/copilot/src/copilot_completion_provider.rs               |  13 
crates/debugger_ui/src/stack_trace_view.rs                      |   9 
crates/diagnostics/src/diagnostic_renderer.rs                   |   3 
crates/diagnostics/src/diagnostics.rs                           |   3 
crates/editor/src/editor.rs                                     | 287 +-
crates/editor/src/editor_tests.rs                               | 278 +-
crates/editor/src/element.rs                                    |  18 
crates/editor/src/hover_links.rs                                |   2 
crates/editor/src/hover_popover.rs                              |   4 
crates/editor/src/inlay_hint_cache.rs                           |  81 
crates/editor/src/items.rs                                      |   6 
crates/editor/src/jsx_tag_auto_close.rs                         |   2 
crates/editor/src/mouse_context_menu.rs                         |   6 
crates/editor/src/proposed_changes_editor.rs                    |   6 
crates/editor/src/test.rs                                       |   6 
crates/editor/src/test/editor_test_context.rs                   |   6 
crates/git_ui/src/commit_view.rs                                |   4 
crates/git_ui/src/project_diff.rs                               |  15 
crates/go_to_line/src/go_to_line.rs                             |  13 
crates/inline_completion_button/src/inline_completion_button.rs |  13 
crates/journal/src/journal.rs                                   |  11 
crates/language_tools/src/syntax_tree_view.rs                   |   4 
crates/markdown_preview/src/markdown_preview_view.rs            |  11 
crates/outline/src/outline.rs                                   |  11 
crates/outline_panel/src/outline_panel.rs                       |   6 
crates/picker/src/picker.rs                                     |  11 
crates/project_panel/src/project_panel.rs                       |   4 
crates/project_symbols/src/project_symbols.rs                   |  11 
crates/repl/src/session.rs                                      |   3 
crates/rules_library/src/rules_library.rs                       |  28 
crates/search/src/buffer_search.rs                              |  21 
crates/search/src/project_search.rs                             |  10 
crates/tasks_ui/src/modal.rs                                    |   4 
crates/tasks_ui/src/tasks_ui.rs                                 |   4 
crates/vim/src/change_list.rs                                   |   4 
crates/vim/src/command.rs                                       |  28 
crates/vim/src/helix.rs                                         |  12 
crates/vim/src/indent.rs                                        |   9 
crates/vim/src/insert.rs                                        |   4 
crates/vim/src/motion.rs                                        |   3 
crates/vim/src/normal.rs                                        |  23 
crates/vim/src/normal/change.rs                                 |   5 
crates/vim/src/normal/convert.rs                                |  12 
crates/vim/src/normal/delete.rs                                 |   9 
crates/vim/src/normal/increment.rs                              |   4 
crates/vim/src/normal/mark.rs                                   |   7 
crates/vim/src/normal/paste.rs                                  |  12 
crates/vim/src/normal/substitute.rs                             |   4 
crates/vim/src/normal/toggle_comments.rs                        |  10 
crates/vim/src/normal/yank.rs                                   |  10 
crates/vim/src/replace.rs                                       |  10 
crates/vim/src/rewrap.rs                                        |  12 
crates/vim/src/surrounds.rs                                     |  10 
crates/vim/src/vim.rs                                           |  53 
crates/vim/src/visual.rs                                        |  35 
crates/zed/src/zed.rs                                           |  20 
65 files changed, 777 insertions(+), 591 deletions(-)

Detailed changes

crates/agent_ui/src/active_thread.rs 🔗

@@ -19,7 +19,7 @@ use audio::{Audio, Sound};
 use collections::{HashMap, HashSet};
 use editor::actions::{MoveUp, Paste};
 use editor::scroll::Autoscroll;
-use editor::{Editor, EditorElement, EditorEvent, EditorStyle, MultiBuffer};
+use editor::{Editor, EditorElement, EditorEvent, EditorStyle, MultiBuffer, SelectionEffects};
 use gpui::{
     AbsoluteLength, Animation, AnimationExt, AnyElement, App, ClickEvent, ClipboardEntry,
     ClipboardItem, DefiniteLength, EdgesRefinement, Empty, Entity, EventEmitter, Focusable, Hsla,
@@ -689,9 +689,12 @@ fn open_markdown_link(
                             })
                             .context("Could not find matching symbol")?;
 
-                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                            s.select_anchor_ranges([symbol_range.start..symbol_range.start])
-                        });
+                        editor.change_selections(
+                            SelectionEffects::scroll(Autoscroll::center()),
+                            window,
+                            cx,
+                            |s| s.select_anchor_ranges([symbol_range.start..symbol_range.start]),
+                        );
                         anyhow::Ok(())
                     })
                 })
@@ -708,10 +711,15 @@ fn open_markdown_link(
                         .downcast::<Editor>()
                         .context("Item is not an editor")?;
                     active_editor.update_in(cx, |editor, window, cx| {
-                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                            s.select_ranges([Point::new(line_range.start as u32, 0)
-                                ..Point::new(line_range.start as u32, 0)])
-                        });
+                        editor.change_selections(
+                            SelectionEffects::scroll(Autoscroll::center()),
+                            window,
+                            cx,
+                            |s| {
+                                s.select_ranges([Point::new(line_range.start as u32, 0)
+                                    ..Point::new(line_range.start as u32, 0)])
+                            },
+                        );
                         anyhow::Ok(())
                     })
                 })

crates/agent_ui/src/agent_diff.rs 🔗

@@ -5,7 +5,8 @@ use anyhow::Result;
 use buffer_diff::DiffHunkStatus;
 use collections::{HashMap, HashSet};
 use editor::{
-    Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot, ToPoint,
+    Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot,
+    SelectionEffects, ToPoint,
     actions::{GoToHunk, GoToPreviousHunk},
     scroll::Autoscroll,
 };
@@ -171,15 +172,9 @@ impl AgentDiffPane {
 
                     if let Some(first_hunk) = first_hunk {
                         let first_hunk_start = first_hunk.multi_buffer_range().start;
-                        editor.change_selections(
-                            Some(Autoscroll::fit()),
-                            window,
-                            cx,
-                            |selections| {
-                                selections
-                                    .select_anchor_ranges([first_hunk_start..first_hunk_start]);
-                            },
-                        )
+                        editor.change_selections(Default::default(), window, cx, |selections| {
+                            selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
+                        })
                     }
                 }
 
@@ -242,7 +237,7 @@ impl AgentDiffPane {
 
                 if let Some(first_hunk) = first_hunk {
                     let first_hunk_start = first_hunk.multi_buffer_range().start;
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
+                    editor.change_selections(Default::default(), window, cx, |selections| {
                         selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
                     })
                 }
@@ -416,7 +411,7 @@ fn update_editor_selection(
     };
 
     if let Some(target_hunk) = target_hunk {
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
+        editor.change_selections(Default::default(), window, cx, |selections| {
             let next_hunk_start = target_hunk.multi_buffer_range().start;
             selections.select_anchor_ranges([next_hunk_start..next_hunk_start]);
         })
@@ -1544,7 +1539,7 @@ impl AgentDiff {
                             let first_hunk_start = first_hunk.multi_buffer_range().start;
 
                             editor.change_selections(
-                                Some(Autoscroll::center()),
+                                SelectionEffects::scroll(Autoscroll::center()),
                                 window,
                                 cx,
                                 |selections| {
@@ -1868,7 +1863,7 @@ mod tests {
 
         // Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
             });
         });
@@ -2124,7 +2119,7 @@ mod tests {
 
         // Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
         editor1.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
             });
         });

crates/agent_ui/src/inline_assistant.rs 🔗

@@ -18,6 +18,7 @@ use agent_settings::AgentSettings;
 use anyhow::{Context as _, Result};
 use client::telemetry::Telemetry;
 use collections::{HashMap, HashSet, VecDeque, hash_map};
+use editor::SelectionEffects;
 use editor::{
     Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange,
     MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,
@@ -1159,7 +1160,7 @@ impl InlineAssistant {
 
         let position = assist.range.start;
         editor.update(cx, |editor, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_anchor_ranges([position..position])
             });
 

crates/agent_ui/src/text_thread_editor.rs 🔗

@@ -21,7 +21,6 @@ use editor::{
         BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId,
         RenderBlock, ToDisplayPoint,
     },
-    scroll::Autoscroll,
 };
 use editor::{FoldPlaceholder, display_map::CreaseId};
 use fs::Fs;
@@ -389,7 +388,7 @@ impl TextThreadEditor {
                 cursor..cursor
             };
             self.editor.update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
+                editor.change_selections(Default::default(), window, cx, |selections| {
                     selections.select_ranges([new_selection])
                 });
             });
@@ -449,8 +448,7 @@ impl TextThreadEditor {
         if let Some(command) = self.slash_commands.command(name, cx) {
             self.editor.update(cx, |editor, cx| {
                 editor.transact(window, cx, |editor, window, cx| {
-                    editor
-                        .change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel());
+                    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();
                     if newest_cursor.column > 0
@@ -1583,7 +1581,7 @@ impl TextThreadEditor {
 
             self.editor.update(cx, |editor, cx| {
                 editor.transact(window, cx, |this, window, cx| {
-                    this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    this.change_selections(Default::default(), window, cx, |s| {
                         s.select(selections);
                     });
                     this.insert("", window, cx);
@@ -3141,6 +3139,7 @@ pub fn make_lsp_adapter_delegate(
 #[cfg(test)]
 mod tests {
     use super::*;
+    use editor::SelectionEffects;
     use fs::FakeFs;
     use gpui::{App, TestAppContext, VisualTestContext};
     use indoc::indoc;
@@ -3366,7 +3365,9 @@ mod tests {
     ) {
         context_editor.update_in(cx, |context_editor, window, cx| {
             context_editor.editor.update(cx, |editor, cx| {
-                editor.change_selections(None, window, cx, |s| s.select_ranges([range]));
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                    s.select_ranges([range])
+                });
             });
 
             context_editor.copy(&Default::default(), window, cx);

crates/assistant_tools/src/edit_file_tool.rs 🔗

@@ -10,7 +10,7 @@ use assistant_tool::{
     ToolUseStatus,
 };
 use buffer_diff::{BufferDiff, BufferDiffSnapshot};
-use editor::{Editor, EditorMode, MinimapVisibility, MultiBuffer, PathKey, scroll::Autoscroll};
+use editor::{Editor, EditorMode, MinimapVisibility, MultiBuffer, PathKey};
 use futures::StreamExt;
 use gpui::{
     Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Entity, Task,
@@ -823,7 +823,7 @@ impl ToolCard for EditFileToolCard {
                                                         let first_hunk_start =
                                                             first_hunk.multi_buffer_range().start;
                                                         editor.change_selections(
-                                                            Some(Autoscroll::fit()),
+                                                            Default::default(),
                                                             window,
                                                             cx,
                                                             |selections| {

crates/collab/src/tests/channel_buffer_tests.rs 🔗

@@ -178,7 +178,7 @@ async fn test_channel_notes_participant_indices(
     channel_view_a.update_in(cx_a, |notes, window, cx| {
         notes.editor.update(cx, |editor, cx| {
             editor.insert("a", window, cx);
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(Default::default(), window, cx, |selections| {
                 selections.select_ranges(vec![0..1]);
             });
         });
@@ -188,7 +188,7 @@ async fn test_channel_notes_participant_indices(
         notes.editor.update(cx, |editor, cx| {
             editor.move_down(&Default::default(), window, cx);
             editor.insert("b", window, cx);
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(Default::default(), window, cx, |selections| {
                 selections.select_ranges(vec![1..2]);
             });
         });
@@ -198,7 +198,7 @@ async fn test_channel_notes_participant_indices(
         notes.editor.update(cx, |editor, cx| {
             editor.move_down(&Default::default(), window, cx);
             editor.insert("c", window, cx);
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(Default::default(), window, cx, |selections| {
                 selections.select_ranges(vec![2..3]);
             });
         });
@@ -273,12 +273,12 @@ async fn test_channel_notes_participant_indices(
         .unwrap();
 
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |selections| {
+        editor.change_selections(Default::default(), window, cx, |selections| {
             selections.select_ranges(vec![0..1]);
         });
     });
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |selections| {
+        editor.change_selections(Default::default(), window, cx, |selections| {
             selections.select_ranges(vec![2..3]);
         });
     });

crates/collab/src/tests/editor_tests.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 };
 use call::ActiveCall;
 use editor::{
-    DocumentColorsRenderMode, Editor, EditorSettings, RowInfo,
+    DocumentColorsRenderMode, Editor, EditorSettings, RowInfo, SelectionEffects,
     actions::{
         ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst,
         ExpandMacroRecursively, MoveToEnd, Redo, Rename, SelectAll, ToggleCodeActions, Undo,
@@ -348,7 +348,9 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
 
     // Type a completion trigger character as the guest.
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13])
+        });
         editor.handle_input(".", window, cx);
     });
     cx_b.focus(&editor_b);
@@ -461,7 +463,9 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
     // Now we do a second completion, this time to ensure that documentation/snippets are
     // resolved
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([46..46]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([46..46])
+        });
         editor.handle_input("; a", window, cx);
         editor.handle_input(".", window, cx);
     });
@@ -613,7 +617,7 @@ async fn test_collaborating_with_code_actions(
 
     // Move cursor to a location that contains code actions.
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
         });
     });
@@ -817,7 +821,9 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // Move cursor to a location that can be renamed.
     let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([7..7]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([7..7])
+        });
         editor.rename(&Rename, window, cx).unwrap()
     });
 
@@ -863,7 +869,9 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
         editor.cancel(&editor::actions::Cancel, window, cx);
     });
     let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([7..8]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([7..8])
+        });
         editor.rename(&Rename, window, cx).unwrap()
     });
 
@@ -1364,7 +1372,9 @@ async fn test_on_input_format_from_host_to_guest(
     // Type a on type formatting trigger character as the guest.
     cx_a.focus(&editor_a);
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13])
+        });
         editor.handle_input(">", window, cx);
     });
 
@@ -1460,7 +1470,9 @@ async fn test_on_input_format_from_guest_to_host(
     // Type a on type formatting trigger character as the guest.
     cx_b.focus(&editor_b);
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13])
+        });
         editor.handle_input(":", window, cx);
     });
 
@@ -1697,7 +1709,9 @@ async fn test_mutual_editor_inlay_hint_cache_update(
 
     let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
     editor_b.update_in(cx_b, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13].clone()));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13].clone())
+        });
         editor.handle_input(":", window, cx);
     });
     cx_b.focus(&editor_b);
@@ -1718,7 +1732,9 @@ async fn test_mutual_editor_inlay_hint_cache_update(
 
     let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13])
+        });
         editor.handle_input("a change to increment both buffers' versions", window, cx);
     });
     cx_a.focus(&editor_a);
@@ -2121,7 +2137,9 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo
     });
 
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13].clone()));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([13..13].clone())
+        });
         editor.handle_input(":", window, cx);
     });
     color_request_handle.next().await.unwrap();

crates/collab/src/tests/following_tests.rs 🔗

@@ -6,7 +6,7 @@ use collab_ui::{
     channel_view::ChannelView,
     notifications::project_shared_notification::ProjectSharedNotification,
 };
-use editor::{Editor, MultiBuffer, PathKey};
+use editor::{Editor, MultiBuffer, PathKey, SelectionEffects};
 use gpui::{
     AppContext as _, BackgroundExecutor, BorrowAppContext, Entity, SharedString, TestAppContext,
     VisualContext, VisualTestContext, point,
@@ -376,7 +376,9 @@ async fn test_basic_following(
 
     // Changes to client A's editor are reflected on client B.
     editor_a1.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1, 2..2])
+        });
     });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
     executor.run_until_parked();
@@ -393,7 +395,9 @@ async fn test_basic_following(
     editor_b1.update(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
 
     editor_a1.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([3..3])
+        });
         editor.set_scroll_position(point(0., 100.), window, cx);
     });
     executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
@@ -1647,7 +1651,9 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // b should follow a to position 1
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]))
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1])
+        })
     });
     cx_a.executor()
         .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
@@ -1667,7 +1673,9 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
 
     // b should not follow a to position 2
     editor_a.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]))
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([2..2])
+        })
     });
     cx_a.executor()
         .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE);
@@ -1968,7 +1976,7 @@ 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| {
             editor.insert("Hello from A.", window, cx);
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_ranges(vec![3..4]);
             });
         });
@@ -2109,7 +2117,7 @@ async fn test_following_after_replacement(cx_a: &mut TestAppContext, cx_b: &mut
         workspace.add_item_to_center(Box::new(editor.clone()) as _, window, cx)
     });
     editor.update_in(cx_a, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::row_range(4..4)]);
         })
     });

crates/collab_ui/src/channel_view.rs 🔗

@@ -7,8 +7,8 @@ use client::{
 };
 use collections::HashMap;
 use editor::{
-    CollaborationHub, DisplayPoint, Editor, EditorEvent, display_map::ToDisplayPoint,
-    scroll::Autoscroll,
+    CollaborationHub, DisplayPoint, Editor, EditorEvent, SelectionEffects,
+    display_map::ToDisplayPoint, scroll::Autoscroll,
 };
 use gpui::{
     AnyView, App, ClipboardItem, Context, Entity, EventEmitter, Focusable, Pixels, Point, Render,
@@ -260,9 +260,16 @@ impl ChannelView {
                 .find(|item| &Channel::slug(&item.text).to_lowercase() == &position)
             {
                 self.editor.update(cx, |editor, cx| {
-                    editor.change_selections(Some(Autoscroll::focused()), window, cx, |s| {
-                        s.replace_cursors_with(|map| vec![item.range.start.to_display_point(map)])
-                    })
+                    editor.change_selections(
+                        SelectionEffects::scroll(Autoscroll::focused()),
+                        window,
+                        cx,
+                        |s| {
+                            s.replace_cursors_with(|map| {
+                                vec![item.range.start.to_display_point(map)]
+                            })
+                        },
+                    )
                 });
                 return;
             }

crates/copilot/src/copilot_completion_provider.rs 🔗

@@ -264,7 +264,8 @@ fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b:
 mod tests {
     use super::*;
     use editor::{
-        Editor, ExcerptRange, MultiBuffer, test::editor_lsp_test_context::EditorLspTestContext,
+        Editor, ExcerptRange, MultiBuffer, SelectionEffects,
+        test::editor_lsp_test_context::EditorLspTestContext,
     };
     use fs::FakeFs;
     use futures::StreamExt;
@@ -478,7 +479,7 @@ mod tests {
         // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
         cx.update_editor(|editor, window, cx| {
             editor.set_text("fn foo() {\n  \n}", window, cx);
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
             });
         });
@@ -767,7 +768,7 @@ mod tests {
         );
         _ = editor.update(cx, |editor, window, cx| {
             // Ensure copilot suggestions are shown for the first excerpt.
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
             });
             editor.next_edit_prediction(&Default::default(), window, cx);
@@ -793,7 +794,7 @@ mod tests {
         );
         _ = editor.update(cx, |editor, window, cx| {
             // Move to another excerpt, ensuring the suggestion gets cleared.
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
             });
             assert!(!editor.has_active_inline_completion());
@@ -1019,7 +1020,7 @@ mod tests {
             );
 
         _ = editor.update(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
             });
             editor.refresh_inline_completion(true, false, window, cx);
@@ -1029,7 +1030,7 @@ mod tests {
         assert!(copilot_requests.try_next().is_err());
 
         _ = editor.update(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges([Point::new(5, 0)..Point::new(5, 0)])
             });
             editor.refresh_inline_completion(true, false, window, cx);

crates/debugger_ui/src/stack_trace_view.rs 🔗

@@ -4,7 +4,7 @@ use collections::HashMap;
 use dap::StackFrameId;
 use editor::{
     Anchor, Bias, DebugStackFrameLine, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer,
-    RowHighlightOptions, ToPoint, scroll::Autoscroll,
+    RowHighlightOptions, SelectionEffects, ToPoint, scroll::Autoscroll,
 };
 use gpui::{
     AnyView, App, AppContext, Entity, EventEmitter, Focusable, IntoElement, Render, SharedString,
@@ -99,10 +99,11 @@ impl StackTraceView {
                             if frame_anchor.excerpt_id
                                 != editor.selections.newest_anchor().head().excerpt_id
                             {
-                                let auto_scroll =
-                                    Some(Autoscroll::center().for_anchor(frame_anchor));
+                                let effects = SelectionEffects::scroll(
+                                    Autoscroll::center().for_anchor(frame_anchor),
+                                );
 
-                                editor.change_selections(auto_scroll, window, cx, |selections| {
+                                editor.change_selections(effects, window, cx, |selections| {
                                     let selection_id = selections.new_selection_id();
 
                                     let selection = Selection {

crates/diagnostics/src/diagnostic_renderer.rs 🔗

@@ -4,7 +4,6 @@ use editor::{
     Anchor, Editor, EditorSnapshot, ToOffset,
     display_map::{BlockContext, BlockPlacement, BlockProperties, BlockStyle},
     hover_popover::diagnostics_markdown_style,
-    scroll::Autoscroll,
 };
 use gpui::{AppContext, Entity, Focusable, WeakEntity};
 use language::{BufferId, Diagnostic, DiagnosticEntry};
@@ -311,7 +310,7 @@ impl DiagnosticBlock {
         let range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);
 
         editor.unfold_ranges(&[range.start..range.end], true, false, cx);
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        editor.change_selections(Default::default(), window, cx, |s| {
             s.select_ranges([range.start..range.start]);
         });
         window.focus(&editor.focus_handle(cx));

crates/diagnostics/src/diagnostics.rs 🔗

@@ -12,7 +12,6 @@ use diagnostic_renderer::DiagnosticBlock;
 use editor::{
     DEFAULT_MULTIBUFFER_CONTEXT, Editor, EditorEvent, ExcerptRange, MultiBuffer, PathKey,
     display_map::{BlockPlacement, BlockProperties, BlockStyle, CustomBlockId},
-    scroll::Autoscroll,
 };
 use futures::future::join_all;
 use gpui::{
@@ -626,7 +625,7 @@ impl ProjectDiagnosticsEditor {
                     if let Some(anchor_range) = anchor_ranges.first() {
                         let range_to_select = anchor_range.start..anchor_range.start;
                         this.editor.update(cx, |editor, cx| {
-                            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                            editor.change_selections(Default::default(), window, cx, |s| {
                                 s.select_anchor_ranges([range_to_select]);
                             })
                         });

crates/editor/src/editor.rs 🔗

@@ -1262,6 +1262,19 @@ impl Default for SelectionHistoryMode {
 }
 
 #[derive(Debug)]
+/// SelectionEffects controls the side-effects of updating the selection.
+///
+/// The default behaviour does "what you mostly want":
+/// - it pushes to the nav history if the cursor moved by >10 lines
+/// - it re-triggers completion requests
+/// - it scrolls to fit
+///
+/// You might want to modify these behaviours. For example when doing a "jump"
+/// like go to definition, we always want to add to nav history; but when scrolling
+/// in vim mode we never do.
+///
+/// Similarly, you might want to disable scrolling if you don't want the viewport to
+/// move.
 pub struct SelectionEffects {
     nav_history: Option<bool>,
     completions: bool,
@@ -3164,12 +3177,11 @@ impl Editor {
     /// effects of selection change occur at the end of the transaction.
     pub fn change_selections<R>(
         &mut self,
-        effects: impl Into<SelectionEffects>,
+        effects: SelectionEffects,
         window: &mut Window,
         cx: &mut Context<Self>,
         change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
     ) -> R {
-        let effects = effects.into();
         if let Some(state) = &mut self.deferred_selection_effects_state {
             state.effects.scroll = effects.scroll.or(state.effects.scroll);
             state.effects.completions = effects.completions;
@@ -3449,8 +3461,13 @@ impl Editor {
         };
 
         let selections_count = self.selections.count();
+        let effects = if auto_scroll {
+            SelectionEffects::default()
+        } else {
+            SelectionEffects::no_scroll()
+        };
 
-        self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
+        self.change_selections(effects, window, cx, |s| {
             if let Some(point_to_delete) = point_to_delete {
                 s.delete(point_to_delete);
 
@@ -3488,13 +3505,18 @@ impl Editor {
                 .buffer_snapshot
                 .anchor_before(position.to_point(&display_map));
 
-            self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
-                s.clear_disjoint();
-                s.set_pending_anchor_range(
-                    pointer_position..pointer_position,
-                    SelectMode::Character,
-                );
-            });
+            self.change_selections(
+                SelectionEffects::scroll(Autoscroll::newest()),
+                window,
+                cx,
+                |s| {
+                    s.clear_disjoint();
+                    s.set_pending_anchor_range(
+                        pointer_position..pointer_position,
+                        SelectMode::Character,
+                    );
+                },
+            );
         };
 
         let tail = self.selections.newest::<Point>(cx).tail();
@@ -3609,7 +3631,7 @@ impl Editor {
                 pending.reversed = false;
             }
 
-            self.change_selections(None, window, cx, |s| {
+            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.set_pending(pending, mode);
             });
         } else {
@@ -3625,7 +3647,7 @@ impl Editor {
         self.columnar_selection_state.take();
         if self.selections.pending_anchor().is_some() {
             let selections = self.selections.all::<usize>(cx);
-            self.change_selections(None, window, cx, |s| {
+            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select(selections);
                 s.clear_pending();
             });
@@ -3699,7 +3721,7 @@ impl Editor {
             _ => selection_ranges,
         };
 
-        self.change_selections(None, window, cx, |s| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(ranges);
         });
         cx.notify();
@@ -3739,7 +3761,7 @@ impl Editor {
         }
 
         if self.mode.is_full()
-            && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
+            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
         {
             return;
         }
@@ -4542,9 +4564,7 @@ impl Editor {
                 })
                 .collect();
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(new_selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
             this.refresh_inline_completion(true, false, window, cx);
         });
     }
@@ -4573,7 +4593,7 @@ impl Editor {
         self.transact(window, cx, |editor, window, cx| {
             editor.edit(edits, cx);
 
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 let mut index = 0;
                 s.move_cursors_with(|map, _, _| {
                     let row = rows[index];
@@ -4635,7 +4655,7 @@ impl Editor {
         self.transact(window, cx, |editor, window, cx| {
             editor.edit(edits, cx);
 
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 let mut index = 0;
                 s.move_cursors_with(|map, _, _| {
                     let row = rows[index];
@@ -4712,7 +4732,7 @@ impl Editor {
                 anchors
             });
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select_anchors(selection_anchors);
             });
 
@@ -4856,7 +4876,7 @@ impl Editor {
             .collect();
 
         drop(buffer);
-        self.change_selections(None, window, cx, |selections| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
             selections.select(new_selections)
         });
     }
@@ -7160,7 +7180,7 @@ impl Editor {
                         self.unfold_ranges(&[target..target], true, false, cx);
                         // Note that this is also done in vim's handler of the Tab action.
                         self.change_selections(
-                            Some(Autoscroll::newest()),
+                            SelectionEffects::scroll(Autoscroll::newest()),
                             window,
                             cx,
                             |selections| {
@@ -7205,7 +7225,7 @@ impl Editor {
                     buffer.edit(edits.iter().cloned(), None, cx)
                 });
 
-                self.change_selections(None, window, cx, |s| {
+                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_anchor_ranges([last_edit_end..last_edit_end]);
                 });
 
@@ -7252,9 +7272,14 @@ impl Editor {
         match &active_inline_completion.completion {
             InlineCompletion::Move { target, .. } => {
                 let target = *target;
-                self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
-                    selections.select_anchor_ranges([target..target]);
-                });
+                self.change_selections(
+                    SelectionEffects::scroll(Autoscroll::newest()),
+                    window,
+                    cx,
+                    |selections| {
+                        selections.select_anchor_ranges([target..target]);
+                    },
+                );
             }
             InlineCompletion::Edit { edits, .. } => {
                 // Find an insertion that starts at the cursor position.
@@ -7855,9 +7880,12 @@ impl Editor {
                     this.entry("Run to cursor", None, move |window, cx| {
                         weak_editor
                             .update(cx, |editor, cx| {
-                                editor.change_selections(None, window, cx, |s| {
-                                    s.select_ranges([Point::new(row, 0)..Point::new(row, 0)])
-                                });
+                                editor.change_selections(
+                                    SelectionEffects::no_scroll(),
+                                    window,
+                                    cx,
+                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
+                                );
                             })
                             .ok();
 
@@ -9398,7 +9426,7 @@ impl Editor {
                 .collect::<Vec<_>>()
         });
         if let Some(tabstop) = tabstops.first() {
-            self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            self.change_selections(Default::default(), window, cx, |s| {
                 // Reverse order so that the first range is the newest created selection.
                 // Completions will use it and autoscroll will prioritize it.
                 s.select_ranges(tabstop.ranges.iter().rev().cloned());
@@ -9516,7 +9544,7 @@ impl Editor {
                 }
             }
             if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
-                self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                self.change_selections(Default::default(), window, cx, |s| {
                     // Reverse order so that the first range is the newest created selection.
                     // Completions will use it and autoscroll will prioritize it.
                     s.select_ranges(current_ranges.iter().rev().cloned())
@@ -9606,9 +9634,7 @@ impl Editor {
                 }
             }
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
             this.insert("", window, cx);
             let empty_str: Arc<str> = Arc::from("");
             for (buffer, edits) in linked_ranges {
@@ -9644,7 +9670,7 @@ impl Editor {
     pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         let cursor = movement::right(map, selection.head());
@@ -9787,9 +9813,7 @@ impl Editor {
 
         self.transact(window, cx, |this, window, cx| {
             this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
             this.refresh_inline_completion(true, false, window, cx);
         });
     }
@@ -9822,9 +9846,7 @@ impl Editor {
 
         self.transact(window, cx, |this, window, cx| {
             this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
         });
     }
 
@@ -9977,9 +9999,7 @@ impl Editor {
                 );
             });
             let selections = this.selections.all::<usize>(cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
         });
     }
 
@@ -10004,9 +10024,7 @@ impl Editor {
                 buffer.autoindent_ranges(selections, cx);
             });
             let selections = this.selections.all::<usize>(cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
         });
     }
 
@@ -10087,7 +10105,7 @@ impl Editor {
                 })
                 .collect();
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(new_selections);
             });
         });
@@ -10153,7 +10171,7 @@ impl Editor {
                 }
             }
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select_anchor_ranges(cursor_positions)
             });
         });
@@ -10740,7 +10758,7 @@ impl Editor {
                 })
                 .collect();
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(new_selections);
             });
 
@@ -11091,7 +11109,7 @@ impl Editor {
                 buffer.edit(edits, None, cx);
             });
 
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(new_selections);
             });
 
@@ -11127,7 +11145,7 @@ impl Editor {
             this.buffer.update(cx, |buffer, cx| {
                 buffer.edit(edits, None, cx);
             });
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select_anchor_ranges([last_edit_start..last_edit_end]);
             });
         });
@@ -11329,7 +11347,7 @@ impl Editor {
                 }
             });
             this.fold_creases(refold_creases, true, window, cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(new_selections);
             })
         });
@@ -11430,9 +11448,7 @@ impl Editor {
                 }
             });
             this.fold_creases(refold_creases, true, window, cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(new_selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
         });
     }
 
@@ -11440,7 +11456,7 @@ 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 edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            let edits = this.change_selections(Default::default(), window, cx, |s| {
                 let mut edits: Vec<(Range<usize>, String)> = Default::default();
                 s.move_with(|display_map, selection| {
                     if !selection.is_empty() {
@@ -11488,7 +11504,7 @@ impl Editor {
             this.buffer
                 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
             let selections = this.selections.all::<usize>(cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(selections);
             });
         });
@@ -11744,7 +11760,7 @@ impl Editor {
         }
 
         self.transact(window, cx, |this, window, cx| {
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.select(selections);
             });
             this.insert("", window, cx);
@@ -11760,7 +11776,7 @@ impl Editor {
 
     pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
-        self.change_selections(None, window, cx, |s| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.move_with(|snapshot, sel| {
                 if sel.is_empty() {
                     sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
@@ -11964,9 +11980,7 @@ impl Editor {
                 });
 
                 let selections = this.selections.all::<usize>(cx);
-                this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                    s.select(selections)
-                });
+                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
             } else {
                 this.insert(&clipboard_text, window, cx);
             }
@@ -12005,7 +12019,7 @@ impl Editor {
             if let Some((selections, _)) =
                 self.selection_history.transaction(transaction_id).cloned()
             {
-                self.change_selections(None, window, cx, |s| {
+                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_anchors(selections.to_vec());
                 });
             } else {
@@ -12035,7 +12049,7 @@ impl Editor {
             if let Some((_, Some(selections))) =
                 self.selection_history.transaction(transaction_id).cloned()
             {
-                self.change_selections(None, window, cx, |s| {
+                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_anchors(selections.to_vec());
                 });
             } else {
@@ -12065,7 +12079,7 @@ impl Editor {
 
     pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 let cursor = if selection.is_empty() {
                     movement::left(map, selection.start)
@@ -12079,14 +12093,14 @@ impl Editor {
 
     pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
         })
     }
 
     pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 let cursor = if selection.is_empty() {
                     movement::right(map, selection.end)
@@ -12100,7 +12114,7 @@ impl Editor {
 
     pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
         })
     }
@@ -12121,7 +12135,7 @@ impl Editor {
         let selection_count = self.selections.count();
         let first_selection = self.selections.first_anchor();
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12162,7 +12176,7 @@ impl Editor {
 
         let text_layout_details = &self.text_layout_details(window);
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12199,7 +12213,7 @@ impl Editor {
 
         let text_layout_details = &self.text_layout_details(window);
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12225,7 +12239,7 @@ impl Editor {
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let text_layout_details = &self.text_layout_details(window);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
             })
@@ -12240,7 +12254,7 @@ impl Editor {
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let text_layout_details = &self.text_layout_details(window);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
             })
@@ -12261,7 +12275,7 @@ impl Editor {
 
         let text_layout_details = &self.text_layout_details(window);
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
             })
@@ -12299,15 +12313,15 @@ impl Editor {
 
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
 
-        let autoscroll = if action.center_cursor {
-            Autoscroll::center()
+        let effects = if action.center_cursor {
+            SelectionEffects::scroll(Autoscroll::center())
         } else {
-            Autoscroll::fit()
+            SelectionEffects::default()
         };
 
         let text_layout_details = &self.text_layout_details(window);
 
-        self.change_selections(Some(autoscroll), window, cx, |s| {
+        self.change_selections(effects, window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12328,7 +12342,7 @@ impl Editor {
     pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let text_layout_details = &self.text_layout_details(window);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::up(map, head, goal, false, text_layout_details)
             })
@@ -12349,7 +12363,7 @@ impl Editor {
         let selection_count = self.selections.count();
         let first_selection = self.selections.first_anchor();
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12385,7 +12399,7 @@ impl Editor {
 
         let text_layout_details = &self.text_layout_details(window);
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
             })
@@ -12423,14 +12437,14 @@ impl Editor {
 
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
 
-        let autoscroll = if action.center_cursor {
-            Autoscroll::center()
+        let effects = if action.center_cursor {
+            SelectionEffects::scroll(Autoscroll::center())
         } else {
-            Autoscroll::fit()
+            SelectionEffects::default()
         };
 
         let text_layout_details = &self.text_layout_details(window);
-        self.change_selections(Some(autoscroll), window, cx, |s| {
+        self.change_selections(effects, window, cx, |s| {
             s.move_with(|map, selection| {
                 if !selection.is_empty() {
                     selection.goal = SelectionGoal::None;
@@ -12451,7 +12465,7 @@ impl Editor {
     pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let text_layout_details = &self.text_layout_details(window);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, goal| {
                 movement::down(map, head, goal, false, text_layout_details)
             })
@@ -12509,7 +12523,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (
                     movement::previous_word_start(map, head),
@@ -12526,7 +12540,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (
                     movement::previous_subword_start(map, head),
@@ -12543,7 +12557,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::previous_word_start(map, head),
@@ -12560,7 +12574,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::previous_subword_start(map, head),
@@ -12579,7 +12593,7 @@ impl Editor {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
             this.select_autoclose_pair(window, cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         let cursor = if action.ignore_newlines {
@@ -12604,7 +12618,7 @@ impl Editor {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
             this.select_autoclose_pair(window, cx);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         let cursor = movement::previous_subword_start(map, selection.head());
@@ -12623,7 +12637,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (movement::next_word_end(map, head), SelectionGoal::None)
             });
@@ -12637,7 +12651,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (movement::next_subword_end(map, head), SelectionGoal::None)
             });
@@ -12651,7 +12665,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (movement::next_word_end(map, head), SelectionGoal::None)
             });
@@ -12665,7 +12679,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (movement::next_subword_end(map, head), SelectionGoal::None)
             });
@@ -12680,7 +12694,7 @@ impl Editor {
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         let cursor = if action.ignore_newlines {
@@ -12704,7 +12718,7 @@ impl Editor {
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         let cursor = movement::next_subword_end(map, selection.head());
@@ -12723,7 +12737,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (
                     movement::indented_line_beginning(
@@ -12745,7 +12759,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::indented_line_beginning(
@@ -12768,7 +12782,7 @@ impl Editor {
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
         self.transact(window, cx, |this, window, cx| {
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            this.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|_, selection| {
                     selection.reversed = true;
                 });
@@ -12793,7 +12807,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_cursors_with(|map, head, _| {
                 (
                     movement::line_end(map, head, action.stop_at_soft_wraps),
@@ -12810,7 +12824,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::line_end(map, head, action.stop_at_soft_wraps),
@@ -12869,7 +12883,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::start_of_paragraph(map, selection.head(), 1),
@@ -12890,7 +12904,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::end_of_paragraph(map, selection.head(), 1),
@@ -12911,7 +12925,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::start_of_paragraph(map, head, 1),
@@ -12932,7 +12946,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::end_of_paragraph(map, head, 1),
@@ -12953,7 +12967,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::start_of_excerpt(
@@ -12978,7 +12992,7 @@ impl Editor {
             return;
         }
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::start_of_excerpt(
@@ -13003,7 +13017,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::end_of_excerpt(
@@ -13028,7 +13042,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 selection.collapse_to(
                     movement::end_of_excerpt(
@@ -13053,7 +13067,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
@@ -13074,7 +13088,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
@@ -13095,7 +13109,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
@@ -13116,7 +13130,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.move_heads_with(|map, head, _| {
                 (
                     movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
@@ -13137,7 +13151,7 @@ impl Editor {
             return;
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select_ranges(vec![0..0]);
         });
     }
@@ -13151,7 +13165,7 @@ impl Editor {
         let mut selection = self.selections.last::<Point>(cx);
         selection.set_head(Point::zero(), SelectionGoal::None);
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select(vec![selection]);
         });
     }
@@ -13163,7 +13177,7 @@ impl Editor {
         }
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let cursor = self.buffer.read(cx).read(cx).len();
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select_ranges(vec![cursor..cursor])
         });
     }
@@ -13229,7 +13243,7 @@ impl Editor {
         let buffer = self.buffer.read(cx).snapshot(cx);
         let mut selection = self.selections.first::<usize>(cx);
         selection.set_head(buffer.len(), SelectionGoal::None);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select(vec![selection]);
         });
     }
@@ -13237,7 +13251,7 @@ impl Editor {
     pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
         self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
         let end = self.buffer.read(cx).read(cx).len();
-        self.change_selections(None, window, cx, |s| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(vec![0..end]);
         });
     }
@@ -13253,7 +13267,7 @@ impl Editor {
             selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
             selection.reversed = false;
         }
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select(selections);
         });
     }
@@ -13290,7 +13304,7 @@ impl Editor {
                 }
             }
         }
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select_ranges(new_selection_ranges);
         });
     }
@@ -13438,7 +13452,7 @@ impl Editor {
             }
         }
 
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select(final_selections);
         });
 
@@ -13476,7 +13490,12 @@ impl Editor {
             auto_scroll.is_some(),
             cx,
         );
-        self.change_selections(auto_scroll, window, cx, |s| {
+        let effects = if let Some(scroll) = auto_scroll {
+            SelectionEffects::scroll(scroll)
+        } else {
+            SelectionEffects::no_scroll()
+        };
+        self.change_selections(effects, window, cx, |s| {
             if replace_newest {
                 s.delete(s.newest_anchor().id);
             }
@@ -13688,7 +13707,7 @@ impl Editor {
         }
 
         self.unfold_ranges(&new_selections.clone(), false, false, cx);
-        self.change_selections(None, window, cx, |selections| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
             selections.select_ranges(new_selections)
         });
 
@@ -13859,7 +13878,7 @@ impl Editor {
         let selections = self.selections.disjoint_anchors();
         match selections.first() {
             Some(first) if selections.len() >= 2 => {
-                self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                self.change_selections(Default::default(), window, cx, |s| {
                     s.select_ranges([first.range()]);
                 });
             }
@@ -13883,7 +13902,7 @@ impl Editor {
         let selections = self.selections.disjoint_anchors();
         match selections.last() {
             Some(last) if selections.len() >= 2 => {
-                self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                self.change_selections(Default::default(), window, cx, |s| {
                     s.select_ranges([last.range()]);
                 });
             }
@@ -14162,9 +14181,7 @@ impl Editor {
             }
 
             drop(snapshot);
-            this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
-                s.select(selections)
-            });
+            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
 
             let selections = this.selections.all::<Point>(cx);
             let selections_on_single_row = selections.windows(2).all(|selections| {
@@ -14183,7 +14200,7 @@ impl Editor {
             if advance_downwards {
                 let snapshot = this.buffer.read(cx).snapshot(cx);
 
-                this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                this.change_selections(Default::default(), window, cx, |s| {
                     s.move_cursors_with(|display_snapshot, display_point, _| {
                         let mut point = display_point.to_point(display_snapshot);
                         point.row += 1;
@@ -14250,7 +14267,7 @@ impl Editor {
             .collect::<Vec<_>>();
 
         if selected_larger_symbol {
-            self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            self.change_selections(Default::default(), window, cx, |s| {
                 s.select(new_selections);
             });
         }
@@ -14350,7 +14367,7 @@ impl Editor {
 
         if selected_larger_node {
             self.select_syntax_node_history.disable_clearing = true;
-            self.change_selections(None, window, cx, |s| {
+            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select(new_selections.clone());
             });
             self.select_syntax_node_history.disable_clearing = false;
@@ -14396,7 +14413,7 @@ impl Editor {
             }
 
             self.select_syntax_node_history.disable_clearing = true;
-            self.change_selections(None, window, cx, |s| {
+            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select(selections.to_vec());
             });
             self.select_syntax_node_history.disable_clearing = false;

crates/editor/src/editor_tests.rs 🔗

@@ -179,7 +179,9 @@ fn test_edit_events(cx: &mut TestAppContext) {
 
     // No event is emitted when the mutation is a no-op.
     _ = editor2.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([0..0])
+        });
 
         editor.backspace(&Backspace, window, cx);
     });
@@ -202,7 +204,9 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
 
     _ = editor.update(cx, |editor, window, cx| {
         editor.start_transaction_at(now, window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([2..4])
+        });
 
         editor.insert("cd", window, cx);
         editor.end_transaction_at(now, cx);
@@ -210,14 +214,18 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
         assert_eq!(editor.selections.ranges(cx), vec![4..4]);
 
         editor.start_transaction_at(now, window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([4..5])
+        });
         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]);
 
         now += group_interval + Duration::from_millis(1);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([2..2])
+        });
 
         // Simulate an edit in another editor
         buffer.update(cx, |buffer, cx| {
@@ -325,7 +333,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
         assert_eq!(editor.marked_text_ranges(cx), None);
 
         // Start a new IME composition with multiple cursors.
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 OffsetUtf16(1)..OffsetUtf16(1),
                 OffsetUtf16(3)..OffsetUtf16(3),
@@ -623,7 +631,7 @@ fn test_clone(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(selection_ranges.clone())
         });
         editor.fold_creases(
@@ -709,12 +717,12 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
             // Move the cursor a small distance.
             // Nothing is added to the navigation history.
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
                 ])
             });
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
                 ])
@@ -723,7 +731,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
 
             // Move the cursor a large distance.
             // The history can jump back to the previous position.
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
                 ])
@@ -893,7 +901,7 @@ fn test_fold_action(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
             ]);
@@ -984,7 +992,7 @@ fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
             ]);
@@ -1069,7 +1077,7 @@ fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
             ]);
@@ -1301,7 +1309,7 @@ fn test_move_cursor(cx: &mut TestAppContext) {
             &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
         );
 
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
             ]);
@@ -1446,7 +1454,7 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
         build_editor(buffer.clone(), window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
         });
 
@@ -1536,7 +1544,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
@@ -1731,7 +1739,7 @@ fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 
         // First, let's assert behavior on the first line, that was not soft-wrapped.
         // Start the cursor at the `k` on the first line
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
             ]);
@@ -1753,7 +1761,7 @@ fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 
         // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
         // Start the cursor at the last line (`y` that was wrapped to a new line)
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
             ]);
@@ -1819,7 +1827,7 @@ fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
@@ -1901,7 +1909,7 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
                 DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
@@ -1971,7 +1979,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
             "use one::{\n    two::three::\n    four::five\n};"
         );
 
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
             ]);
@@ -2234,7 +2242,7 @@ async fn test_autoscroll(cx: &mut TestAppContext) {
     // on screen, the editor autoscrolls to reveal the newest cursor, and
     // allows the vertical scroll margin below that cursor.
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
+        editor.change_selections(Default::default(), window, cx, |selections| {
             selections.select_ranges([
                 Point::new(0, 0)..Point::new(0, 0),
                 Point::new(6, 0)..Point::new(6, 0),
@@ -2262,7 +2270,7 @@ async fn test_autoscroll(cx: &mut TestAppContext) {
     // Add a cursor above the visible area. Since both cursors fit on screen,
     // the editor scrolls to show both.
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
+        editor.change_selections(Default::default(), window, cx, |selections| {
             selections.select_ranges([
                 Point::new(1, 0)..Point::new(1, 0),
                 Point::new(6, 0)..Point::new(6, 0),
@@ -2429,7 +2437,7 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 // an empty selection - the preceding word fragment is deleted
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
@@ -2448,7 +2456,7 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 // an empty selection - the following word fragment is deleted
                 DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
@@ -2483,7 +2491,7 @@ fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
     };
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
             ])
@@ -2519,7 +2527,7 @@ fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
     };
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
             ])
@@ -2558,7 +2566,7 @@ fn test_newline(cx: &mut TestAppContext) {
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
                 DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
@@ -2591,7 +2599,7 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
             cx,
         );
         let mut editor = build_editor(buffer.clone(), window, cx);
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 Point::new(2, 4)..Point::new(2, 5),
                 Point::new(5, 4)..Point::new(5, 5),
@@ -3078,7 +3086,7 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
     let editor = cx.add_window(|window, cx| {
         let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
         let mut editor = build_editor(buffer.clone(), window, cx);
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([3..4, 11..12, 19..20])
         });
         editor
@@ -3727,7 +3735,7 @@ fn test_delete_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
@@ -3750,7 +3758,7 @@ fn test_delete_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
             ])
@@ -3787,7 +3795,7 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
         );
 
         // When multiple lines are selected, remove newlines that are spanned by the selection
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
         });
         editor.join_lines(&JoinLines, window, cx);
@@ -3806,7 +3814,7 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
         );
 
         // When joining an empty line don't insert a space
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
         });
         editor.join_lines(&JoinLines, window, cx);
@@ -3846,7 +3854,7 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 
         // We remove any leading spaces
         assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
         });
         editor.join_lines(&JoinLines, window, cx);
@@ -3873,7 +3881,7 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
         let mut editor = build_editor(buffer.clone(), window, cx);
         let buffer = buffer.read(cx).as_singleton().unwrap();
 
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 Point::new(0, 2)..Point::new(1, 1),
                 Point::new(1, 2)..Point::new(1, 2),
@@ -4713,7 +4721,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
@@ -4739,7 +4747,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
                 DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
@@ -4763,7 +4771,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
@@ -4789,7 +4797,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
                 DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
@@ -4811,7 +4819,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
                 DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
@@ -4848,7 +4856,7 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
             window,
             cx,
         );
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
@@ -4951,7 +4959,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
             Some(Autoscroll::fit()),
             cx,
         );
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
         });
         editor.move_line_down(&MoveLineDown, window, cx);
@@ -5036,7 +5044,9 @@ fn test_transpose(cx: &mut TestAppContext) {
     _ = cx.add_window(|window, cx| {
         let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
         editor.set_style(EditorStyle::default(), window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1])
+        });
         editor.transpose(&Default::default(), window, cx);
         assert_eq!(editor.text(cx), "bac");
         assert_eq!(editor.selections.ranges(cx), [2..2]);
@@ -5055,12 +5065,16 @@ fn test_transpose(cx: &mut TestAppContext) {
     _ = cx.add_window(|window, cx| {
         let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
         editor.set_style(EditorStyle::default(), window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([3..3])
+        });
         editor.transpose(&Default::default(), window, cx);
         assert_eq!(editor.text(cx), "acb\nde");
         assert_eq!(editor.selections.ranges(cx), [3..3]);
 
-        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
+        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]);
@@ -5079,7 +5093,9 @@ fn test_transpose(cx: &mut TestAppContext) {
     _ = cx.add_window(|window, cx| {
         let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
         editor.set_style(EditorStyle::default(), window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1, 2..2, 4..4])
+        });
         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]);
@@ -5106,7 +5122,9 @@ fn test_transpose(cx: &mut TestAppContext) {
     _ = cx.add_window(|window, cx| {
         let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
         editor.set_style(EditorStyle::default(), window, cx);
-        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
+        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), "🏀🍐✋");
         assert_eq!(editor.selections.ranges(cx), [8..8]);
@@ -6085,7 +6103,7 @@ fn test_select_line(cx: &mut TestAppContext) {
         build_editor(buffer, window, cx)
     });
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
@@ -6212,7 +6230,7 @@ async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestA
     });
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
@@ -6231,7 +6249,7 @@ async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestA
         .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
             ])
@@ -6977,7 +6995,7 @@ async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 
     // Move cursor to a different position
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
         });
     });
@@ -7082,7 +7100,7 @@ async fn test_undo_inline_completion_scrolls_to_edit_pos(cx: &mut TestAppContext
     "});
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(9, 2)..Point::new(9, 2)]);
         });
     });
@@ -7342,7 +7360,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
         .await;
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
                 DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
@@ -7524,7 +7542,7 @@ async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContex
 
     // Test case 1: Cursor at end of word
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)
             ]);
@@ -7548,7 +7566,7 @@ async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContex
 
     // Test case 2: Cursor at end of statement
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11)
             ]);
@@ -7593,7 +7611,7 @@ async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppConte
 
     // Test 1: Cursor on a letter of a string word
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
             ]);
@@ -7627,7 +7645,7 @@ async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppConte
 
     // Test 2: Partial selection within a word
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
             ]);
@@ -7661,7 +7679,7 @@ async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppConte
 
     // Test 3: Complete word already selected
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
             ]);
@@ -7695,7 +7713,7 @@ async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppConte
 
     // Test 4: Selection spanning across words
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
             ]);
@@ -7897,7 +7915,9 @@ async fn test_autoindent(cx: &mut TestAppContext) {
         .await;
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([5..5, 8..8, 9..9])
+        });
         editor.newline(&Newline, window, cx);
         assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
         assert_eq!(
@@ -8679,7 +8699,7 @@ async fn test_surround_with_pair(cx: &mut TestAppContext) {
         .await;
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
                 DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
@@ -8829,7 +8849,7 @@ async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
         .await;
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 Point::new(0, 1)..Point::new(0, 1),
                 Point::new(1, 1)..Point::new(1, 1),
@@ -9511,16 +9531,22 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
     });
 
     multi_buffer_editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(1..2))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(1..2)),
+        );
         editor.insert("|one|two|three|", window, cx);
     });
     assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
     multi_buffer_editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(60..70))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(60..70)),
+        );
         editor.insert("|four|five|six|", window, cx);
     });
     assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
@@ -9683,9 +9709,12 @@ async fn test_autosave_with_dirty_buffers(cx: &mut TestAppContext) {
 
     // Edit only the first buffer
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(10..10))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(10..10)),
+        );
         editor.insert("// edited", window, cx);
     });
 
@@ -11097,7 +11126,9 @@ async fn test_signature_help(cx: &mut TestAppContext) {
     "});
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([0..0])
+        });
     });
 
     let mocked_response = lsp::SignatureHelp {
@@ -11184,7 +11215,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
     // When selecting a range, the popover is gone.
     // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
         })
     });
@@ -11201,7 +11232,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
 
     // When unselecting again, the popover is back if within the brackets.
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
         })
     });
@@ -11221,7 +11252,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
 
     // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
             s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
         })
@@ -11262,7 +11293,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
     cx.condition(|editor, _| !editor.signature_help_state.is_shown())
         .await;
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
         })
     });
@@ -11274,7 +11305,7 @@ async fn test_signature_help(cx: &mut TestAppContext) {
         fn sample(param1: u8, param2: u8) {}
     "});
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
         })
     });
@@ -11930,7 +11961,7 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
     let fake_server = fake_servers.next().await.unwrap();
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 Point::new(1, 11)..Point::new(1, 11),
                 Point::new(7, 11)..Point::new(7, 11),
@@ -13571,7 +13602,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
     editor.update_in(cx, |editor, window, cx| {
         assert_eq!(editor.text(cx), "aaaa\nbbbb");
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([
                 Point::new(0, 0)..Point::new(0, 0),
                 Point::new(1, 0)..Point::new(1, 0),
@@ -13589,7 +13620,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
         );
 
         // Ensure the cursor's head is respected when deleting across an excerpt boundary.
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
         });
         editor.backspace(&Default::default(), window, cx);
@@ -13599,7 +13630,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
             [Point::new(1, 0)..Point::new(1, 0)]
         );
 
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
         });
         editor.backspace(&Default::default(), window, cx);
@@ -13647,7 +13678,9 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
             true,
         );
         assert_eq!(editor.text(cx), expected_text);
-        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges(selection_ranges)
+        });
 
         editor.handle_input("X", window, cx);
 
@@ -13708,7 +13741,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
     let editor = cx.add_window(|window, cx| {
         let mut editor = build_editor(multibuffer.clone(), window, cx);
         let snapshot = editor.snapshot(window, cx);
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
         });
         editor.begin_selection(
@@ -13730,7 +13763,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
 
     // Refreshing selections is a no-op when excerpts haven't changed.
     _ = editor.update(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| s.refresh());
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
         assert_eq!(
             editor.selections.ranges(cx),
             [
@@ -13755,7 +13788,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
 
         // Refreshing selections will relocate the first selection to the original buffer
         // location.
-        editor.change_selections(None, window, cx, |s| s.refresh());
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
         assert_eq!(
             editor.selections.ranges(cx),
             [
@@ -13817,7 +13850,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
         );
 
         // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
-        editor.change_selections(None, window, cx, |s| s.refresh());
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
         assert_eq!(
             editor.selections.ranges(cx),
             [Point::new(0, 3)..Point::new(0, 3)]
@@ -13876,7 +13909,7 @@ async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
         .await;
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_display_ranges([
                 DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
                 DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
@@ -14055,7 +14088,9 @@ async fn test_following(cx: &mut TestAppContext) {
 
     // Update the selections only
     _ = leader.update(cx, |leader, window, cx| {
-        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
+        leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1])
+        });
     });
     follower
         .update(cx, |follower, window, cx| {
@@ -14103,7 +14138,9 @@ async fn test_following(cx: &mut TestAppContext) {
     // Update the selections and scroll position. The follower's scroll position is updated
     // via autoscroll, not via the leader's exact scroll position.
     _ = leader.update(cx, |leader, window, cx| {
-        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
+        leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([0..0])
+        });
         leader.request_autoscroll(Autoscroll::newest(), cx);
         leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
     });
@@ -14127,7 +14164,9 @@ async fn test_following(cx: &mut TestAppContext) {
 
     // Creating a pending selection that precedes another selection
     _ = leader.update(cx, |leader, window, cx| {
-        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
+        leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges([1..1])
+        });
         leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
     });
     follower
@@ -14783,7 +14822,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
 
     editor_handle.update_in(cx, |editor, window, cx| {
         window.focus(&editor.focus_handle(cx));
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
         });
         editor.handle_input("{", window, cx);
@@ -16398,7 +16437,7 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
     });
 
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
         });
         editor.git_restore(&Default::default(), window, cx);
@@ -16542,9 +16581,12 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
     cx.executor().run_until_parked();
 
     multi_buffer_editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(1..2))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(1..2)),
+        );
         editor.open_excerpts(&OpenExcerpts, window, cx);
     });
     cx.executor().run_until_parked();
@@ -16594,9 +16636,12 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
         .unwrap();
 
     multi_buffer_editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(39..40))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(39..40)),
+        );
         editor.open_excerpts(&OpenExcerpts, window, cx);
     });
     cx.executor().run_until_parked();
@@ -16650,9 +16695,12 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
         .unwrap();
 
     multi_buffer_editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-            s.select_ranges(Some(70..70))
-        });
+        editor.change_selections(
+            SelectionEffects::scroll(Autoscroll::Next),
+            window,
+            cx,
+            |s| s.select_ranges(Some(70..70)),
+        );
         editor.open_excerpts(&OpenExcerpts, window, cx);
     });
     cx.executor().run_until_parked();
@@ -18254,7 +18302,7 @@ async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
     .await;
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
         });
     });
@@ -18282,7 +18330,7 @@ async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext
     .await;
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
         });
     });
@@ -18298,7 +18346,7 @@ async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext
     );
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
         });
     });
@@ -18314,7 +18362,7 @@ async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext
     );
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
         });
     });
@@ -18345,7 +18393,7 @@ async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
     .await;
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
         });
     });
@@ -18371,7 +18419,7 @@ async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
     .await;
 
     cx.update_editor(|editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
         });
     });
@@ -19309,14 +19357,14 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
         );
 
         // Test finding task when cursor is inside function body
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
         });
         let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
         assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
 
         // Test finding task when cursor is on function name
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
         });
         let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
@@ -19470,7 +19518,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) {
                 .collect::<String>(),
             "bbbb"
         );
-        editor.change_selections(None, window, cx, |selections| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
             selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
         });
         editor.handle_input("B", window, cx);
@@ -19697,7 +19745,9 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test
             HighlightStyle::color(Hsla::green()),
             cx,
         );
-        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_ranges(Some(highlight_range))
+        });
     });
 
     let full_text = format!("\n\n{sample_text}");
@@ -21067,7 +21117,7 @@ println!("5");
         })
     });
     editor_1.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(expected_ranges.clone());
         });
     });
@@ -21513,7 +21563,7 @@ async fn test_html_linked_edits_on_completion(cx: &mut TestAppContext) {
     let fake_server = fake_servers.next().await.unwrap();
     editor.update_in(cx, |editor, window, cx| {
         editor.set_text("<ad></ad>", window, cx);
-        editor.change_selections(None, window, cx, |selections| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
             selections.select_ranges([Point::new(0, 3)..Point::new(0, 3)]);
         });
         let Some((buffer, _)) = editor
@@ -22519,7 +22569,7 @@ async fn test_pulling_diagnostics(cx: &mut TestAppContext) {
 
     // Moving cursor should not trigger diagnostic request
     editor.update_in(cx, |editor, window, cx| {
-        editor.change_selections(None, window, cx, |s| {
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
         });
     });

crates/editor/src/element.rs 🔗

@@ -5238,8 +5238,8 @@ impl EditorElement {
                     paint_highlight(range.start, range.end, color, edges);
                 }
 
-                let scroll_left = layout.position_map.snapshot.scroll_position().x
-                    * layout.position_map.em_advance;
+                let scroll_left =
+                    layout.position_map.snapshot.scroll_position().x * layout.position_map.em_width;
 
                 for (wrap_position, active) in layout.wrap_guides.iter() {
                     let x = (layout.position_map.text_hitbox.origin.x
@@ -6676,7 +6676,7 @@ impl EditorElement {
                         let position_map: &PositionMap = &position_map;
 
                         let line_height = position_map.line_height;
-                        let max_glyph_advance = position_map.em_advance;
+                        let max_glyph_width = position_map.em_width;
                         let (delta, axis) = match delta {
                             gpui::ScrollDelta::Pixels(mut pixels) => {
                                 //Trackpad
@@ -6687,15 +6687,15 @@ impl EditorElement {
                             gpui::ScrollDelta::Lines(lines) => {
                                 //Not trackpad
                                 let pixels =
-                                    point(lines.x * max_glyph_advance, lines.y * line_height);
+                                    point(lines.x * max_glyph_width, lines.y * line_height);
                                 (pixels, None)
                             }
                         };
 
                         let current_scroll_position = position_map.snapshot.scroll_position();
-                        let x = (current_scroll_position.x * max_glyph_advance
+                        let x = (current_scroll_position.x * max_glyph_width
                             - (delta.x * scroll_sensitivity))
-                            / max_glyph_advance;
+                            / max_glyph_width;
                         let y = (current_scroll_position.y * line_height
                             - (delta.y * scroll_sensitivity))
                             / line_height;
@@ -8591,7 +8591,7 @@ impl Element for EditorElement {
                                 start_row,
                                 editor_content_width,
                                 scroll_width,
-                                em_advance,
+                                em_width,
                                 &line_layouts,
                                 cx,
                             )
@@ -10051,7 +10051,7 @@ fn compute_auto_height_layout(
 mod tests {
     use super::*;
     use crate::{
-        Editor, MultiBuffer,
+        Editor, MultiBuffer, SelectionEffects,
         display_map::{BlockPlacement, BlockProperties},
         editor_tests::{init_test, update_test_language_settings},
     };
@@ -10176,7 +10176,7 @@ mod tests {
         window
             .update(cx, |editor, window, cx| {
                 editor.cursor_shape = CursorShape::Block;
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([
                         Point::new(0, 0)..Point::new(1, 0),
                         Point::new(3, 2)..Point::new(3, 3),

crates/editor/src/hover_links.rs 🔗

@@ -1257,7 +1257,7 @@ mod tests {
             let snapshot = editor.buffer().read(cx).snapshot(cx);
             let anchor_range = snapshot.anchor_before(selection_range.start)
                 ..snapshot.anchor_after(selection_range.end);
-            editor.change_selections(Some(crate::Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
             });
         });

crates/editor/src/hover_popover.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     EditorSnapshot, GlobalDiagnosticRenderer, Hover,
     display_map::{InlayOffset, ToDisplayPoint, invisibles::is_invisible},
     hover_links::{InlayHighlight, RangeInEditor},
-    scroll::{Autoscroll, ScrollAmount},
+    scroll::ScrollAmount,
 };
 use anyhow::Context as _;
 use gpui::{
@@ -746,7 +746,7 @@ pub fn open_markdown_url(link: SharedString, window: &mut Window, cx: &mut App)
                         };
                         editor.update_in(cx, |editor, window, cx| {
                             editor.change_selections(
-                                Some(Autoscroll::fit()),
+                                Default::default(),
                                 window,
                                 cx,
                                 |selections| {

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -1302,6 +1302,7 @@ fn apply_hint_update(
 
 #[cfg(test)]
 pub mod tests {
+    use crate::SelectionEffects;
     use crate::editor_tests::update_test_language_settings;
     use crate::scroll::ScrollAmount;
     use crate::{ExcerptRange, scroll::Autoscroll, test::editor_lsp_test_context::rust_lang};
@@ -1384,7 +1385,9 @@ pub mod tests {
 
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                    s.select_ranges([13..13])
+                });
                 editor.handle_input("some change", window, cx);
             })
             .unwrap();
@@ -1698,7 +1701,9 @@ pub mod tests {
 
         rs_editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                    s.select_ranges([13..13])
+                });
                 editor.handle_input("some rs change", window, cx);
             })
             .unwrap();
@@ -1733,7 +1738,9 @@ pub mod tests {
 
         md_editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                    s.select_ranges([13..13])
+                });
                 editor.handle_input("some md change", window, cx);
             })
             .unwrap();
@@ -2155,7 +2162,9 @@ pub mod tests {
         ] {
             editor
                 .update(cx, |editor, window, cx| {
-                    editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                        s.select_ranges([13..13])
+                    });
                     editor.handle_input(change_after_opening, window, cx);
                 })
                 .unwrap();
@@ -2199,7 +2208,9 @@ pub mod tests {
             edits.push(cx.spawn(|mut cx| async move {
                 task_editor
                     .update(&mut cx, |editor, window, cx| {
-                        editor.change_selections(None, window, cx, |s| s.select_ranges([13..13]));
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                            s.select_ranges([13..13])
+                        });
                         editor.handle_input(async_later_change, window, cx);
                     })
                     .unwrap();
@@ -2447,9 +2458,12 @@ pub mod tests {
 
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                    s.select_ranges([selection_in_cached_range..selection_in_cached_range])
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::center()),
+                    window,
+                    cx,
+                    |s| s.select_ranges([selection_in_cached_range..selection_in_cached_range]),
+                );
             })
             .unwrap();
         cx.executor().advance_clock(Duration::from_millis(
@@ -2712,15 +2726,24 @@ pub mod tests {
 
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-                });
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
-                });
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges([Point::new(4, 0)..Point::new(4, 0)]),
+                );
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges([Point::new(22, 0)..Point::new(22, 0)]),
+                );
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges([Point::new(50, 0)..Point::new(50, 0)]),
+                );
             })
             .unwrap();
         cx.executor().run_until_parked();
@@ -2745,9 +2768,12 @@ pub mod tests {
 
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges([Point::new(100, 0)..Point::new(100, 0)]),
+                );
             })
             .unwrap();
         cx.executor().advance_clock(Duration::from_millis(
@@ -2778,9 +2804,12 @@ pub mod tests {
 
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges([Point::new(4, 0)..Point::new(4, 0)]),
+                );
             })
             .unwrap();
         cx.executor().advance_clock(Duration::from_millis(
@@ -2812,7 +2841,7 @@ pub mod tests {
         editor_edited.store(true, Ordering::Release);
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([Point::new(57, 0)..Point::new(57, 0)])
                 });
                 editor.handle_input("++++more text++++", window, cx);
@@ -3130,7 +3159,7 @@ pub mod tests {
         cx.executor().run_until_parked();
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
                 })
             })
@@ -3412,7 +3441,7 @@ pub mod tests {
         cx.executor().run_until_parked();
         editor
             .update(cx, |editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
                 })
             })

crates/editor/src/items.rs 🔗

@@ -1352,7 +1352,7 @@ impl ProjectItem for Editor {
                         cx,
                     );
                     if !restoration_data.selections.is_empty() {
-                        editor.change_selections(None, window, cx, |s| {
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                             s.select_ranges(clip_ranges(&restoration_data.selections, &snapshot));
                         });
                     }
@@ -1558,7 +1558,7 @@ impl SearchableItem for Editor {
     ) {
         self.unfold_ranges(&[matches[index].clone()], false, true, cx);
         let range = self.range_for_match(&matches[index]);
-        self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        self.change_selections(Default::default(), window, cx, |s| {
             s.select_ranges([range]);
         })
     }
@@ -1570,7 +1570,7 @@ impl SearchableItem for Editor {
         cx: &mut Context<Self>,
     ) {
         self.unfold_ranges(matches, false, false, cx);
-        self.change_selections(None, window, cx, |s| {
+        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
             s.select_ranges(matches.iter().cloned())
         });
     }

crates/editor/src/jsx_tag_auto_close.rs 🔗

@@ -843,7 +843,7 @@ mod jsx_tag_autoclose_tests {
         let mut cx = EditorTestContext::for_editor(editor, cx).await;
 
         cx.update_editor(|editor, window, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select(vec![
                     Selection::from_offset(4),
                     Selection::from_offset(9),

crates/editor/src/mouse_context_menu.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     Copy, CopyAndTrim, CopyPermalinkToLine, Cut, DisplayPoint, DisplaySnapshot, Editor,
     EvaluateSelectedText, FindAllReferences, GoToDeclaration, GoToDefinition, GoToImplementation,
-    GoToTypeDefinition, Paste, Rename, RevealInFileManager, SelectMode, SelectionExt,
-    ToDisplayPoint, ToggleCodeActions,
+    GoToTypeDefinition, Paste, Rename, RevealInFileManager, SelectMode, SelectionEffects,
+    SelectionExt, ToDisplayPoint, ToggleCodeActions,
     actions::{Format, FormatSelections},
     selections_collection::SelectionsCollection,
 };
@@ -177,7 +177,7 @@ pub fn deploy_context_menu(
         let anchor = buffer.anchor_before(point.to_point(&display_map));
         if !display_ranges(&display_map, &editor.selections).any(|r| r.contains(&point)) {
             // Move the cursor to the clicked location so that dispatched actions make sense
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.clear_disjoint();
                 s.set_pending_anchor_range(anchor..anchor, SelectMode::Character);
             });

crates/editor/src/proposed_changes_editor.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SemanticsProvider};
+use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SelectionEffects, SemanticsProvider};
 use buffer_diff::BufferDiff;
 use collections::HashSet;
 use futures::{channel::mpsc, future::join_all};
@@ -213,7 +213,9 @@ impl ProposedChangesEditor {
 
         self.buffer_entries = buffer_entries;
         self.editor.update(cx, |editor, cx| {
-            editor.change_selections(None, window, cx, |selections| selections.refresh());
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
+                selections.refresh()
+            });
             editor.buffer.update(cx, |buffer, cx| {
                 for diff in new_diffs {
                     buffer.add_diff(diff, cx)

crates/editor/src/test.rs 🔗

@@ -5,7 +5,7 @@ use std::{rc::Rc, sync::LazyLock};
 
 pub use crate::rust_analyzer_ext::expand_macro_recursively;
 use crate::{
-    DisplayPoint, Editor, EditorMode, FoldPlaceholder, MultiBuffer,
+    DisplayPoint, Editor, EditorMode, FoldPlaceholder, MultiBuffer, SelectionEffects,
     display_map::{
         Block, BlockPlacement, CustomBlockId, DisplayMap, DisplayRow, DisplaySnapshot,
         ToDisplayPoint,
@@ -93,7 +93,9 @@ pub fn select_ranges(
 ) {
     let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
     assert_eq!(editor.text(cx), unmarked_text);
-    editor.change_selections(None, window, cx, |s| s.select_ranges(text_ranges));
+    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+        s.select_ranges(text_ranges)
+    });
 }
 
 #[track_caller]

crates/editor/src/test/editor_test_context.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer, RowExt,
+    AnchorRangeExt, DisplayPoint, Editor, MultiBuffer, RowExt,
     display_map::{HighlightKey, ToDisplayPoint},
 };
 use buffer_diff::DiffHunkStatusKind;
@@ -362,7 +362,7 @@ impl EditorTestContext {
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
         self.editor.update_in(&mut self.cx, |editor, window, cx| {
             editor.set_text(unmarked_text, window, cx);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.select_ranges(selection_ranges)
             })
         });
@@ -379,7 +379,7 @@ impl EditorTestContext {
         let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
         self.editor.update_in(&mut self.cx, |editor, window, cx| {
             assert_eq!(editor.text(cx), unmarked_text);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.select_ranges(selection_ranges)
             })
         });

crates/git_ui/src/commit_view.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::{Context as _, Result};
 use buffer_diff::{BufferDiff, BufferDiffSnapshot};
-use editor::{Editor, EditorEvent, MultiBuffer};
+use editor::{Editor, EditorEvent, MultiBuffer, SelectionEffects};
 use git::repository::{CommitDetails, CommitDiff, CommitSummary, RepoPath};
 use gpui::{
     AnyElement, AnyView, App, AppContext as _, AsyncApp, Context, Entity, EventEmitter,
@@ -154,7 +154,7 @@ impl CommitView {
             });
             editor.update(cx, |editor, cx| {
                 editor.disable_header_for_buffer(metadata_buffer_id.unwrap(), cx);
-                editor.change_selections(None, window, cx, |selections| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                     selections.select_ranges(vec![0..0]);
                 });
             });

crates/git_ui/src/project_diff.rs 🔗

@@ -8,7 +8,7 @@ use anyhow::Result;
 use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
 use collections::HashSet;
 use editor::{
-    Editor, EditorEvent,
+    Editor, EditorEvent, SelectionEffects,
     actions::{GoToHunk, GoToPreviousHunk},
     scroll::Autoscroll,
 };
@@ -255,9 +255,14 @@ impl ProjectDiff {
     fn move_to_path(&mut self, path_key: PathKey, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(position) = self.multibuffer.read(cx).location_for_path(&path_key, cx) {
             self.editor.update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::focused()), window, cx, |s| {
-                    s.select_ranges([position..position]);
-                })
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::focused()),
+                    window,
+                    cx,
+                    |s| {
+                        s.select_ranges([position..position]);
+                    },
+                )
             });
         } else {
             self.pending_scroll = Some(path_key);
@@ -463,7 +468,7 @@ impl ProjectDiff {
 
         self.editor.update(cx, |editor, cx| {
             if was_empty {
-                editor.change_selections(None, window, cx, |selections| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                     // TODO select the very beginning (possibly inside a deletion)
                     selections.select_ranges([0..0])
                 });

crates/go_to_line/src/go_to_line.rs 🔗

@@ -2,8 +2,8 @@ pub mod cursor_position;
 
 use cursor_position::{LineIndicatorFormat, UserCaretPosition};
 use editor::{
-    Anchor, Editor, MultiBufferSnapshot, RowHighlightOptions, ToOffset, ToPoint, actions::Tab,
-    scroll::Autoscroll,
+    Anchor, Editor, MultiBufferSnapshot, RowHighlightOptions, SelectionEffects, ToOffset, ToPoint,
+    actions::Tab, scroll::Autoscroll,
 };
 use gpui::{
     App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, SharedString, Styled,
@@ -249,9 +249,12 @@ impl GoToLine {
             let Some(start) = self.anchor_from_query(&snapshot, cx) else {
                 return;
             };
-            editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                s.select_anchor_ranges([start..start])
-            });
+            editor.change_selections(
+                SelectionEffects::scroll(Autoscroll::center()),
+                window,
+                cx,
+                |s| s.select_anchor_ranges([start..start]),
+            );
             editor.focus_handle(cx).focus(window);
             cx.notify()
         });

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::Result;
 use client::{UserStore, zed_urls};
 use copilot::{Copilot, Status};
 use editor::{
-    Editor,
+    Editor, SelectionEffects,
     actions::{ShowEditPrediction, ToggleEditPrediction},
     scroll::Autoscroll,
 };
@@ -929,9 +929,14 @@ async fn open_disabled_globs_setting_in_editor(
                     .map(|inner_match| inner_match.start()..inner_match.end())
             });
             if let Some(range) = range {
-                item.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
-                    selections.select_ranges(vec![range]);
-                });
+                item.change_selections(
+                    SelectionEffects::scroll(Autoscroll::newest()),
+                    window,
+                    cx,
+                    |selections| {
+                        selections.select_ranges(vec![range]);
+                    },
+                );
             }
         })?;
 

crates/journal/src/journal.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::Result;
 use chrono::{Datelike, Local, NaiveTime, Timelike};
-use editor::Editor;
 use editor::scroll::Autoscroll;
+use editor::{Editor, SelectionEffects};
 use gpui::{App, AppContext as _, Context, Window, actions};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -168,9 +168,12 @@ pub fn new_journal_entry(workspace: &Workspace, window: &mut Window, cx: &mut Ap
                 if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {
                     editor.update_in(cx, |editor, window, cx| {
                         let len = editor.buffer().read(cx).len(cx);
-                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                            s.select_ranges([len..len])
-                        });
+                        editor.change_selections(
+                            SelectionEffects::scroll(Autoscroll::center()),
+                            window,
+                            cx,
+                            |s| s.select_ranges([len..len]),
+                        );
                         if len > 0 {
                             editor.insert("\n\n", window, cx);
                         }

crates/language_tools/src/syntax_tree_view.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{Anchor, Editor, ExcerptId, scroll::Autoscroll};
+use editor::{Anchor, Editor, ExcerptId, SelectionEffects, scroll::Autoscroll};
 use gpui::{
     App, AppContext as _, Context, Div, Entity, EventEmitter, FocusHandle, Focusable, Hsla,
     InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement,
@@ -340,7 +340,7 @@ impl Render for SyntaxTreeView {
                                                 mem::swap(&mut range.start, &mut range.end);
 
                                                 editor.change_selections(
-                                                    Some(Autoscroll::newest()),
+                                                    SelectionEffects::scroll(Autoscroll::newest()),
                                                     window, cx,
                                                     |selections| {
                                                         selections.select_ranges(vec![range]);

crates/markdown_preview/src/markdown_preview_view.rs 🔗

@@ -4,7 +4,7 @@ use std::{ops::Range, path::PathBuf};
 
 use anyhow::Result;
 use editor::scroll::Autoscroll;
-use editor::{Editor, EditorEvent};
+use editor::{Editor, EditorEvent, SelectionEffects};
 use gpui::{
     App, ClickEvent, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
     IntoElement, ListState, ParentElement, Render, RetainAllImageCache, Styled, Subscription, Task,
@@ -468,9 +468,12 @@ impl MarkdownPreviewView {
     ) {
         if let Some(state) = &self.active_editor {
             state.editor.update(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::center()), window, cx, |selections| {
-                    selections.select_ranges(vec![selection])
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::center()),
+                    window,
+                    cx,
+                    |selections| selections.select_ranges(vec![selection]),
+                );
                 window.focus(&editor.focus_handle(cx));
             });
         }

crates/outline/src/outline.rs 🔗

@@ -4,8 +4,8 @@ use std::{
     sync::Arc,
 };
 
-use editor::RowHighlightOptions;
 use editor::{Anchor, AnchorRangeExt, Editor, scroll::Autoscroll};
+use editor::{RowHighlightOptions, SelectionEffects};
 use fuzzy::StringMatch;
 use gpui::{
     App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle,
@@ -288,9 +288,12 @@ impl PickerDelegate for OutlineViewDelegate {
                 .highlighted_rows::<OutlineRowHighlights>()
                 .next();
             if let Some((rows, _)) = highlight {
-                active_editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                    s.select_ranges([rows.start..rows.start])
-                });
+                active_editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::center()),
+                    window,
+                    cx,
+                    |s| s.select_ranges([rows.start..rows.start]),
+                );
                 active_editor.clear_row_highlights::<OutlineRowHighlights>();
                 window.focus(&active_editor.focus_handle(cx));
             }

crates/outline_panel/src/outline_panel.rs 🔗

@@ -19,10 +19,10 @@ use collections::{BTreeSet, HashMap, HashSet, hash_map};
 use db::kvp::KEY_VALUE_STORE;
 use editor::{
     AnchorRangeExt, Bias, DisplayPoint, Editor, EditorEvent, EditorSettings, ExcerptId,
-    ExcerptRange, MultiBufferSnapshot, RangeToAnchorExt, ShowScrollbar,
+    ExcerptRange, MultiBufferSnapshot, RangeToAnchorExt, SelectionEffects, ShowScrollbar,
     display_map::ToDisplayPoint,
     items::{entry_git_aware_label_color, entry_label_color},
-    scroll::{Autoscroll, AutoscrollStrategy, ScrollAnchor, ScrollbarAutoHide},
+    scroll::{Autoscroll, ScrollAnchor, ScrollbarAutoHide},
 };
 use file_icons::FileIcons;
 use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
@@ -1099,7 +1099,7 @@ impl OutlinePanel {
                 if change_selection {
                     active_editor.update(cx, |editor, cx| {
                         editor.change_selections(
-                            Some(Autoscroll::Strategy(AutoscrollStrategy::Center, None)),
+                            SelectionEffects::scroll(Autoscroll::center()),
                             window,
                             cx,
                             |s| s.select_ranges(Some(anchor..anchor)),

crates/picker/src/picker.rs 🔗

@@ -4,7 +4,7 @@ pub mod popover_menu;
 
 use anyhow::Result;
 use editor::{
-    Editor,
+    Editor, SelectionEffects,
     actions::{MoveDown, MoveUp},
     scroll::Autoscroll,
 };
@@ -695,9 +695,12 @@ impl<D: PickerDelegate> Picker<D> {
             editor.update(cx, |editor, cx| {
                 editor.set_text(query, window, cx);
                 let editor_offset = editor.buffer().read(cx).len(cx);
-                editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
-                    s.select_ranges(Some(editor_offset..editor_offset))
-                });
+                editor.change_selections(
+                    SelectionEffects::scroll(Autoscroll::Next),
+                    window,
+                    cx,
+                    |s| s.select_ranges(Some(editor_offset..editor_offset)),
+                );
             });
         }
     }

crates/project_panel/src/project_panel.rs 🔗

@@ -12,7 +12,7 @@ use editor::{
         entry_diagnostic_aware_icon_decoration_and_color,
         entry_diagnostic_aware_icon_name_and_color, entry_git_aware_label_color,
     },
-    scroll::{Autoscroll, ScrollbarAutoHide},
+    scroll::ScrollbarAutoHide,
 };
 use file_icons::FileIcons;
 use git::status::GitSummary;
@@ -1589,7 +1589,7 @@ impl ProjectPanel {
                     });
                     self.filename_editor.update(cx, |editor, cx| {
                         editor.set_text(file_name, window, cx);
-                        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                        editor.change_selections(Default::default(), window, cx, |s| {
                             s.select_ranges([selection])
                         });
                         window.focus(&editor.focus_handle(cx));

crates/project_symbols/src/project_symbols.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{Bias, Editor, scroll::Autoscroll, styled_runs_for_code_label};
+use editor::{Bias, Editor, SelectionEffects, scroll::Autoscroll, styled_runs_for_code_label};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
     App, Context, DismissEvent, Entity, FontWeight, ParentElement, StyledText, Task, WeakEntity,
@@ -136,9 +136,12 @@ impl PickerDelegate for ProjectSymbolsDelegate {
                         workspace.open_project_item::<Editor>(pane, buffer, true, true, window, cx);
 
                     editor.update(cx, |editor, cx| {
-                        editor.change_selections(Some(Autoscroll::center()), window, cx, |s| {
-                            s.select_ranges([position..position])
-                        });
+                        editor.change_selections(
+                            SelectionEffects::scroll(Autoscroll::center()),
+                            window,
+                            cx,
+                            |s| s.select_ranges([position..position]),
+                        );
                     });
                 })?;
                 anyhow::Ok(())

crates/repl/src/session.rs 🔗

@@ -8,6 +8,7 @@ use crate::{
 };
 use anyhow::Context as _;
 use collections::{HashMap, HashSet};
+use editor::SelectionEffects;
 use editor::{
     Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
     display_map::{
@@ -477,7 +478,7 @@ impl Session {
         if move_down {
             editor.update(cx, move |editor, cx| {
                 editor.change_selections(
-                    Some(Autoscroll::top_relative(8)),
+                    SelectionEffects::scroll(Autoscroll::top_relative(8)),
                     window,
                     cx,
                     |selections| {

crates/rules_library/src/rules_library.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::Result;
 use collections::{HashMap, HashSet};
-use editor::CompletionProvider;
+use editor::{CompletionProvider, SelectionEffects};
 use editor::{CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle, actions::Tab};
 use gpui::{
     Action, App, Bounds, Entity, EventEmitter, Focusable, PromptLevel, Subscription, Task,
@@ -895,10 +895,15 @@ impl RulesLibrary {
             }
             EditorEvent::Blurred => {
                 title_editor.update(cx, |title_editor, cx| {
-                    title_editor.change_selections(None, window, cx, |selections| {
-                        let cursor = selections.oldest_anchor().head();
-                        selections.select_anchor_ranges([cursor..cursor]);
-                    });
+                    title_editor.change_selections(
+                        SelectionEffects::no_scroll(),
+                        window,
+                        cx,
+                        |selections| {
+                            let cursor = selections.oldest_anchor().head();
+                            selections.select_anchor_ranges([cursor..cursor]);
+                        },
+                    );
                 });
             }
             _ => {}
@@ -920,10 +925,15 @@ impl RulesLibrary {
             }
             EditorEvent::Blurred => {
                 body_editor.update(cx, |body_editor, cx| {
-                    body_editor.change_selections(None, window, cx, |selections| {
-                        let cursor = selections.oldest_anchor().head();
-                        selections.select_anchor_ranges([cursor..cursor]);
-                    });
+                    body_editor.change_selections(
+                        SelectionEffects::no_scroll(),
+                        window,
+                        cx,
+                        |selections| {
+                            let cursor = selections.oldest_anchor().head();
+                            selections.select_anchor_ranges([cursor..cursor]);
+                        },
+                    );
                 });
             }
             _ => {}

crates/search/src/buffer_search.rs 🔗

@@ -1540,7 +1540,10 @@ mod tests {
     use std::ops::Range;
 
     use super::*;
-    use editor::{DisplayPoint, Editor, MultiBuffer, SearchSettings, display_map::DisplayRow};
+    use editor::{
+        DisplayPoint, Editor, MultiBuffer, SearchSettings, SelectionEffects,
+        display_map::DisplayRow,
+    };
     use gpui::{Hsla, TestAppContext, UpdateGlobal, VisualTestContext};
     use language::{Buffer, Point};
     use project::Project;
@@ -1677,7 +1680,7 @@ mod tests {
         });
 
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
                 ])
@@ -1764,7 +1767,7 @@ mod tests {
         // Park the cursor in between matches and ensure that going to the previous match selects
         // the closest match to the left.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
                 ])
@@ -1785,7 +1788,7 @@ mod tests {
         // Park the cursor in between matches and ensure that going to the next match selects the
         // closest match to the right.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
                 ])
@@ -1806,7 +1809,7 @@ mod tests {
         // Park the cursor after the last match and ensure that going to the previous match selects
         // the last match.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(3), 60)..DisplayPoint::new(DisplayRow(3), 60)
                 ])
@@ -1827,7 +1830,7 @@ mod tests {
         // Park the cursor after the last match and ensure that going to the next match selects the
         // first match.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(3), 60)..DisplayPoint::new(DisplayRow(3), 60)
                 ])
@@ -1848,7 +1851,7 @@ mod tests {
         // Park the cursor before the first match and ensure that going to the previous match
         // selects the last match.
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_display_ranges([
                     DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
                 ])
@@ -2625,7 +2628,7 @@ mod tests {
         });
 
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges(vec![Point::new(1, 0)..Point::new(2, 4)])
             })
         });
@@ -2708,7 +2711,7 @@ mod tests {
         });
 
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges(vec![
                     Point::new(1, 0)..Point::new(1, 4),
                     Point::new(5, 3)..Point::new(6, 4),

crates/search/src/project_search.rs 🔗

@@ -7,7 +7,7 @@ use anyhow::Context as _;
 use collections::{HashMap, HashSet};
 use editor::{
     Anchor, Editor, EditorElement, EditorEvent, EditorSettings, EditorStyle, MAX_TAB_TITLE_LEN,
-    MultiBuffer, actions::SelectAll, items::active_match_index, scroll::Autoscroll,
+    MultiBuffer, SelectionEffects, actions::SelectAll, items::active_match_index,
 };
 use futures::{StreamExt, stream::FuturesOrdered};
 use gpui::{
@@ -1303,7 +1303,7 @@ impl ProjectSearchView {
             self.results_editor.update(cx, |editor, cx| {
                 let range_to_select = editor.range_for_match(&range_to_select);
                 editor.unfold_ranges(std::slice::from_ref(&range_to_select), false, true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.select_ranges([range_to_select])
                 });
             });
@@ -1350,7 +1350,9 @@ impl ProjectSearchView {
     fn focus_results_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.query_editor.update(cx, |query_editor, cx| {
             let cursor = query_editor.selections.newest_anchor().head();
-            query_editor.change_selections(None, window, cx, |s| s.select_ranges([cursor..cursor]));
+            query_editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                s.select_ranges([cursor..cursor])
+            });
         });
         let results_handle = self.results_editor.focus_handle(cx);
         window.focus(&results_handle);
@@ -1370,7 +1372,7 @@ impl ProjectSearchView {
                     let range_to_select = match_ranges
                         .first()
                         .map(|range| editor.range_for_match(range));
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_ranges(range_to_select)
                     });
                     editor.scroll(Point::default(), Some(Axis::Vertical), window, cx);

crates/tasks_ui/src/modal.rs 🔗

@@ -751,7 +751,7 @@ fn string_match_candidates<'a>(
 mod tests {
     use std::{path::PathBuf, sync::Arc};
 
-    use editor::Editor;
+    use editor::{Editor, SelectionEffects};
     use gpui::{TestAppContext, VisualTestContext};
     use language::{Language, LanguageConfig, LanguageMatcher, Point};
     use project::{ContextProviderWithTasks, FakeFs, Project};
@@ -1028,7 +1028,7 @@ mod tests {
             .update(|_window, cx| second_item.act_as::<Editor>(cx))
             .unwrap();
         editor.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.select_ranges(Some(Point::new(1, 2)..Point::new(1, 5)))
             })
         });

crates/tasks_ui/src/tasks_ui.rs 🔗

@@ -393,7 +393,7 @@ fn worktree_context(worktree_abs_path: &Path) -> TaskContext {
 mod tests {
     use std::{collections::HashMap, sync::Arc};
 
-    use editor::Editor;
+    use editor::{Editor, SelectionEffects};
     use gpui::TestAppContext;
     use language::{Language, LanguageConfig};
     use project::{BasicContextProvider, FakeFs, Project, task_store::TaskStore};
@@ -538,7 +538,7 @@ mod tests {
 
         // And now, let's select an identifier.
         editor2.update_in(cx, |editor, window, cx| {
-            editor.change_selections(None, window, cx, |selections| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
                 selections.select_ranges([14..18])
             })
         });

crates/vim/src/change_list.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{Bias, Direction, Editor, display_map::ToDisplayPoint, movement, scroll::Autoscroll};
+use editor::{Bias, Direction, Editor, display_map::ToDisplayPoint, movement};
 use gpui::{Context, Window, actions};
 
 use crate::{Vim, state::Mode};
@@ -29,7 +29,7 @@ impl Vim {
                 .next_change(count, direction)
                 .map(|s| s.to_vec())
             {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     let map = s.display_map();
                     s.select_display_ranges(selections.iter().map(|a| {
                         let point = a.to_display_point(&map);

crates/vim/src/command.rs 🔗

@@ -2,10 +2,9 @@ use anyhow::Result;
 use collections::{HashMap, HashSet};
 use command_palette_hooks::CommandInterceptResult;
 use editor::{
-    Bias, Editor, ToPoint,
+    Bias, Editor, SelectionEffects, ToPoint,
     actions::{SortLinesCaseInsensitive, SortLinesCaseSensitive},
     display_map::ToDisplayPoint,
-    scroll::Autoscroll,
 };
 use gpui::{Action, App, AppContext as _, Context, Global, Window, actions};
 use itertools::Itertools;
@@ -422,7 +421,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
             let target = snapshot
                 .buffer_snapshot
                 .clip_point(Point::new(buffer_row.0, current.head().column), Bias::Left);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.select_ranges([target..target]);
             });
 
@@ -493,7 +492,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
                         .disjoint_anchor_ranges()
                         .collect::<Vec<_>>()
                 });
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     let end = Point::new(range.end.0, s.buffer().line_len(range.end));
                     s.select_ranges([end..Point::new(range.start.0, 0)]);
                 });
@@ -503,7 +502,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
         window.dispatch_action(action.action.boxed_clone(), cx);
         cx.defer_in(window, move |vim, window, cx| {
             vim.update_editor(window, cx, |_, editor, window, cx| {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     if let Some(previous_selections) = previous_selections {
                         s.select_ranges(previous_selections);
                     } else {
@@ -1455,15 +1454,20 @@ impl OnMatchingLines {
                 editor
                     .update_in(cx, |editor, window, cx| {
                         editor.start_transaction_at(Instant::now(), window, cx);
-                        editor.change_selections(None, window, cx, |s| {
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                             s.replace_cursors_with(|_| new_selections);
                         });
                         window.dispatch_action(action, cx);
                         cx.defer_in(window, move |editor, window, cx| {
                             let newest = editor.selections.newest::<Point>(cx).clone();
-                            editor.change_selections(None, window, cx, |s| {
-                                s.select(vec![newest]);
-                            });
+                            editor.change_selections(
+                                SelectionEffects::no_scroll(),
+                                window,
+                                cx,
+                                |s| {
+                                    s.select(vec![newest]);
+                                },
+                            );
                             editor.end_transaction_at(Instant::now(), cx);
                         })
                     })
@@ -1566,7 +1570,7 @@ impl Vim {
                 )
                 .unwrap_or((start.range(), MotionKind::Exclusive));
             if range.start != start.start {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([
                         range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
                     ]);
@@ -1606,7 +1610,7 @@ impl Vim {
                 .range(&snapshot, start.clone(), around)
                 .unwrap_or(start.range());
             if range.start != start.start {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges([
                         range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
                     ]);
@@ -1799,7 +1803,7 @@ impl ShellExec {
                     editor.transact(window, cx, |editor, window, cx| {
                         editor.edit([(range.clone(), text)], cx);
                         let snapshot = editor.buffer().read(cx).snapshot(cx);
-                        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                        editor.change_selections(Default::default(), window, cx, |s| {
                             let point = if is_read {
                                 let point = range.end.to_point(&snapshot);
                                 Point::new(point.row.saturating_sub(1), 0)

crates/vim/src/helix.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{DisplayPoint, Editor, movement, scroll::Autoscroll};
+use editor::{DisplayPoint, Editor, movement};
 use gpui::{Action, actions};
 use gpui::{Context, Window};
 use language::{CharClassifier, CharKind};
@@ -47,7 +47,7 @@ impl Vim {
         mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
     ) {
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let times = times.unwrap_or(1);
                     let new_goal = SelectionGoal::None;
@@ -100,7 +100,7 @@ impl Vim {
         mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
     ) {
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let times = times.unwrap_or(1);
                     let new_goal = SelectionGoal::None;
@@ -161,7 +161,7 @@ impl Vim {
     ) {
         self.update_editor(window, cx, |_, editor, window, cx| {
             let text_layout_details = editor.text_layout_details(window);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     let goal = selection.goal;
                     let cursor = if selection.is_empty() || selection.reversed {
@@ -239,7 +239,7 @@ impl Vim {
             Motion::FindForward { .. } => {
                 self.update_editor(window, cx, |_, editor, window, cx| {
                     let text_layout_details = editor.text_layout_details(window);
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.move_with(|map, selection| {
                             let goal = selection.goal;
                             let cursor = if selection.is_empty() || selection.reversed {
@@ -266,7 +266,7 @@ impl Vim {
             Motion::FindBackward { .. } => {
                 self.update_editor(window, cx, |_, editor, window, cx| {
                     let text_layout_details = editor.text_layout_details(window);
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.move_with(|map, selection| {
                             let goal = selection.goal;
                             let cursor = if selection.is_empty() || selection.reversed {

crates/vim/src/indent.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{Vim, motion::Motion, object::Object, state::Mode};
 use collections::HashMap;
+use editor::SelectionEffects;
 use editor::{Bias, Editor, display_map::ToDisplayPoint};
 use gpui::actions;
 use gpui::{Context, Window};
@@ -88,7 +89,7 @@ impl Vim {
             let text_layout_details = editor.text_layout_details(window);
             editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
@@ -106,7 +107,7 @@ impl Vim {
                     IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
                     IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
                 }
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -128,7 +129,7 @@ impl Vim {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
@@ -140,7 +141,7 @@ impl Vim {
                     IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
                     IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
                 }
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

crates/vim/src/insert.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{Vim, state::Mode};
-use editor::{Bias, Editor, scroll::Autoscroll};
+use editor::{Bias, Editor};
 use gpui::{Action, Context, Window, actions};
 use language::SelectionGoal;
 use settings::Settings;
@@ -34,7 +34,7 @@ impl Vim {
                 editor.dismiss_menus_and_popups(false, window, cx);
 
                 if !HelixModeSetting::get_global(cx).0 {
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.move_cursors_with(|map, mut cursor, _| {
                             *cursor.column_mut() = cursor.column().saturating_sub(1);
                             (map.clip_point(cursor, Bias::Left), SelectionGoal::None)

crates/vim/src/motion.rs 🔗

@@ -4,7 +4,6 @@ use editor::{
     movement::{
         self, FindRange, TextLayoutDetails, find_boundary, find_preceding_boundary_display_point,
     },
-    scroll::Autoscroll,
 };
 use gpui::{Action, Context, Window, actions, px};
 use language::{CharKind, Point, Selection, SelectionGoal};
@@ -626,7 +625,7 @@ impl Vim {
                 Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
                     if !prior_selections.is_empty() {
                         self.update_editor(window, cx, |_, editor, window, cx| {
-                            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                            editor.change_selections(Default::default(), window, cx, |s| {
                                 s.select_ranges(prior_selections.iter().cloned())
                             })
                         });

crates/vim/src/normal.rs 🔗

@@ -26,7 +26,6 @@ use collections::BTreeSet;
 use convert::ConvertTarget;
 use editor::Bias;
 use editor::Editor;
-use editor::scroll::Autoscroll;
 use editor::{Anchor, SelectionEffects};
 use editor::{display_map::ToDisplayPoint, movement};
 use gpui::{Context, Window, actions};
@@ -103,7 +102,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
     Vim::action(editor, cx, |vim, _: &HelixDelete, window, cx| {
         vim.record_current_action(cx);
         vim.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if selection.is_empty() {
                         selection.end = movement::right(map, selection.end)
@@ -377,7 +376,7 @@ impl Vim {
         self.start_recording(cx);
         self.switch_mode(Mode::Insert, false, window, cx);
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
             });
         });
@@ -388,7 +387,7 @@ impl Vim {
         if self.mode.is_visual() {
             let current_mode = self.mode;
             self.update_editor(window, cx, |_, editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if current_mode == Mode::VisualLine {
                             let start_of_line = motion::start_of_line(map, false, selection.start);
@@ -412,7 +411,7 @@ impl Vim {
         self.start_recording(cx);
         self.switch_mode(Mode::Insert, false, window, cx);
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (
                         first_non_whitespace(map, false, cursor),
@@ -432,7 +431,7 @@ impl Vim {
         self.start_recording(cx);
         self.switch_mode(Mode::Insert, false, window, cx);
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (next_line_end(map, cursor, 1), SelectionGoal::None)
                 });
@@ -453,7 +452,7 @@ impl Vim {
                 return;
             };
 
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.select_anchor_ranges(marks.iter().map(|mark| *mark..*mark))
             });
         });
@@ -489,7 +488,7 @@ impl Vim {
                     })
                     .collect::<Vec<_>>();
                 editor.edit_with_autoindent(edits, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_cursors_with(|map, cursor, _| {
                         let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
                         let insert_point = motion::end_of_line(map, false, previous_line, 1);
@@ -530,7 +529,7 @@ impl Vim {
                         (end_of_line..end_of_line, "\n".to_string() + &indent)
                     })
                     .collect::<Vec<_>>();
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.maybe_move_cursors_with(|map, cursor, goal| {
                         Motion::CurrentLine.move_point(
                             map,
@@ -607,7 +606,7 @@ impl Vim {
                     .collect::<Vec<_>>();
                 editor.edit(edits, cx);
 
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|_, selection| {
                         if let Some(position) = original_positions.get(&selection.id) {
                             selection.collapse_to(*position, SelectionGoal::None);
@@ -755,7 +754,7 @@ impl Vim {
                     editor.newline(&editor::actions::Newline, window, cx);
                 }
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let point = movement::saturating_left(map, selection.head());
                         selection.collapse_to(point, SelectionGoal::None)
@@ -791,7 +790,7 @@ impl Vim {
         cx: &mut Context<Editor>,
         mut positions: HashMap<usize, Anchor>,
     ) {
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        editor.change_selections(Default::default(), window, cx, |s| {
             s.move_with(|map, selection| {
                 if let Some(anchor) = positions.remove(&selection.id) {
                     selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

crates/vim/src/normal/change.rs 🔗

@@ -8,7 +8,6 @@ use editor::{
     Bias, DisplayPoint,
     display_map::{DisplaySnapshot, ToDisplayPoint},
     movement::TextLayoutDetails,
-    scroll::Autoscroll,
 };
 use gpui::{Context, Window};
 use language::Selection;
@@ -40,7 +39,7 @@ impl Vim {
             editor.transact(window, cx, |editor, window, cx| {
                 // We are swapping to insert mode anyway. Just set the line end clipping behavior now
                 editor.set_clip_at_line_ends(false, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let kind = match motion {
                             Motion::NextWordStart { ignore_punctuation }
@@ -114,7 +113,7 @@ impl Vim {
             // We are swapping to insert mode anyway. Just set the line end clipping behavior now
             editor.set_clip_at_line_ends(false, cx);
             editor.transact(window, cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         objects_found |= object.expand_selection(map, selection, around);
                     });

crates/vim/src/normal/convert.rs 🔗

@@ -1,5 +1,5 @@
 use collections::HashMap;
-use editor::{display_map::ToDisplayPoint, scroll::Autoscroll};
+use editor::{SelectionEffects, display_map::ToDisplayPoint};
 use gpui::{Context, Window};
 use language::{Bias, Point, SelectionGoal};
 use multi_buffer::MultiBufferRow;
@@ -36,7 +36,7 @@ impl Vim {
             let text_layout_details = editor.text_layout_details(window);
             editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Left);
                         selection_starts.insert(selection.id, anchor);
@@ -66,7 +66,7 @@ impl Vim {
                         editor.convert_to_rot47(&Default::default(), window, cx)
                     }
                 }
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -90,7 +90,7 @@ impl Vim {
             editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                         original_positions.insert(
@@ -116,7 +116,7 @@ impl Vim {
                         editor.convert_to_rot47(&Default::default(), window, cx)
                     }
                 }
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -239,7 +239,7 @@ impl Vim {
                         .collect::<String>();
                     editor.edit([(range, text)], cx)
                 }
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.select_ranges(cursor_positions)
                 })
             });

crates/vim/src/normal/delete.rs 🔗

@@ -7,7 +7,6 @@ use collections::{HashMap, HashSet};
 use editor::{
     Bias, DisplayPoint,
     display_map::{DisplaySnapshot, ToDisplayPoint},
-    scroll::Autoscroll,
 };
 use gpui::{Context, Window};
 use language::{Point, Selection};
@@ -30,7 +29,7 @@ impl Vim {
                 let mut original_columns: HashMap<_, _> = Default::default();
                 let mut motion_kind = None;
                 let mut ranges_to_copy = Vec::new();
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let original_head = selection.head();
                         original_columns.insert(selection.id, original_head.column());
@@ -71,7 +70,7 @@ impl Vim {
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head();
                         if kind.linewise() {
@@ -102,7 +101,7 @@ impl Vim {
                 // Emulates behavior in vim where if we expanded backwards to include a newline
                 // the cursor gets set back to the start of the line
                 let mut should_move_to_start: HashSet<_> = Default::default();
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                         let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
@@ -159,7 +158,7 @@ impl Vim {
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head();
                         if should_move_to_start.contains(&selection.id) {

crates/vim/src/normal/increment.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{Editor, MultiBufferSnapshot, ToOffset, ToPoint, scroll::Autoscroll};
+use editor::{Editor, MultiBufferSnapshot, ToOffset, ToPoint};
 use gpui::{Action, Context, Window};
 use language::{Bias, Point};
 use schemars::JsonSchema;
@@ -97,7 +97,7 @@ impl Vim {
                 editor.edit(edits, cx);
 
                 let snapshot = editor.buffer().read(cx).snapshot(cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     let mut new_ranges = Vec::new();
                     for (visual, anchor) in new_anchors.iter() {
                         let mut point = anchor.to_point(&snapshot);

crates/vim/src/normal/mark.rs 🔗

@@ -4,7 +4,6 @@ use editor::{
     Anchor, Bias, DisplayPoint, Editor, MultiBuffer,
     display_map::{DisplaySnapshot, ToDisplayPoint},
     movement,
-    scroll::Autoscroll,
 };
 use gpui::{Context, Entity, EntityId, UpdateGlobal, Window};
 use language::SelectionGoal;
@@ -116,7 +115,7 @@ impl Vim {
                     }
                 }
 
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.select_anchor_ranges(ranges)
                 });
             })
@@ -169,7 +168,7 @@ impl Vim {
                                 }
                             })
                             .collect();
-                        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                        editor.change_selections(Default::default(), window, cx, |s| {
                             s.select_ranges(points.into_iter().map(|p| p..p))
                         })
                     })
@@ -251,7 +250,7 @@ impl Vim {
                 }
 
                 if !should_jump && !ranges.is_empty() {
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_anchor_ranges(ranges)
                     });
                 }

crates/vim/src/normal/paste.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{DisplayPoint, RowExt, display_map::ToDisplayPoint, movement, scroll::Autoscroll};
+use editor::{DisplayPoint, RowExt, SelectionEffects, display_map::ToDisplayPoint, movement};
 use gpui::{Action, Context, Window};
 use language::{Bias, SelectionGoal};
 use schemars::JsonSchema;
@@ -187,7 +187,7 @@ impl Vim {
                 // and put the cursor on the first non-blank character of the first inserted line (or at the end if the first line is blank).
                 // otherwise vim will insert the next text at (or before) the current cursor position,
                 // the cursor will go to the last (or first, if is_multiline) inserted character.
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.replace_cursors_with(|map| {
                         let mut cursors = Vec::new();
                         for (anchor, line_mode, is_multiline) in &new_selections {
@@ -238,7 +238,7 @@ impl Vim {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                     });
@@ -252,7 +252,7 @@ impl Vim {
                 };
                 editor.insert(&text, window, cx);
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         selection.start = map.clip_point(selection.start, Bias::Left);
                         selection.end = selection.start
@@ -276,7 +276,7 @@ impl Vim {
             let text_layout_details = editor.text_layout_details(window);
             editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         motion.expand_selection(
                             map,
@@ -296,7 +296,7 @@ impl Vim {
                 };
                 editor.insert(&text, window, cx);
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         selection.start = map.clip_point(selection.start, Bias::Left);
                         selection.end = selection.start

crates/vim/src/normal/substitute.rs 🔗

@@ -1,4 +1,4 @@
-use editor::{Editor, movement};
+use editor::{Editor, SelectionEffects, movement};
 use gpui::{Context, Window, actions};
 use language::Point;
 
@@ -41,7 +41,7 @@ impl Vim {
             editor.set_clip_at_line_ends(false, cx);
             editor.transact(window, cx, |editor, window, cx| {
                 let text_layout_details = editor.text_layout_details(window);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if selection.start == selection.end {
                             Motion::Right.expand_selection(

crates/vim/src/normal/toggle_comments.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{Vim, motion::Motion, object::Object};
 use collections::HashMap;
-use editor::{Bias, display_map::ToDisplayPoint};
+use editor::{Bias, SelectionEffects, display_map::ToDisplayPoint};
 use gpui::{Context, Window};
 use language::SelectionGoal;
 
@@ -18,7 +18,7 @@ impl Vim {
             let text_layout_details = editor.text_layout_details(window);
             editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
@@ -32,7 +32,7 @@ impl Vim {
                     });
                 });
                 editor.toggle_comments(&Default::default(), window, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@@ -53,7 +53,7 @@ impl Vim {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
@@ -61,7 +61,7 @@ impl Vim {
                     });
                 });
                 editor.toggle_comments(&Default::default(), window, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

crates/vim/src/normal/yank.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
     state::{Mode, Register},
 };
 use collections::HashMap;
-use editor::{ClipboardSelection, Editor};
+use editor::{ClipboardSelection, Editor, SelectionEffects};
 use gpui::Context;
 use gpui::Window;
 use language::Point;
@@ -31,7 +31,7 @@ impl Vim {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut original_positions: HashMap<_, _> = Default::default();
                 let mut kind = None;
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let original_position = (selection.head(), selection.goal);
                         kind = motion.expand_selection(
@@ -51,7 +51,7 @@ impl Vim {
                 });
                 let Some(kind) = kind else { return };
                 vim.yank_selections_content(editor, kind, window, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|_, selection| {
                         let (head, goal) = original_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(head, goal);
@@ -73,7 +73,7 @@ impl Vim {
             editor.transact(window, cx, |editor, window, cx| {
                 editor.set_clip_at_line_ends(false, cx);
                 let mut start_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         object.expand_selection(map, selection, around);
                         let start_position = (selection.start, selection.goal);
@@ -81,7 +81,7 @@ impl Vim {
                     });
                 });
                 vim.yank_selections_content(editor, MotionKind::Exclusive, window, cx);
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|_, selection| {
                         let (head, goal) = start_positions.remove(&selection.id).unwrap();
                         selection.collapse_to(head, goal);

crates/vim/src/replace.rs 🔗

@@ -5,8 +5,8 @@ use crate::{
     state::Mode,
 };
 use editor::{
-    Anchor, Bias, Editor, EditorSnapshot, ToOffset, ToPoint, display_map::ToDisplayPoint,
-    scroll::Autoscroll,
+    Anchor, Bias, Editor, EditorSnapshot, SelectionEffects, ToOffset, ToPoint,
+    display_map::ToDisplayPoint,
 };
 use gpui::{Context, Window, actions};
 use language::{Point, SelectionGoal};
@@ -72,7 +72,7 @@ impl Vim {
 
                 editor.edit_with_block_indent(edits.clone(), Vec::new(), cx);
 
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_anchor_ranges(edits.iter().map(|(range, _)| range.end..range.end));
                 });
                 editor.set_clip_at_line_ends(true, cx);
@@ -124,7 +124,7 @@ impl Vim {
 
                 editor.edit(edits, cx);
 
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.select_ranges(new_selections);
                 });
                 editor.set_clip_at_line_ends(true, cx);
@@ -251,7 +251,7 @@ impl Vim {
             }
 
             if let Some(position) = final_cursor_position {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|_map, selection| {
                         selection.collapse_to(position, SelectionGoal::None);
                     });

crates/vim/src/rewrap.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{Vim, motion::Motion, object::Object, state::Mode};
 use collections::HashMap;
-use editor::{Bias, Editor, RewrapOptions, display_map::ToDisplayPoint, scroll::Autoscroll};
+use editor::{Bias, Editor, RewrapOptions, SelectionEffects, display_map::ToDisplayPoint};
 use gpui::{Context, Window, actions};
 use language::SelectionGoal;
 
@@ -22,7 +22,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
                     },
                     cx,
                 );
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if let Some(anchor) = positions.remove(&selection.id) {
                             let mut point = anchor.to_display_point(map);
@@ -53,7 +53,7 @@ impl Vim {
             let text_layout_details = editor.text_layout_details(window);
             editor.transact(window, cx, |editor, window, cx| {
                 let mut selection_starts: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         selection_starts.insert(selection.id, anchor);
@@ -73,7 +73,7 @@ impl Vim {
                     },
                     cx,
                 );
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = selection_starts.remove(&selection.id).unwrap();
                         let mut point = anchor.to_display_point(map);
@@ -96,7 +96,7 @@ impl Vim {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.transact(window, cx, |editor, window, cx| {
                 let mut original_positions: HashMap<_, _> = Default::default();
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
                         original_positions.insert(selection.id, anchor);
@@ -110,7 +110,7 @@ impl Vim {
                     },
                     cx,
                 );
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let anchor = original_positions.remove(&selection.id).unwrap();
                         let mut point = anchor.to_display_point(map);

crates/vim/src/surrounds.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
     object::Object,
     state::Mode,
 };
-use editor::{Bias, movement, scroll::Autoscroll};
+use editor::{Bias, movement};
 use gpui::{Context, Window};
 use language::BracketPair;
 
@@ -109,7 +109,7 @@ impl Vim {
 
                 editor.edit(edits, cx);
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     if mode == Mode::VisualBlock {
                         s.select_anchor_ranges(anchors.into_iter().take(1))
                     } else {
@@ -207,7 +207,7 @@ impl Vim {
                     }
                 }
 
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.select_ranges(anchors);
                 });
                 edits.sort_by_key(|(range, _)| range.start);
@@ -317,7 +317,7 @@ impl Vim {
                     edits.sort_by_key(|(range, _)| range.start);
                     editor.edit(edits, cx);
                     editor.set_clip_at_line_ends(true, cx);
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_anchor_ranges(stable_anchors);
                     });
                 });
@@ -375,7 +375,7 @@ impl Vim {
                             anchors.push(start..start)
                         }
                     }
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_ranges(anchors);
                     });
                     editor.set_clip_at_line_ends(true, cx);

crates/vim/src/vim.rs 🔗

@@ -22,7 +22,8 @@ mod visual;
 use anyhow::Result;
 use collections::HashMap;
 use editor::{
-    Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, ToPoint,
+    Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, SelectionEffects,
+    ToPoint,
     movement::{self, FindRange},
 };
 use gpui::{
@@ -963,7 +964,7 @@ impl Vim {
                 }
             }
 
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 // we cheat with visual block mode and use multiple cursors.
                 // the cost of this cheat is we need to convert back to a single
                 // cursor whenever vim would.
@@ -1163,7 +1164,7 @@ impl Vim {
             } else {
                 self.update_editor(window, cx, |_, editor, window, cx| {
                     editor.set_clip_at_line_ends(false, cx);
-                    editor.change_selections(None, window, cx, |s| {
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                         s.move_with(|_, selection| {
                             selection.collapse_to(selection.start, selection.goal)
                         })
@@ -1438,27 +1439,29 @@ impl Vim {
             Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
                 self.update_editor(window, cx, |vim, editor, window, cx| {
                     let original_mode = vim.undo_modes.get(transaction_id);
-                    editor.change_selections(None, window, cx, |s| match original_mode {
-                        Some(Mode::VisualLine) => {
-                            s.move_with(|map, selection| {
-                                selection.collapse_to(
-                                    map.prev_line_boundary(selection.start.to_point(map)).1,
-                                    SelectionGoal::None,
-                                )
-                            });
-                        }
-                        Some(Mode::VisualBlock) => {
-                            let mut first = s.first_anchor();
-                            first.collapse_to(first.start, first.goal);
-                            s.select_anchors(vec![first]);
-                        }
-                        _ => {
-                            s.move_with(|map, selection| {
-                                selection.collapse_to(
-                                    map.clip_at_line_end(selection.start),
-                                    selection.goal,
-                                );
-                            });
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                        match original_mode {
+                            Some(Mode::VisualLine) => {
+                                s.move_with(|map, selection| {
+                                    selection.collapse_to(
+                                        map.prev_line_boundary(selection.start.to_point(map)).1,
+                                        SelectionGoal::None,
+                                    )
+                                });
+                            }
+                            Some(Mode::VisualBlock) => {
+                                let mut first = s.first_anchor();
+                                first.collapse_to(first.start, first.goal);
+                                s.select_anchors(vec![first]);
+                            }
+                            _ => {
+                                s.move_with(|map, selection| {
+                                    selection.collapse_to(
+                                        map.clip_at_line_end(selection.start),
+                                        selection.goal,
+                                    );
+                                });
+                            }
                         }
                     });
                 });
@@ -1466,7 +1469,7 @@ impl Vim {
             }
             Mode::Normal => {
                 self.update_editor(window, cx, |_, editor, window, cx| {
-                    editor.change_selections(None, window, cx, |s| {
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                         s.move_with(|map, selection| {
                             selection
                                 .collapse_to(map.clip_at_line_end(selection.end), selection.goal)

crates/vim/src/visual.rs 🔗

@@ -2,10 +2,9 @@ use std::sync::Arc;
 
 use collections::HashMap;
 use editor::{
-    Bias, DisplayPoint, Editor,
+    Bias, DisplayPoint, Editor, SelectionEffects,
     display_map::{DisplaySnapshot, ToDisplayPoint},
     movement,
-    scroll::Autoscroll,
 };
 use gpui::{Context, Window, actions};
 use language::{Point, Selection, SelectionGoal};
@@ -133,7 +132,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
 
         vim.update_editor(window, cx, |_, editor, window, cx| {
             editor.set_clip_at_line_ends(false, cx);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 let map = s.display_map();
                 let ranges = ranges
                     .into_iter()
@@ -187,7 +186,7 @@ impl Vim {
                     motion.move_point(map, point, goal, times, &text_layout_details)
                 })
             } else {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let was_reversed = selection.reversed;
                         let mut current_head = selection.head();
@@ -259,7 +258,7 @@ impl Vim {
         ) -> Option<(DisplayPoint, SelectionGoal)>,
     ) {
         let text_layout_details = editor.text_layout_details(window);
-        editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+        editor.change_selections(Default::default(), window, cx, |s| {
             let map = &s.display_map();
             let mut head = s.newest_anchor().head().to_display_point(map);
             let mut tail = s.oldest_anchor().tail().to_display_point(map);
@@ -375,7 +374,7 @@ impl Vim {
             }
 
             self.update_editor(window, cx, |_, editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut mut_selection = selection.clone();
 
@@ -454,7 +453,7 @@ impl Vim {
     ) {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.split_selection_into_lines(&Default::default(), window, cx);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (next_line_end(map, cursor, 1), SelectionGoal::None)
                 });
@@ -472,7 +471,7 @@ impl Vim {
     ) {
         self.update_editor(window, cx, |_, editor, window, cx| {
             editor.split_selection_into_lines(&Default::default(), window, cx);
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_cursors_with(|map, cursor, _| {
                     (
                         first_non_whitespace(map, false, cursor),
@@ -495,7 +494,7 @@ impl Vim {
 
     pub fn other_end(&mut self, _: &OtherEnd, window: &mut Window, cx: &mut Context<Self>) {
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|_, selection| {
                     selection.reversed = !selection.reversed;
                 });
@@ -511,7 +510,7 @@ impl Vim {
     ) {
         let mode = self.mode;
         self.update_editor(window, cx, |_, editor, window, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.move_with(|_, selection| {
                     selection.reversed = !selection.reversed;
                 });
@@ -530,7 +529,7 @@ impl Vim {
             editor.selections.line_mode = false;
 
             editor.transact(window, cx, |editor, window, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         if line_mode {
                             let mut position = selection.head();
@@ -567,7 +566,7 @@ impl Vim {
                 vim.copy_selections_content(editor, kind, window, cx);
 
                 if line_mode && vim.mode != Mode::VisualBlock {
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.move_with(|map, selection| {
                             let end = selection.end.to_point(map);
                             let start = selection.start.to_point(map);
@@ -587,7 +586,7 @@ impl Vim {
 
                 // Fixup cursor position after the deletion
                 editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                editor.change_selections(Default::default(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let mut cursor = selection.head().to_point(map);
 
@@ -613,7 +612,7 @@ impl Vim {
 
             // For visual line mode, adjust selections to avoid yanking the next line when on \n
             if line_mode && vim.mode != Mode::VisualBlock {
-                editor.change_selections(None, window, cx, |s| {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                     s.move_with(|map, selection| {
                         let start = selection.start.to_point(map);
                         let end = selection.end.to_point(map);
@@ -634,7 +633,7 @@ impl Vim {
                 MotionKind::Exclusive
             };
             vim.yank_selections_content(editor, kind, window, cx);
-            editor.change_selections(None, window, cx, |s| {
+            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                 s.move_with(|map, selection| {
                     if line_mode {
                         selection.start = start_of_line(map, false, selection.start);
@@ -687,7 +686,9 @@ impl Vim {
                 }
 
                 editor.edit(edits, cx);
-                editor.change_selections(None, window, cx, |s| s.select_ranges(stable_anchors));
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+                    s.select_ranges(stable_anchors)
+                });
             });
         });
         self.switch_mode(Mode::Normal, false, window, cx);
@@ -799,7 +800,7 @@ impl Vim {
             if direction == Direction::Prev {
                 std::mem::swap(&mut start_selection, &mut end_selection);
             }
-            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+            editor.change_selections(Default::default(), window, cx, |s| {
                 s.select_ranges([start_selection..end_selection]);
             });
             editor.set_collapse_matches(true);

crates/zed/src/zed.rs 🔗

@@ -18,7 +18,7 @@ use client::zed_urls;
 use collections::VecDeque;
 use debugger_ui::debugger_panel::DebugPanel;
 use editor::ProposedChangesEditorToolbar;
-use editor::{Editor, MultiBuffer, scroll::Autoscroll};
+use editor::{Editor, MultiBuffer};
 use futures::future::Either;
 use futures::{StreamExt, channel::mpsc, select_biased};
 use git_ui::git_panel::GitPanel;
@@ -1125,7 +1125,7 @@ fn open_log_file(workspace: &mut Workspace, window: &mut Window, cx: &mut Contex
 
                         editor.update(cx, |editor, cx| {
                             let last_multi_buffer_offset = editor.buffer().read(cx).len(cx);
-                            editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                            editor.change_selections(Default::default(), window, cx, |s| {
                                 s.select_ranges(Some(
                                     last_multi_buffer_offset..last_multi_buffer_offset,
                                 ));
@@ -1774,7 +1774,7 @@ mod tests {
     use super::*;
     use assets::Assets;
     use collections::HashSet;
-    use editor::{DisplayPoint, Editor, display_map::DisplayRow, scroll::Autoscroll};
+    use editor::{DisplayPoint, Editor, SelectionEffects, display_map::DisplayRow};
     use gpui::{
         Action, AnyWindowHandle, App, AssetSource, BorrowAppContext, SemanticVersion,
         TestAppContext, UpdateGlobal, VisualTestContext, WindowHandle, actions,
@@ -3348,7 +3348,7 @@ mod tests {
         workspace
             .update(cx, |_, window, cx| {
                 editor1.update(cx, |editor, cx| {
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_display_ranges([DisplayPoint::new(DisplayRow(10), 0)
                             ..DisplayPoint::new(DisplayRow(10), 0)])
                     });
@@ -3378,7 +3378,7 @@ mod tests {
         workspace
             .update(cx, |_, window, cx| {
                 editor3.update(cx, |editor, cx| {
-                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
+                    editor.change_selections(Default::default(), window, cx, |s| {
                         s.select_display_ranges([DisplayPoint::new(DisplayRow(12), 0)
                             ..DisplayPoint::new(DisplayRow(12), 0)])
                     });
@@ -3593,7 +3593,7 @@ mod tests {
         workspace
             .update(cx, |_, window, cx| {
                 editor1.update(cx, |editor, cx| {
-                    editor.change_selections(None, window, cx, |s| {
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                         s.select_display_ranges([DisplayPoint::new(DisplayRow(15), 0)
                             ..DisplayPoint::new(DisplayRow(15), 0)])
                     })
@@ -3604,7 +3604,7 @@ mod tests {
             workspace
                 .update(cx, |_, window, cx| {
                     editor1.update(cx, |editor, cx| {
-                        editor.change_selections(None, window, cx, |s| {
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                             s.select_display_ranges([DisplayPoint::new(DisplayRow(3), 0)
                                 ..DisplayPoint::new(DisplayRow(3), 0)])
                         });
@@ -3615,7 +3615,7 @@ mod tests {
             workspace
                 .update(cx, |_, window, cx| {
                     editor1.update(cx, |editor, cx| {
-                        editor.change_selections(None, window, cx, |s| {
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                             s.select_display_ranges([DisplayPoint::new(DisplayRow(13), 0)
                                 ..DisplayPoint::new(DisplayRow(13), 0)])
                         })
@@ -3627,7 +3627,7 @@ mod tests {
             .update(cx, |_, window, cx| {
                 editor1.update(cx, |editor, cx| {
                     editor.transact(window, cx, |editor, window, cx| {
-                        editor.change_selections(None, window, cx, |s| {
+                        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                             s.select_display_ranges([DisplayPoint::new(DisplayRow(2), 0)
                                 ..DisplayPoint::new(DisplayRow(14), 0)])
                         });
@@ -3640,7 +3640,7 @@ mod tests {
         workspace
             .update(cx, |_, window, cx| {
                 editor1.update(cx, |editor, cx| {
-                    editor.change_selections(None, window, cx, |s| {
+                    editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
                         s.select_display_ranges([DisplayPoint::new(DisplayRow(1), 0)
                             ..DisplayPoint::new(DisplayRow(1), 0)])
                     })