summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-07-25 21:35:33 +0200
committerrabite <rabite@posteo.de>2019-07-25 21:56:30 +0200
commit889abf77ada1058c76c58f4b9a9500d7b49c58d6 (patch)
tree6fe186488d6b6bd61c8817260d591fd587fcd979
parentc106fded2e72434d9a98b8c227d6981e5caee5fe (diff)
add custom keybindings
-rw-r--r--Cargo.lock42
-rw-r--r--Cargo.toml7
-rw-r--r--README.md206
-rw-r--r--config.tar.gzbin2494 -> 3337 bytes
-rw-r--r--src/config.rs10
-rw-r--r--src/fail.rs105
-rw-r--r--src/file_browser.rs105
-rw-r--r--src/foldview.rs127
-rw-r--r--src/keybind.rs1094
-rw-r--r--src/listview.rs86
-rw-r--r--src/main.rs5
-rw-r--r--src/mediaview.rs32
-rw-r--r--src/minibuffer.rs119
-rw-r--r--src/paths.rs6
-rw-r--r--src/proclist.rs88
-rw-r--r--src/quick_actions.rs91
-rw-r--r--src/tabview.rs50
17 files changed, 1832 insertions, 341 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 840fc80..d27cdbb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index b32ec48..5356c4b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 }
diff --git a/README.md b/README.md
index 42cdc79..265016a 100644
--- a/README.md
+++ b/README.md
@@ -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
index 91fdb29..8f571dc 100644
--- a/config.tar.gz
+++ b/config.tar.gz
Binary files differ
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()?,
-