diff options
author | rabite <rabite@posteo.de> | 2019-07-25 21:35:33 +0200 |
---|---|---|
committer | rabite <rabite@posteo.de> | 2019-07-25 21:56:30 +0200 |
commit | 889abf77ada1058c76c58f4b9a9500d7b49c58d6 (patch) | |
tree | 6fe186488d6b6bd61c8817260d591fd587fcd979 | |
parent | c106fded2e72434d9a98b8c227d6981e5caee5fe (diff) |
add custom keybindings
-rw-r--r-- | Cargo.lock | 42 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | README.md | 206 | ||||
-rw-r--r-- | config.tar.gz | bin | 2494 -> 3337 bytes | |||
-rw-r--r-- | src/config.rs | 10 | ||||
-rw-r--r-- | src/fail.rs | 105 | ||||
-rw-r--r-- | src/file_browser.rs | 105 | ||||
-rw-r--r-- | src/foldview.rs | 127 | ||||
-rw-r--r-- | src/keybind.rs | 1094 | ||||
-rw-r--r-- | src/listview.rs | 86 | ||||
-rw-r--r-- | src/main.rs | 5 | ||||
-rw-r--r-- | src/mediaview.rs | 32 | ||||
-rw-r--r-- | src/minibuffer.rs | 119 | ||||
-rw-r--r-- | src/paths.rs | 6 | ||||
-rw-r--r-- | src/proclist.rs | 88 | ||||
-rw-r--r-- | src/quick_actions.rs | 91 | ||||
-rw-r--r-- | src/tabview.rs | 50 |
17 files changed, 1832 insertions, 341 deletions
@@ -516,6 +516,14 @@ dependencies = [ ] [[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "hunter" version = "1.3.4" dependencies = [ @@ -543,10 +551,13 @@ dependencies = [ "parse-ansi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "pathbuftools 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "signal-notify 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "sixel 0.3.1 (git+https://github.com/rabite0/sixel-rs?tag=v0.3.1)", "sixel-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "systemstat 0.1.4 (git+https://github.com/myfreeweb/systemstat?tag=v0.1.4)", "termion 1.5.3 (git+https://github.com/redox-os/termion)", "tree_magic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1207,6 +1218,11 @@ dependencies = [ ] [[package]] +name = "rust-ini" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "rustc-demangle" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1312,6 +1328,22 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "strum" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strum_macros" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "syn" version = "0.15.39" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1456,6 +1488,11 @@ dependencies = [ ] [[package]] +name = "unicode-segmentation" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1606,6 +1643,7 @@ dependencies = [ "checksum gstreamer-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfc2f6cc9b6a1f5159bfd500310fe431cfb0b74b3af17ce3fdf8353cf586975" "checksum gstreamer-video 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44868f3390bec7cad142f5fc3e39bb1782d1453d07889efd2ac69a50b0656d1f" "checksum gstreamer-video-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8fcb1e577de93d1ad1e5117234ce64d40f215143d752140719923651608983" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "99198e595d012efccf12abf4abc08da2d97be0b0355a2b08d101347527476ba4" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" @@ -1681,6 +1719,7 @@ dependencies = [ "checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48" +"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" @@ -1697,6 +1736,8 @@ dependencies = [ "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" +"checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" "checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum systemstat 0.1.4 (git+https://github.com/myfreeweb/systemstat?tag=v0.1.4)" = "<none>" @@ -1712,6 +1753,7 @@ dependencies = [ "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" +"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7" @@ -1,5 +1,3 @@ -cargo-features = ["default-run"] - [package] name = "hunter" version = "1.3.4" @@ -41,7 +39,10 @@ pathbuftools = "0.1" clap = "2.33" mime = "0.3.13" base64 = "0.10.1" -#glib = "*" +strum = "0.15" +strum_macros = "0.15" +rust-ini = "0.13" + image = { version = "0.21.1", optional = true } gstreamer = { version = "0.14", optional = true } @@ -5,11 +5,11 @@ hunter NEW +- [Custom Keybindings] Customize keys to your liking - [Graphics] High quality support for graphics using SIXEL/kitty protocols - [QuickActions] Added quick action creator/customizer - [Previews] New and improved preview customization - [**[IRC channel](https://webchat.freenode.net/?channels=hunter)**] Problems? Bugs? Praise? Chat with us: [#hunter @ Freenode](https://webchat.freenode.net/?channels=hunter)! -- [Quick Actions] Run specific actions based on file type @@ -130,6 +130,10 @@ media_previewer=hunter-media graphics_mode=auto ``` +## Keys + +Keys can be configured in ```~/.config/hunter/keys```. Some actions can be further customized with arguments. For example, you can specify ```GotoTab(n)```, where n is a positive number to move up n times. Some keys like F1-F12 are represented as an enum like this: ```F(u8)```. You can take that u8 and stick it into the ```GotoTab``` action by using a placeholder binding like this: ```GotoTab(_)=F_```. This also works for key combinations, so you can specify ```C-_``` to bind all Ctrl-<key> combinations to some action like Delete(_) on bookmarks. To bind ```_``` itself escape like this: ```\_```. See the default configuration for more examples. + ## Previews Defining previews is easy. You just need a shell script that takes a path as first parameter and prints out what you want to see in the preview column. Put that shell script in @@ -200,96 +204,110 @@ To change the directory of your shell when quitting hunter with Q you need to so Keybindings: ============ -## holy mode -By default hunter uses vi-style keybindings. If you use a QWERTY-like keyboard layout this is probably what you want. Most people will want this, so I made it the default. If you have a different keyboard layout this might not be the best choice. The holy-branch changes the movement keys to the emacs keybindings, which is more ergonomic on e.g. Colemak. - -## Main view: - -| Key | Action | -| ------------------- | :--------------------------------- | -| j/k (holy: n/p) | move down/up | -| J/K (holy: N/P) | 5x move down/5x move up | -| ]/[ | move down/up on left column | -| < | move to top | -| > | move to bottom | -| l/h (holy: f/b) | open/go back | -| S | search file | -| Alt(s) | search next | -| Alt(S) | search prev | -| Ctrl(f) | filter | -| space | multi select file | -| Alt(space) | select with external program | -| v | invert selections | -| V | clear all selections | -| Alt(v) | only show selected files | -| t | toggle tag | -| h | toggle show hidden | -| r | reverse sort | -| s | cycle sort (name/size/mtime) | -| K | select next by mtime | -| k | select prev by mtime | -| d | toggle dirs first | -| ~ | go to $HOME | -| / | turbo cd | -| Alt(/) | enter dir with external program | -| Q | quit with dir/selections | -| L | run in background | -| ~ | goto prev cwd | -| ` | goto bookmark | -| m | add bookmark | -| w | show processes | -| g holy(l) | show log | -| a | show quick actions | -| z | open subshell in cwd | -| c | toggle columns | -| F(n) | switch to tab | -| Alt(m) | toggle media pause and autoplay | -| Alt(M) | toggle media mute | -| Alt(>) | seek media +5s | -| Alt(<) | seek media -5s | - - - -## Keybindings in bookmark popup: - -| Key | Action | -| ------------------- |:---------------------------------| -|(key) |open bookmark | -|` |goto last cwd | -|Ctrl(c) |cancel | -|Alt(key) |delete bookmark | - -## Keybindings in process viewer: - -| Key | Action | -| ------------------- |:---------------------------------| -|w |close process viewer | -|d |remove process | -|k |kill process | -|k holy(p) |move up | -|j holy(n) |move down | -|f |toggle follow output | -|Ctrl(j) holy(Ctrl(n) |scroll output down | -|Ctrl(k) holy(Ctrl(p) |scroll output up | -|Ctrl(v) |page down | -|Alt(v) |page up | -|< |scroll to bottom | -|> |scroll to top | - - -## Keybindings in minibuffer: - -| Key | Action | -| ------------------- |:---------------------------------| -|Esc/Ctrl(c) |cancel input | -|Tab |complete | -|F(n) |insert tab substitution | -|Ctrl(d) |delete char | -|Ctrl(b) |move cursor left | -|Ctrl(f) |move cursor right | -|Ctrl(p)/Alt(p) |history up | -|Ctrl(n)/Alt(n) |history down | -|Ctrl(u) |clear line | -|Ctrl(h) |delete word | -|Ctrl(a) |move cursor to beginning | -|Ctrl(e) |move cursor to end | +Note: ```_``` means any key. + +## Movement: +Up(1)=k, Up +Down(1)=j, Down +Left=b, Left +Right=f, Right +Top=<, Home +Bottom=>, End +Up(10)=K +Down(10)=J +PageUp=C-v, PageUp +PageDown=M-v, PageDown + +## File Browser (global effects): +Quit=q +QuitWithDir=Q +LeftColumnDown=] +LeftColumnUp=[ +GotoHome=~ +TurboCd=/ +SelectExternal=M-Space +EnterDirExternal=M-/ +RunInBackground=F +GotoPrevCwd=- +ShowBookmarks=` +AddBookmark=b +ShowProcesses=w +ShowLog=g +ShowQuickActions=a +RunSubshell=z +ToggleColumns=c +ExecCmd=! + +## File List (affects current directory): +Search=S +SearchNext=M-s +SearchPrev=M-S +Filter=C-f +Select=Space +InvertSelection=v +ClearSelection=V +FilterSelection=M-V +ToggleTag=t +ToggleHidden=h +ReverseSort=r +CycleSort=s +ToNextMtime=K +ToPrevMtime=k +ToggleDirsFirst=d + +## Tabs +NewTab=C-t +CloseTab=C-w +NextTab=Tab +PrevTab=BackTab +GotoTab(_)=F_ + +## Media +TogglePause=M-m +ToggleMute=M-M +SeekForward=M-> +SeekBackward=M-< + +## Bookmarks +GotoLastCwd=` +Goto(_)=_ +Delete(_)=M-_ + +## Processes +Close=w, Esc +Remove=d +Kill=k +FollowOutput=f +ScrollOutputUp=C-p +ScrollOutputDown=C-n +ScrollOutputPageUp=C-V +ScrollOutputPageDown=C-v +ScrollOutputTop=C-< +ScrollOutputBottom=> + +## MiniBuffer +InsertChar(_)=_ +InsertTab(_)=F_ +Cancel=C-c, Esc +Finish=Enter +Complete=Tab +DeleteChar=C-d, Delete +BackwardDeleteChar=Backspace +CursorLeft=C-b, Left +CursorRight=C-f, Right +HistoryUp=C-p, M-p, Up +HistoryDown=C-n, M-n, Down +ClearLine=C-u +DeleteWord=C-h +CursorToStart=C-a, Home +CursorToEnd=C-e, End + +## Folds +ToggleFold=t,Tab + +## Log +Close=g,Esc + +## QuickActions +Close=a, Esc, C-a +SelectOrRun(_)=_ diff --git a/config.tar.gz b/config.tar.gz Binary files differindex 91fdb29..8f571dc 100644 --- a/config.tar.gz +++ b/config.tar.gz diff --git a/src/config.rs b/src/config.rs index 864248d..9a81cba 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,6 +6,7 @@ use std::sync::RwLock; use crate::paths; use crate::fail::{HError, HResult, ErrorLog}; +use crate::keybind::KeyBinds; #[derive(Clone)] @@ -92,6 +93,7 @@ pub struct Config { pub media_previewer: String, pub ratios: Vec::<usize>, pub graphics: String, + pub keybinds: KeyBinds, } @@ -115,6 +117,7 @@ impl Config { media_previewer: "hunter-media".to_string(), ratios: vec![20,30,49], graphics: detect_g_mode(), + keybinds: KeyBinds::default(), } } @@ -182,7 +185,12 @@ impl Config { config }); - let config = infuse_argv_config(config); + let mut config = infuse_argv_config(config); + + //use std::iter::Extend; + KeyBinds::load() + .map(|kb| config.keybinds = kb) + .log(); Ok(config) } diff --git a/src/fail.rs b/src/fail.rs index 1e6baf0..918f7df 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -1,7 +1,7 @@ use failure; use failure::Fail; //use failure::Backtrace; -use async_value::AError; +//use async_value::AError; use termion::event::Key; @@ -102,10 +102,16 @@ pub enum HError { UTF8ParseError(std::str::Utf8Error), #[fail(display = "Failed to parse integer!")] ParseIntError(std::num::ParseIntError), + #[fail(display = "Failed to parse char!")] + ParseCharError(std::char::ParseCharError), #[fail(display = "{}", _0)] Media(MediaError), #[fail(display = "{}", _0)] Mime(MimeError), + #[fail(display = "{}", _0)] + KeyBind(KeyBindError), + #[fail(display = "FileBrowser needs to know about all tab's files to run exec!")] + FileBrowserNeedTabFiles } impl HError { @@ -203,33 +209,64 @@ pub trait ErrorLog where Self: Sized { fn log_and(self) -> Self; } -impl<T> ErrorLog for HResult<T> { +// impl<T> ErrorLog for HResult<T> { +// fn log(self) { +// if let Err(err) = self { +// put_log(&err).ok(); +// } +// } + +// fn log_and(self) -> Self { +// if let Err(err) = &self { +// put_log(err).ok(); +// } +// self +// } +// } + + +// impl<T> ErrorLog for Result<T, AError> { +// fn log(self) { +// if let Err(err) = self { +// put_log(&err.into()).ok(); +// } +// } + +// fn log_and(self) -> Self { +// if let Err(err) = &self { +// put_log(&err.clone().into()).ok(); +// } +// self +// } +// } + +impl<T, E> ErrorLog for Result<T, E> +where E: Into<HError> + Clone { fn log(self) { if let Err(err) = self { + let err: HError = err.into(); put_log(&err).ok(); } } - fn log_and(self) -> Self { - if let Err(err) = &self { - put_log(err).ok(); + if let Err(ref err) = self { + let err: HError = err.clone().into(); + put_log(&err).ok(); } self } } - -impl<T> ErrorLog for Result<T, AError> { +impl<E> ErrorLog for E +where E: Into<HError> + Clone { fn log(self) { - if let Err(err) = self { - put_log(&err.into()).ok(); - } - } + let err: HError = self.into(); + put_log(&err).ok(); + } fn log_and(self) -> Self { - if let Err(err) = &self { - put_log(&err.clone().into()).ok(); - } + let err: HError = self.clone().into(); + put_log(&err).ok(); self } } @@ -334,6 +371,13 @@ impl From<std::num::ParseIntError> for HError { } } +impl From<std::char::ParseCharError> for HError { + fn from(error: std::char::ParseCharError) -> Self { + let err = HError::ParseCharError(error); + err + } +} + // MIME Errors @@ -352,3 +396,36 @@ impl From<MimeError> for HError { HError::Mime(e) } } + + +impl From<KeyBindError> for HError { + fn from(e: KeyBindError) -> Self { + HError::KeyBind(e) + } +} + + +#[derive(Fail, Debug, Clone)] +pub enum KeyBindError { + #[fail(display = "Movement has not been defined for this widget")] + MovementUndefined, + #[fail(display = "Keybind defined with wrong key: {} -> {}", _0, _1)] + WrongKey(String, String), + #[fail(display = "Defined keybind for non-existing action: {}", _0)] + WrongAction(String), + #[fail(display = "Failed to parse keybind: {}", _0)] + ParseKeyError(String), + #[fail(display = "Trouble with ini file! Error: {}", _0)] + IniError(Arc<ini::ini::Error>), + #[fail(display = "Couldn't parse as either char or u8: {}", _0)] + CharOrNumParseError(String), + #[fail(display = "Wanted {}, but got {}!", _0, _1)] + CharOrNumWrongType(String, String) + +} + +impl From<ini::ini::Error> for KeyBindError { + fn from(err: ini::ini::Error) -> Self { + KeyBindError::IniError(Arc::new(err)) + } +} diff --git a/src/file_browser.rs b/src/file_browser.rs index b77109b..73e8e1f 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -1,4 +1,4 @@ -use termion::event::{Event, Key}; +use termion::event::Key; use pathbuftools::PathBufTools; use osstrtools::OsStrTools; use async_value::Stale; @@ -126,6 +126,11 @@ impl Tabbable for TabView<FileBrowser> { Ok(()) } + fn prev_tab(&mut self) -> HResult<()> { + self.prev_tab_(); + Ok(()) + } + fn goto_tab(&mut self, index: usize) -> HResult<()> { self.goto_tab_(index) } @@ -152,10 +157,11 @@ impl Tabbable for TabView<FileBrowser> { } fn on_key_sub(&mut self, key: Key) -> HResult<()> { - match key { - Key::Char('!') => { + match self.active_tab_mut().on_key(key) { + // returned by specific tab when called with ExecCmd action + Err(HError::FileBrowserNeedTabFiles) => { let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone()) - .collect::<Vec<_>>(); + .collect::<Vec<_>>(); let selected_files = self .widgets .iter() @@ -165,7 +171,7 @@ impl Tabbable for TabView<FileBrowser> { self.widgets[self.active].exec_cmd(tab_dirs, selected_files) } - _ => { self.active_tab_mut().on_key(key) } + result @ _ => result } } @@ -246,6 +252,7 @@ impl Tabbable for TabView<FileBrowser> { + impl FileBrowser { pub fn new(core: &WidgetCore, cache: Option<FsCache>) -> HResult<FileBrowser> { let fs_cache = cache.unwrap_or_else(|| FsCache::new(core.get_sender())); @@ -1077,7 +1084,6 @@ impl FileBrowser { fn exec_cmd(&mut self, tab_dirs: Vec<File>, tab_files: Vec<Vec<File>>) -> HResult<()> { - let cwd = self.cwd()?.clone(); let selected_file = self.selected_file().ok(); let selected_files = self.selected_files().ok(); @@ -1295,44 +1301,69 @@ impl Widget for FileBrowser { } fn on_key(&mut self, key: Key) -> HResult<()> { - match key { - Key::Char('a') => self.quick_action()?, - Key::Char(']') => self.move_down_left_widget()?, - Key::Char('[') => self.move_up_left_widget()?, - Key::Alt(' ') => self.external_select()?, - Key::Alt('/') => self.external_cd()?, - Key::Char('/') => { self.turbo_cd()?; }, - Key::Char('~') => { self.go_home()?; }, - Key::Char('q') => HError::quit()?, - Key::Char('Q') => { self.quit_with_dir()?; }, - Key::Right | Key::Char('l') => { self.enter_dir()?; }, - Key::Char('L') => { self.open_bg()?; }, - Key::Left | Key::Char('h') => { self.go_back()?; }, - Key::Char('-') => { self.goto_prev_cwd()?; }, - Key::Char('`') => { self.goto_bookmark()?; }, - Key::Char('m') => { self.add_bookmark()?; }, - Key::Char('w') => { self.show_procview()?; }, - Key::Char('g') => self.show_log()?, - Key::Char('z') => self.run_subshell()?, - Key::Char('c') => self.toggle_colums(), - _ => { - let main_widget_result = self.main_widget_mut()?.on_key(key); - if let Err(HError::WidgetUndefinedKeyError{..}) = main_widget_result { - match self.preview_widget_mut()?.on_key(key) { - Ok(()) => {} - Err(HError::WidgetUndefinedKeyError{key}) => { - self.bad(Event::Key(key))?; - } - err @ Err(_) => { err?; } + match self.do_key(key) { + Err(HError::WidgetUndefinedKeyError{..}) => { + match self.main_widget_mut()?.on_key(key) { + Err(HError::WidgetUndefinedKeyError{..}) => { + self.preview_widget_mut()?.on_key(key)? } + e @ _ => e? } - }, - } + } + e @ _ => e? + }; + if !self.columns.zoom_active { self.update_preview().log(); } Ok(()) } } +use crate::keybind::{Acting, Bindings, FileBrowserAction, Movement}; + +impl Acting for FileBrowser { + type Action=FileBrowserAction; + + fn search_in(&self) -> Bindings<Self::Action> { + self.core.config().keybinds.filebrowser + } + + fn movement(&mut self, movement: &Movement) -> HResult<()> { + use Movement::*; + + match movement { + Left => self.go_back(), + Right => self.enter_dir(), + _ => self.main_widget_mut()?.movement(movement) + } + } + + fn do_action(&mut self, action: &Self::Action) -> HResult<()> { + use FileBrowserAction::*; + match action { + Quit => HError::quit()?, + QuitWithDir => self.quit_with_dir()?, + LeftColumnDown => self.move_down_left_widget()?, + LeftColumnUp => self.move_up_left_widget()?, + GotoHome => self.go_home()?, + TurboCd => self.turbo_cd()?, + SelectExternal => self.external_select()?, + EnterDirExternal => self.external_cd()?, + RunInBackground => self.open_bg()?, + GotoPrevCwd => self.goto_prev_cwd()?, + ShowBookmarks => self.goto_bookmark()?, + AddBookmark => self.add_bookmark()?, + ShowProcesses => self.show_procview()?, + ShowLog => self.show_log()?, + ShowQuickActions => self.quick_action()?, + RunSubshell => self.run_subshell()?, + ToggleColumns => self.toggle_colums(), + // Tab implementation needs to call exec_cmd because ALL files are needed + ExecCmd => Err(HError::FileBrowserNeedTabFiles)? + } + Ok(()) + } +} + impl PartialEq for FileBrowser { fn eq(&self, other: &FileBrowser) -> bool { if self.columns == other.columns && self.cwd == other.cwd { diff --git a/src/foldview.rs b/src/foldview.rs index 1f285bd..5310b85 100644 --- a/src/foldview.rs +++ b/src/foldview.rs @@ -5,8 +5,9 @@ use chrono::{DateTime, Local}; use crate::term; use crate::widget::Widget; use crate::listview::{ListView, Listable}; -use crate::fail::{HResult, HError}; +use crate::fail::{HResult, HError, KeyBindError}; use crate::dirty::Dirtyable; +use crate::keybind::{Acting, AnyKey, Bindings, BindingSection, Movement, FoldAction, LogAction}; pub type LogView = ListView<Vec<LogEntry>>; @@ -81,9 +82,73 @@ impl From<&HError> for LogEntry { } } +pub trait ActingExt +where + Self::Action: BindingSection + std::fmt::Debug, + Bindings<Self::Action>: Default, + Self: Widget +{ + type Action; + + fn search_in(&self) -> Bindings<Self::Action>; + fn movement(&mut self, _movement: &Movement) -> HResult<()> { + Err(KeyBindError::MovementUndefined)? + } + fn do_key_ext(&mut self, key: Key) -> HResult<()> { + let gkey = AnyKey::from(key); + + // Moving takes priority + if let Some(movement) = self.get_core()? + .config() + .keybinds + .movement + .get(gkey) { + match self.movement(movement) { + Ok(()) => return Ok(()), + Err(HError::KeyBind(KeyBindError::MovementUndefined)) => {} + Err(e) => Err(e)? + } + } + + self.search_in(); + + let bindings = self.search_in(); + + if let Some(action) = bindings.get(key) { + return self.do_action(action) + } else if let Some(any_key) = gkey.any() { + if let Some(action) = bindings.get(any_key) { + let action = action.insert_key_param(key); + return self.do_action(&action); + } + } + + HError::undefined_key(key) + } + fn do_action(&mut self, _action: &Self::Action) -> HResult<()> { + Err(KeyBindError::MovementUndefined)? + } +} +impl ActingExt for ListView<Vec<LogEntry>> { + type Action = LogAction; + + fn search_in(&self) -> Bindings<Self::Action> { + self.core.config().keybinds.log + } + + fn do_action(&mut self, action: &Self::Action) -> HResult<()> { + match action { + LogAction::Close => self.popup_finnished() + } + } +} -pub trait FoldableWidgetExt { +pub trait FoldableWidgetExt +where + Self: ActingExt, + Bindings<<Self as ActingExt>::Action>: Default +{ fn on_refresh(&mut self) -> HResult<()> { Ok(()) } fn render_header(&self) -> HResult<String> { Ok("".to_string()) } fn render_footer(&self) -> HResult<String> { Ok("".to_string()) } @@ -204,7 +269,8 @@ pub trait Foldable { impl<F: Foldable> ListView<Vec<F>> where - ListView<Vec<F>>: FoldableWidgetExt { + ListView<Vec<F>>: FoldableWidgetExt, + Bindings<<ListView<Vec<F>> as ActingExt>::Action>: Default { pub fn toggle_fold(&mut self) -> HResult<()> { let fold = self.current_fold()?; @@ -257,7 +323,9 @@ where impl<F: Foldable> Listable for ListView<Vec<F>> where - ListView<Vec<F>>: FoldableWidgetExt { + ListView<Vec<F>>: FoldableWidgetExt, + Bindings<<ListView<Vec<F>> as ActingExt>::Action>: Default +{ fn len(&self) -> usize { self.content.iter().map(|f| f.lines()).sum() @@ -294,21 +362,42 @@ where } fn on_key(&mut self, key: Key) -> HResult<()> { - // this on_key() could have been implmented by some type - let result = FoldableWidgetExt::on_key(self, key); - if let Err(HError::WidgetUndefinedKeyError{key}) = result { - match key { - Key::Up | Key::Char('k') => self.move_up(), - Key::Char('K') => for _ in 0..10 { self.move_up() }, - Key::Char('J') => for _ in 0..10 { self.move_down() }, - Key::Down | Key::Char('j') => self.move_down(), - Key::Char('t') => self.toggle_fold()?, - Key::Char('g') | Key::Esc => self.popup_finnished()?, - |