feat: configure cursor distance from viewport edges

This commit is contained in:
2026-10-13 22:10:47 +02:00
parent 00628e0efd
commit b58fb085a3
11 changed files with 592 additions and 82 deletions
+468 -75
View File
@@ -2,6 +2,7 @@ use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
fmt::Write as _,
hash::{Hash, Hasher},
io::{self, Stdout},
panic::{self, AssertUnwindSafe, PanicHookInfo},
sync::{Arc, LazyLock, Mutex},
@@ -277,6 +278,27 @@ fn clamp_visual_bottom(
start
}
fn scroll_margin(size: usize, distance: Option<usize>) -> usize {
distance.unwrap_or(size / 2).min(size / 2)
}
fn scroll_offset(top: usize, cursor: usize, size: usize, distance: Option<usize>) -> usize {
if size == 0 {
return top;
}
let margin = scroll_margin(size, distance);
if margin == size / 2 {
return cursor.saturating_sub(margin);
}
if cursor < top.saturating_add(margin) {
cursor.saturating_sub(margin)
} else if cursor >= top.saturating_add(size - margin) {
cursor.saturating_sub(size - margin - 1)
} else {
top
}
}
#[derive(Default)]
struct BufferViewports {
offsets: BTreeMap<usize, (usize, usize)>,
@@ -508,6 +530,7 @@ impl BufferViewports {
if let Some(panel) = dock
&& let Some(picker) = editor.file_picker()
{
self.panels.picker_scroll.distance = editor.scroll_distance();
draw_file_list(
frame,
picker,
@@ -1617,7 +1640,7 @@ fn draw_document(
),
));
if !wrapped {
let limit = if document.anchor.is_some() {
let limit = if document.anchor.is_some() || editor.scroll_distance() != Some(0) {
editor.lines().len() - 1
} else {
editor.lines().len().saturating_sub(height)
@@ -1661,7 +1684,24 @@ fn draw_document(
column,
),
};
if let Some(row) = toggle_row {
let margin = scroll_margin(height, editor.scroll_distance());
let anchor = toggle_row.or_else(|| {
if margin == height / 2 {
return Some(margin);
}
let (first, count) =
visual_page(&mut document.wrap, editor.lines(), start, margin + 1);
let last =
visual_page(&mut document.wrap, editor.lines(), start, height - margin).0;
if caret < first || count <= margin {
Some(margin)
} else if caret > last {
Some(height - margin - 1)
} else {
None
}
});
if let Some(row) = anchor {
start = caret;
for _ in 0..row {
let Some(previous) =
@@ -1671,21 +1711,9 @@ fn draw_document(
};
start = previous;
}
} else if caret < start {
start = caret;
} else if caret > visual_page(&mut document.wrap, editor.lines(), start, height).0 {
start = caret;
for _ in 1..height {
let Some(previous) =
previous_visual(&mut document.wrap, editor.lines(), start)
else {
break;
};
start = previous;
}
}
}
if document.anchor.is_none() {
if document.anchor.is_none() && editor.scroll_distance() == Some(0) {
start = clamp_visual_bottom(&mut document.wrap, editor.lines(), start, height);
}
*top = start.source;
@@ -1706,17 +1734,11 @@ fn draw_document(
}
} else {
if document.manual.is_none() {
if cursor.row < *top {
*top = cursor.row;
} else if cursor.row >= top.saturating_add(height) {
*top = cursor.row + 1 - height;
if toggle_row.is_none() {
*top = scroll_offset(*top, cursor.row, height, editor.scroll_distance());
}
let column = display_column(&editor.lines()[cursor.row], cursor.column);
if column < *left {
*left = column;
} else if column >= left.saturating_add(width) {
*left = column + 1 - width;
}
*left = scroll_offset(*left, column, width, editor.scroll_distance());
}
document.visible.extend(
(*top..top.saturating_add(height).min(editor.lines().len())).map(|row| {
@@ -1923,6 +1945,7 @@ fn draw_document(
if let Some(panel) = dock
&& let Some(picker) = editor.file_picker()
{
panels.picker_scroll.distance = editor.scroll_distance();
draw_file_list(
frame,
picker,
@@ -2059,6 +2082,16 @@ fn draw_overlays(
caret: Option<(Rect, Position)>,
panels: &mut WheelPanels,
) {
panels.picker_scroll.distance = editor.scroll_distance();
if editor.completion().is_none() {
panels.picker_scroll.completion = None;
}
if editor.palette().is_none() {
panels.picker_scroll.palette = None;
}
if editor.diff().is_none_or(|diff| diff.phase == Phase::View) {
panels.picker_scroll.revisions = None;
}
panels.input = None;
panels.notification = None;
let covered = editor.completion().is_some()
@@ -2089,7 +2122,7 @@ fn draw_overlays(
if let Some(completion) = editor.completion()
&& let Some((_, caret)) = caret
{
panels.completion = draw_completion(frame, completion, area, caret);
panels.completion = draw_completion(frame, completion, area, caret, panels);
}
if let Some(picker) = editor.file_picker()
&& picker.presentation == FilePresentation::Floating
@@ -2116,6 +2149,7 @@ fn draw_overlays(
area,
editor.mode(),
editor.selection_navigation_hint(),
panels,
);
}
if let Some(hover) = editor.hover() {
@@ -2415,6 +2449,12 @@ fn draw_diff(
panels: &mut WheelPanels,
editor: Option<&Editor>,
) {
if let Some(editor) = editor {
panels.picker_scroll.distance = editor.scroll_distance();
}
if diff.phase == Phase::View {
panels.picker_scroll.revisions = None;
}
let comparison = diff.comparison.as_ref();
let selected = comparison.and_then(|comparison| comparison.files.get(diff.file));
if diff.phase != Phase::View || !diff.selected_file_loaded() {
@@ -2551,7 +2591,26 @@ fn draw_diff(
1
};
let count = usize::from(results.height) / row_height;
let first = diff.selected.saturating_sub(count.saturating_sub(1)).max(1);
let mut key = std::collections::hash_map::DefaultHasher::new();
(
diff.phase as u8,
diff.category,
&diff.query,
diff.comparison_mode as u8,
diff.catalog
.as_ref()
.map(|catalog| (catalog.entries.as_ptr(), catalog.entries.len())),
)
.hash(&mut key);
let first = PickerViewport::follow(
&mut panels.picker_scroll.revisions,
key.finish(),
diff.selected.saturating_sub(1),
usize::from(results.height),
row_height,
panels.picker_scroll.distance,
)
.saturating_add(1);
let mut lines = Vec::with_capacity(count * row_height);
for (index, revision) in entries
.enumerate()
@@ -2744,7 +2803,14 @@ fn draw_diff(
let viewport = panels
.diff_wrap
.get_or_insert_with(DiffWrapViewport::default);
viewport.update(diff, file, [before_area, after_area], caret, toggle_row);
viewport.update(
diff,
file,
[before_area, after_area],
caret,
toggle_row,
editor.map_or(Some(0), Editor::scroll_distance),
);
top = viewport.top.display;
} else {
panels.diff_wrap = None;
@@ -2763,10 +2829,8 @@ fn draw_diff(
if let Some(anchor) = toggle_row {
top = row.saturating_sub(anchor.min(height.saturating_sub(1)));
}
if row < top {
top = row;
} else if height > 0 && row >= top.saturating_add(height) {
top = row + 1 - height;
if toggle_row.is_none() {
top = scroll_offset(top, row, height, editor.scroll_distance());
}
}
let source_len = selected.map_or(0, |file| match focus {
@@ -2779,11 +2843,7 @@ fn draw_diff(
|file| diff_pane_text_width(diff, file, pane, focus),
);
let column = display_column(&editor.lines()[editor.cursor().row], editor.cursor().column);
if column < *left {
*left = column;
} else if width > 0 && column >= left.saturating_add(width) {
*left = column + 1 - width;
}
*left = scroll_offset(*left, column, width, editor.scroll_distance());
}
if let Some(id) = diff.comparison_id
&& let Some(file) = selected.filter(|file| file.loaded)
@@ -3479,6 +3539,7 @@ impl DiffWrapViewport {
areas: [Rect; 2],
caret: Option<(Focus, Cursor)>,
anchor_row: Option<usize>,
distance: Option<usize>,
) {
let Some(id) = diff.comparison_id else { return };
let key = Some((id, diff.file, diff.live_revision));
@@ -3522,23 +3583,29 @@ impl DiffWrapViewport {
if let Some((focus, cursor)) = caret
&& let Some(position) = self.caret_position(diff, file, focus, cursor)
{
if let Some(row) = anchor_row {
self.top = position;
for _ in 0..row.min(height.saturating_sub(1)) {
self.top = self.step(diff, file, self.top, false);
}
self.fill(diff, file, paint_height);
} else if position < self.top {
self.top = position;
self.fill(diff, file, paint_height);
} else if !self
let margin = scroll_margin(height, distance);
let row = self
.rows
.iter()
.take(height)
.any(|row| row.position == position)
{
.position(|row| row.position == position);
let anchor = anchor_row.or_else(|| {
if height == 0 {
None
} else if margin == height / 2
|| position < self.top
|| row.is_some_and(|row| row < margin)
{
Some(margin)
} else if row.is_none_or(|row| row >= height - margin) {
Some(height - margin - 1)
} else {
None
}
});
if let Some(row) = anchor {
self.top = position;
for _ in 1..height {
for _ in 0..row.min(height.saturating_sub(1)) {
self.top = self.step(diff, file, self.top, false);
}
self.fill(diff, file, paint_height);
@@ -3716,6 +3783,7 @@ struct WheelPanels {
hover_syntax: SyntaxCache,
files: Option<(Rect, usize, usize)>,
manual_files: Option<FileWheelOffset>,
picker_scroll: PickerScroll,
diff: Option<DiffGeometry>,
diff_colors: DiffColors,
diff_syntax: Option<DiffSyntaxCache>,
@@ -3723,27 +3791,98 @@ struct WheelPanels {
diff_wrap: Option<DiffWrapViewport>,
}
struct PickerScroll {
distance: Option<usize>,
files: Option<PickerViewport>,
completion: Option<PickerViewport>,
palette: Option<PickerViewport>,
revisions: Option<PickerViewport>,
}
impl Default for PickerScroll {
fn default() -> Self {
Self {
distance: Some(0),
files: None,
completion: None,
palette: None,
revisions: None,
}
}
}
#[derive(Default)]
struct PickerViewport {
key: u64,
top: usize,
}
impl PickerViewport {
fn follow(
viewport: &mut Option<Self>,
key: u64,
selected: usize,
height: usize,
row_height: usize,
distance: Option<usize>,
) -> usize {
let viewport = viewport.get_or_insert_with(Self::default);
if viewport.key != key {
viewport.key = key;
viewport.top = 0;
}
if height < row_height {
return viewport.top;
}
let margin = scroll_margin(height, distance);
let top = scroll_offset(
viewport.top.saturating_mul(row_height),
selected.saturating_mul(row_height),
height,
distance,
);
let first = selected.saturating_sub(margin.div_ceil(row_height));
let last = selected
.saturating_add(1)
.saturating_sub((height - margin) / row_height);
viewport.top = if margin == height / 2 || last > first {
selected.saturating_sub((height / 2) / row_height)
} else {
top.div_ceil(row_height).clamp(last, first)
};
viewport.top
}
}
fn file_picker_key(picker: &FilePicker) -> u64 {
let mut key = std::collections::hash_map::DefaultHasher::new();
(
&picker.root,
&picker.query,
picker.presentation as u8,
picker.view as u8,
picker.viewport_revision(),
)
.hash(&mut key);
key.finish()
}
struct FileWheelOffset {
top: usize,
root: std::path::PathBuf,
query: String,
state: (usize, usize, FilePresentation, FileView, bool, bool),
state: (usize, u64),
}
impl FileWheelOffset {
fn state(picker: &FilePicker) -> (usize, usize, FilePresentation, FileView, bool, bool) {
(
picker.selected,
picker.entries().len(),
picker.presentation,
picker.view,
picker.searching,
picker.focused,
)
fn state(picker: &FilePicker) -> (usize, u64) {
(picker.selected, file_picker_key(picker))
}
fn matches(&self, picker: &FilePicker) -> bool {
self.state == Self::state(picker) && self.root == picker.root && self.query == picker.query
fn matches(&self, picker: &FilePicker, key: u64) -> bool {
self.state == (picker.selected, key)
&& self.root == picker.root
&& self.query == picker.query
}
}
@@ -3751,6 +3890,7 @@ impl WheelPanels {
fn begin_frame(&mut self) {
if self.files.is_none() {
self.manual_files = None;
self.picker_scroll.files = None;
}
self.hover = None;
self.caret = None;
@@ -3761,28 +3901,35 @@ impl WheelPanels {
self.previous_diff = self.diff.take();
}
// Keyboard/paste may change the catalog without changing its length.
fn follow_files(&mut self) {
self.manual_files = None;
}
fn file_top(&mut self, picker: &FilePicker, panel: Rect, height: usize) -> usize {
let key = FileWheelOffset::state(picker).1;
if self
.manual_files
.as_ref()
.is_some_and(|offset| !offset.matches(picker))
.is_some_and(|offset| !offset.matches(picker, key))
{
self.manual_files = None;
}
let count = picker.entries().len();
let following = picker
.selected
.min(count.saturating_sub(1))
.saturating_sub(height.saturating_sub(1));
let following = PickerViewport::follow(
&mut self.picker_scroll.files,
key,
picker.selected.min(count.saturating_sub(1)),
height,
1,
self.picker_scroll.distance,
);
let top = self.manual_files.as_mut().map_or(following, |offset| {
offset.top = offset.top.min(count.saturating_sub(height.max(1)));
offset.top
});
if let Some(viewport) = &mut self.picker_scroll.files {
viewport.top = top;
}
self.files = Some((panel, height, top));
top
}
@@ -3866,10 +4013,11 @@ impl WheelPanels {
&& panel.contains(position)
{
if height > 0 {
let key = file_picker_key(picker);
if self
.manual_files
.as_ref()
.is_some_and(|offset| !offset.matches(picker))
.is_some_and(|offset| !offset.matches(picker, key))
{
self.manual_files = None;
}
@@ -3877,7 +4025,7 @@ impl WheelPanels {
top: drawn_top,
root: picker.root.clone(),
query: picker.query.clone(),
state: FileWheelOffset::state(picker),
state: (picker.selected, key),
});
offset.top = if down {
offset.top.saturating_add(3)
@@ -3964,6 +4112,7 @@ fn draw_completion(
completion: &Completion,
area: Rect,
caret: Position,
panels: &mut WheelPanels,
) -> Option<Rect> {
if area.width == 0 || area.height == 0 || !area.contains(caret) {
return None;
@@ -3975,7 +4124,16 @@ fn draw_completion(
let above = caret.y - area.y;
let downward = below >= desired_height || below >= above;
let height = desired_height.min(if downward { below } else { above }.max(1));
let first = selected.saturating_sub(usize::from(height) - 1);
let mut key = std::collections::hash_map::DefaultHasher::new();
(completion.items.as_ptr(), completion.items.len()).hash(&mut key);
let first = PickerViewport::follow(
&mut panels.picker_scroll.completion,
key.finish(),
selected,
usize::from(height),
1,
panels.picker_scroll.distance,
);
let lines: Vec<Line> = (first..count)
.take(usize::from(height))
.map(|index| {
@@ -4406,6 +4564,7 @@ fn draw_palette(
area: Rect,
mode: Mode,
navigation_hint: &str,
panels: &mut WheelPanels,
) {
if area.width == 0 || area.height == 0 {
return;
@@ -4473,6 +4632,7 @@ fn draw_palette(
);
frame.set_cursor_position((query_area.x + (query_width - offset) as u16, query_area.y));
if palette.entries.is_empty() {
panels.picker_scroll.palette = None;
frame.render_widget(
Paragraph::new(if palette.buffers {
"No matching buffers"
@@ -4488,7 +4648,17 @@ fn draw_palette(
usize::from(results_area.height >= 2) + 1
};
let count = usize::from(results_area.height) / row_height;
let first = palette.selected.saturating_sub(count - 1);
let mut key = std::collections::hash_map::DefaultHasher::new();
(&palette.query, palette.buffers).hash(&mut key);
(palette.entries.as_ptr(), palette.entries.len()).hash(&mut key);
let first = PickerViewport::follow(
&mut panels.picker_scroll.palette,
key.finish(),
palette.selected.min(palette.entries.len() - 1),
usize::from(results_area.height),
row_height,
panels.picker_scroll.distance,
);
let id_width = palette
.entries
.last()
@@ -4689,6 +4859,109 @@ mod tests {
}
}
#[test]
fn scroll_distance_follows_document_and_diff_visual_rows_and_columns() {
let directory = Directory::new();
let text = (0..80)
.map(|row| format!("row-{row:02} {}", "x".repeat(160)))
.collect::<Vec<_>>()
.join("\n");
for diff in [false, true] {
for wrapped in [false, true] {
for distance in [Some(0), Some(2), None, Some(usize::MAX)] {
let mut editor = if diff {
let mut editor = readonly_diff_editor(syntax_diff_file(
"history.txt",
&text,
&text.replace('x', "y"),
));
editor.handle(Input::Char('\t'));
editor
} else {
Editor::open(Some(directory.file("document.txt", &text)), vec![]).unwrap()
};
editor.set_scroll_distance(distance);
if wrapped {
editor.execute("word_wrap.toggle");
}
let mut views = BufferViewports::default();
let mut terminal =
Terminal::new(ratatui::backend::TestBackend::new(80, 16)).unwrap();
let render = |terminal: &mut Terminal<ratatui::backend::TestBackend>,
views: &mut BufferViewports,
editor: &mut Editor| {
if diff {
render_native_diff(terminal, views, editor)
} else {
terminal.draw(|frame| views.draw(frame, editor)).unwrap();
terminal.get_cursor_position().unwrap()
}
};
render(&mut terminal, &mut views, &mut editor);
for _ in 0..30 {
editor.handle(Input::Down);
render(&mut terminal, &mut views, &mut editor);
}
let viewport = if diff {
diff_inner(views.panels.diff.as_ref().unwrap().regions[1].0)
} else {
views.document.geometry.unwrap().1
};
let height = usize::from(viewport.height);
let expected = if distance.is_none_or(|distance| distance >= height / 2) {
height / 2
} else {
height - distance.unwrap() - 1
};
let caret = terminal.get_cursor_position().unwrap();
assert_eq!(
usize::from(caret.y - viewport.y),
expected,
"diff={diff}, wrap={wrapped}, distance={distance:?}"
);
if !wrapped && distance == Some(2) {
editor.handle(Input::Up);
let reversed = render(&mut terminal, &mut views, &mut editor);
assert_eq!(reversed.y + 1, caret.y);
}
if !wrapped {
for _ in 0..120 {
editor.handle(Input::Right);
render(&mut terminal, &mut views, &mut editor);
}
let left = if diff {
editor.diff().unwrap().left_before
} else {
views.offsets[&editor.buffer_id()].1
};
let width = if diff {
usize::from(views.panels.diff.as_ref().unwrap().text_widths[0])
} else {
views.document.text_width
};
let expected = if distance.is_none_or(|distance| distance >= width / 2) {
width / 2
} else {
width - distance.unwrap() - 1
};
assert_eq!(editor.cursor().column - left, expected);
}
if distance == Some(usize::MAX) {
terminal.backend_mut().resize(60, 12);
terminal.resize(Rect::new(0, 0, 60, 12)).unwrap();
let caret = render(&mut terminal, &mut views, &mut editor);
let viewport = if diff {
diff_inner(views.panels.diff.as_ref().unwrap().regions[1].0)
} else {
views.document.geometry.unwrap().1
};
assert_eq!(caret.y - viewport.y, viewport.height / 2);
}
}
}
}
}
fn screen_text(terminal: &Terminal<ratatui::backend::TestBackend>) -> String {
let buffer = terminal.backend().buffer();
let area = buffer.area;
@@ -10216,7 +10489,13 @@ end
.collect();
frame.render_widget(Paragraph::new(rows), frame.area());
frame.set_cursor_position(caret);
panel = draw_completion(frame, &completion, workspace, caret);
panel = draw_completion(
frame,
&completion,
workspace,
caret,
&mut WheelPanels::default(),
);
})
.unwrap();
let mut panel = panel.unwrap();
@@ -10247,7 +10526,14 @@ end
terminal
.draw(|frame| {
frame.set_cursor_position(caret);
panel = draw_completion(frame, &completion, workspace, caret).unwrap();
panel = draw_completion(
frame,
&completion,
workspace,
caret,
&mut WheelPanels::default(),
)
.unwrap();
})
.unwrap();
assert_eq!(
@@ -11634,7 +11920,6 @@ end
panels.manual_files.as_ref().unwrap().top,
session.editor().file_picker().unwrap().entries().len() - height
);
panels.follow_files();
terminal
.draw(|frame| {
draw_file_list(
@@ -11647,7 +11932,8 @@ end
)
})
.unwrap();
assert_eq!(panels.files.unwrap().2, 0);
assert_eq!(panels.files.unwrap().2, entry_count - height);
panels.follow_files();
session.handle(Input::Ctrl('p'));
assert!(session.editor().palette().is_some());
assert!(panels.scroll(&mut session, mouse));
@@ -11711,6 +11997,110 @@ end
assert!(screen_text(&terminal).contains("> file-10"));
}
#[test]
fn picker_viewport_preserves_direction_and_uses_visual_row_margins() {
for row_height in [1, 2] {
let mut viewport = None;
let height = 4 * row_height;
for selected in 0..8 {
PickerViewport::follow(&mut viewport, 1, selected, height, row_height, Some(0));
}
for selected in (4..8).rev() {
assert_eq!(
PickerViewport::follow(&mut viewport, 1, selected, height, row_height, Some(0)),
4,
);
}
assert_eq!(
PickerViewport::follow(&mut viewport, 1, 3, height, row_height, Some(0)),
3,
);
assert_eq!(
PickerViewport::follow(&mut viewport, 2, 3, height, row_height, Some(0)),
0,
);
for distance in [None, Some(usize::MAX)] {
assert_eq!(
PickerViewport::follow(&mut viewport, 2, 7, height, row_height, distance),
5,
);
assert_eq!(
PickerViewport::follow(&mut viewport, 2, 0, height, row_height, distance),
0,
);
}
}
let mut viewport = None;
assert_eq!(
PickerViewport::follow(&mut viewport, 1, 7, 8, 2, Some(1)),
5
);
assert_eq!(
PickerViewport::follow(&mut viewport, 1, 6, 8, 2, Some(1)),
5
);
assert_eq!(
PickerViewport::follow(&mut viewport, 1, 7, 8, 2, Some(3)),
5
);
assert_eq!(PickerViewport::follow(&mut viewport, 1, 7, 7, 2, None), 6);
assert_eq!(PickerViewport::follow(&mut viewport, 1, 7, 3, 1, None), 6);
assert_eq!(PickerViewport::follow(&mut viewport, 1, 7, 0, 1, None), 6);
}
#[test]
fn file_viewport_keeps_manual_wheel_until_selection_or_catalog_changes() {
let directory = Directory::new();
for index in 0..12 {
directory.file(&format!("file-{index:02}"), "");
}
let mut picker = FilePicker::scan(
directory.0.clone(),
FilePresentation::Docked,
FileView::List,
)
.unwrap();
let mut panels = WheelPanels::default();
let area = Rect::new(0, 0, 40, 8);
picker.selected = 7;
assert_eq!(panels.file_top(&picker, area, 5), 3);
picker.selected = 6;
assert_eq!(panels.file_top(&picker, area, 5), 3);
panels.picker_scroll.distance = None;
panels.manual_files = Some(FileWheelOffset {
top: 0,
root: picker.root.clone(),
query: picker.query.clone(),
state: FileWheelOffset::state(&picker),
});
picker.focused = !picker.focused;
assert_eq!(panels.file_top(&picker, area, 3), 0);
assert!(panels.manual_files.is_some());
picker.selected = 7;
assert_eq!(panels.file_top(&picker, area, 5), 5);
assert!(panels.manual_files.is_none());
panels.manual_files = Some(FileWheelOffset {
top: 0,
root: picker.root.clone(),
query: picker.query.clone(),
state: FileWheelOffset::state(&picker),
});
std::fs::rename(directory.0.join("file-00"), directory.0.join("new-file")).unwrap();
picker = FilePicker::scan(
directory.0.clone(),
FilePresentation::Docked,
FileView::List,
)
.unwrap();
picker.selected = 7;
assert_eq!(picker.entries().len(), 12);
assert_eq!(panels.file_top(&picker, area, 5), 5);
assert!(panels.manual_files.is_none());
panels.begin_frame();
panels.begin_frame();
assert!(panels.picker_scroll.files.is_none());
}
#[test]
fn file_list_window_follows_control_j_k_selection() {
let mut editor = Editor::open(None, vec![]).unwrap();
@@ -14970,6 +15360,7 @@ end
area,
editor.mode(),
editor.selection_navigation_hint(),
&mut WheelPanels::default(),
)
})
.unwrap();
@@ -15030,6 +15421,7 @@ end
area,
Mode::Visual,
editor.selection_navigation_hint(),
&mut WheelPanels::default(),
)
})
.unwrap();
@@ -15073,6 +15465,7 @@ end
area,
Mode::Normal,
editor.selection_navigation_hint(),
&mut WheelPanels::default(),
)
})
.unwrap();