feat: scroll viewport independently of the cursor
This commit is contained in:
@@ -633,6 +633,26 @@ core_actions![
|
||||
Action::Up,
|
||||
&[Mode::Normal, Mode::Insert, Mode::Visual],
|
||||
),
|
||||
(
|
||||
"core.scroll_up",
|
||||
Action::ScrollUp,
|
||||
&[Mode::Normal, Mode::Visual]
|
||||
),
|
||||
(
|
||||
"core.scroll_down",
|
||||
Action::ScrollDown,
|
||||
&[Mode::Normal, Mode::Visual]
|
||||
),
|
||||
(
|
||||
"core.scroll_half_up",
|
||||
Action::ScrollHalfUp,
|
||||
&[Mode::Normal, Mode::Visual]
|
||||
),
|
||||
(
|
||||
"core.scroll_half_down",
|
||||
Action::ScrollHalfDown,
|
||||
&[Mode::Normal, Mode::Visual]
|
||||
),
|
||||
(
|
||||
"core.line_start",
|
||||
Action::LineStart,
|
||||
@@ -1322,6 +1342,8 @@ fn default_bindings() -> HashMap<Mode, HashMap<Input, Binding>> {
|
||||
for (key, action) in [
|
||||
(Input::Ctrl('k'), CloneAbove),
|
||||
(Input::Ctrl('e'), CloneBelow),
|
||||
(Input::Ctrl('u'), ScrollHalfUp),
|
||||
(Input::Ctrl('d'), ScrollHalfDown),
|
||||
(Input::Escape, Normal),
|
||||
] {
|
||||
bindings
|
||||
@@ -1580,6 +1602,7 @@ struct ViewState {
|
||||
command_line: String,
|
||||
caret_reveal_revision: u64,
|
||||
lsp_center: Cell<Option<(u64, u64, Cursor)>>,
|
||||
viewport_scroll: Cell<(isize, isize)>,
|
||||
insert_start: Option<Snapshot>,
|
||||
pending_edit: Option<EditRecipe>,
|
||||
visual_motions: Option<Vec<Motion>>,
|
||||
@@ -1600,6 +1623,7 @@ impl ViewState {
|
||||
command_line: String::new(),
|
||||
caret_reveal_revision: 0,
|
||||
lsp_center: Cell::new(None),
|
||||
viewport_scroll: Cell::new((0, 0)),
|
||||
insert_start: None,
|
||||
pending_edit: None,
|
||||
visual_motions: None,
|
||||
@@ -5787,8 +5811,14 @@ impl Editor {
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn take_viewport_scroll(&self, height: usize) -> isize {
|
||||
let (rows, halves) = self.active.view.viewport_scroll.take();
|
||||
rows.saturating_add(halves.saturating_mul((height / 2).max(1) as isize))
|
||||
}
|
||||
|
||||
fn reveal_caret(&mut self) {
|
||||
self.active.view.lsp_center.set(None);
|
||||
self.active.view.viewport_scroll.set((0, 0));
|
||||
self.active.view.caret_reveal_revision =
|
||||
self.active.view.caret_reveal_revision.wrapping_add(1);
|
||||
}
|
||||
@@ -7678,6 +7708,7 @@ impl Editor {
|
||||
self.refresh_resolver_status();
|
||||
self.refresh_resolver_comparison();
|
||||
if self.diff_interacting()
|
||||
&& self.active.view.viewport_scroll.get() == (0, 0)
|
||||
&& let Some(diff) = self.interaction_diff_mut()
|
||||
{
|
||||
diff.follow_caret = true;
|
||||
@@ -8246,6 +8277,29 @@ impl Editor {
|
||||
| Action::LspDiagnostics => unreachable!("catalog Ex actions dispatch above"),
|
||||
Action::JumpBack => self.traverse_jumps(false, count),
|
||||
Action::JumpForward => self.traverse_jumps(true, count),
|
||||
Action::ScrollUp
|
||||
| Action::ScrollDown
|
||||
| Action::ScrollHalfUp
|
||||
| Action::ScrollHalfDown => {
|
||||
if self.document_actions_available() {
|
||||
let amount = isize::try_from(count).unwrap_or(isize::MAX);
|
||||
let amount = if matches!(action, Action::ScrollUp | Action::ScrollHalfUp) {
|
||||
-amount
|
||||
} else {
|
||||
amount
|
||||
};
|
||||
let (mut rows, mut halves) = self.active.view.viewport_scroll.get();
|
||||
if matches!(action, Action::ScrollHalfUp | Action::ScrollHalfDown) {
|
||||
halves = halves.saturating_add(amount);
|
||||
} else {
|
||||
rows = rows.saturating_add(amount);
|
||||
}
|
||||
self.active.view.viewport_scroll.set((rows, halves));
|
||||
if let Some(diff) = self.interaction_diff_mut() {
|
||||
diff.follow_caret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::SeekForward | Action::SeekBackward => {
|
||||
self.seek_pending = Some((action == Action::SeekForward, count));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user