diff --git a/app/src/executor.rs b/app/src/executor.rs index 3203a0da3..51e928dbe 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -61,7 +61,7 @@ impl Executor { // Navigation "arrow" => { - let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); + let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or_default(); cx.manager.active_mut().arrow(step) } "peek" => { diff --git a/config/preset/keymap.toml b/config/preset/keymap.toml index 461e8099f..d21b91ccb 100644 --- a/config/preset/keymap.toml +++ b/config/preset/keymap.toml @@ -13,6 +13,11 @@ keymap = [ { on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, { on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "" ], exec = "arrow -50%", desc = "Move cursor up half page" }, + { on = [ "" ], exec = "arrow 50%", desc = "Move cursor down half page" }, + { on = [ "" ], exec = "arrow -100%", desc = "Move cursor up one page" }, + { on = [ "" ], exec = "arrow 100%", desc = "Move cursor down one page" }, + { on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" }, { on = [ "l" ], exec = "enter", desc = "Enter the child directory" }, diff --git a/core/src/lib.rs b/core/src/lib.rs index 3696401fd..e637118f7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,8 +14,9 @@ pub mod help; mod highlighter; pub mod input; pub mod manager; -pub mod position; +mod position; pub mod select; +mod step; pub mod tasks; pub mod which; @@ -23,5 +24,6 @@ pub use blocker::*; pub use event::*; pub use highlighter::*; pub use position::*; +pub use step::*; pub fn init() { init_blocker(); } diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 9d1a832ef..6899d1f7e 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -2,7 +2,7 @@ use config::MANAGER; use ratatui::layout::Rect; use shared::Url; -use crate::{emit, files::{File, Files, FilesOp}}; +use crate::{emit, files::{File, Files, FilesOp}, Step}; #[derive(Default)] pub struct Folder { @@ -58,18 +58,18 @@ impl Folder { true } - pub fn next(&mut self, step: usize) -> bool { + pub fn next(&mut self, step: Step) -> bool { let len = self.files.len(); if len == 0 { return false; } let old = self.cursor; - self.cursor = (self.cursor + step).min(len - 1); + let limit = MANAGER.layout.folder_height(); + self.cursor = step.add(self.cursor, || limit).min(len - 1); self.hovered = self.files.duplicate(self.cursor); self.set_page(false); - let limit = MANAGER.layout.folder_height(); if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) { self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old); } @@ -77,9 +77,9 @@ impl Folder { old != self.cursor } - pub fn prev(&mut self, step: usize) -> bool { + pub fn prev(&mut self, step: Step) -> bool { let old = self.cursor; - self.cursor = self.cursor.saturating_sub(step); + self.cursor = step.add(self.cursor, || MANAGER.layout.folder_height()); self.hovered = self.files.duplicate(self.cursor); self.set_page(false); @@ -105,7 +105,11 @@ impl Folder { pub fn hover(&mut self, url: &Url) -> bool { let new = self.files.position(url).unwrap_or(self.cursor); - if new > self.cursor { self.next(new - self.cursor) } else { self.prev(self.cursor - new) } + if new > self.cursor { + self.next(Step::from(new - self.cursor)) + } else { + self.prev(Step::from(self.cursor - new)) + } } #[inline] diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index 4e5971c15..b9f14c373 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -7,7 +7,7 @@ use tokio::{pin, task::JoinHandle}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use super::{Finder, Folder, Mode, Preview, PreviewLock}; -use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, BLOCKER}; +use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER}; pub struct Tab { pub(super) mode: Mode, @@ -67,12 +67,8 @@ impl Tab { self.search_stop() } - pub fn arrow(&mut self, step: isize) -> bool { - let ok = if step > 0 { - self.current.next(step as usize) - } else { - self.current.prev(step.unsigned_abs()) - }; + pub fn arrow(&mut self, step: Step) -> bool { + let ok = if step.is_positive() { self.current.next(step) } else { self.current.prev(step) }; if !ok { return false; } @@ -248,7 +244,7 @@ impl Tab { }; if let Some(step) = finder.ring(&self.current.files, self.current.cursor(), prev) { - self.arrow(step); + self.arrow(step.into()); } self.finder = Some(finder); @@ -280,7 +276,7 @@ impl Tab { let mut b = finder.catchup(&self.current.files); if let Some(step) = finder.arrow(&self.current.files, self.current.cursor(), prev) { - b |= self.arrow(step); + b |= self.arrow(step.into()); } b diff --git a/core/src/step.rs b/core/src/step.rs new file mode 100644 index 000000000..5faa2459c --- /dev/null +++ b/core/src/step.rs @@ -0,0 +1,55 @@ +use std::{num::ParseIntError, str::FromStr}; + +pub enum Step { + Fixed(isize), + Percent(i8), +} + +impl Default for Step { + fn default() -> Self { Self::Fixed(0) } +} + +impl FromStr for Step { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(if let Some(s) = s.strip_suffix('%') { + Self::Percent(s.parse()?) + } else { + Self::Fixed(s.parse()?) + }) + } +} + +impl From for Step { + fn from(n: isize) -> Self { Self::Fixed(n) } +} + +impl From for Step { + fn from(n: usize) -> Self { Self::Fixed(n as isize) } +} + +impl Step { + #[inline] + fn fixed usize>(self, f: F) -> isize { + match self { + Self::Fixed(n) => n, + Self::Percent(0) => 0, + Self::Percent(n) => n as isize * f() as isize / 100, + } + } + + #[inline] + pub fn add usize>(self, pos: usize, f: F) -> usize { + let fixed = self.fixed(f); + if fixed > 0 { pos + fixed as usize } else { pos.saturating_sub(fixed.unsigned_abs()) } + } + + #[inline] + pub fn is_positive(&self) -> bool { + match *self { + Self::Fixed(n) => n > 0, + Self::Percent(n) => n > 0, + } + } +}