mirror of https://github.com/bobwen-dev/hunter
add custom keybindings
This commit is contained in:
parent
c106fded2e
commit
889abf77ad
|
@ -515,6 +515,14 @@ dependencies = [
|
|||
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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)",
|
||||
|
@ -1206,6 +1217,11 @@ dependencies = [
|
|||
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -1311,6 +1327,22 @@ name = "strsim"
|
|||
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"
|
||||
|
@ -1455,6 +1487,11 @@ dependencies = [
|
|||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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 }
|
||||
|
|
184
README.md
184
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.
|
||||
Note: ```_``` means any key.
|
||||
|
||||
## Main view:
|
||||
## 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
|
||||
|
||||
| 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 |
|
||||
## 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_
|
||||
|
||||
## Keybindings in bookmark popup:
|
||||
## Media
|
||||
TogglePause=M-m
|
||||
ToggleMute=M-M
|
||||
SeekForward=M->
|
||||
SeekBackward=M-<
|
||||
|
||||
| Key | Action |
|
||||
| ------------------- |:---------------------------------|
|
||||
|(key) |open bookmark |
|
||||
|` |goto last cwd |
|
||||
|Ctrl(c) |cancel |
|
||||
|Alt(key) |delete bookmark |
|
||||
## Bookmarks
|
||||
GotoLastCwd=`
|
||||
Goto(_)=_
|
||||
Delete(_)=M-_
|
||||
|
||||
## Keybindings in process viewer:
|
||||
## 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=>
|
||||
|
||||
| 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 |
|
||||
## 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
|
||||
|
||||
## Keybindings in minibuffer:
|
||||
## Log
|
||||
Close=g,Esc
|
||||
|
||||
| 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 |
|
||||
## QuickActions
|
||||
Close=a, Esc, C-a
|
||||
SelectOrRun(_)=_
|
||||
|
|
BIN
config.tar.gz
BIN
config.tar.gz
Binary file not shown.
|
@ -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)
|
||||
}
|
||||
|
|
105
src/fail.rs
105
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
127
src/foldview.rs
127
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);
|
||||
|
||||
pub trait FoldableWidgetExt {
|
||||
// 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
|
||||
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()?,
|
||||
_ => { HError::undefined_key(key)?; },
|
||||
}
|
||||
// Key was defined, or _ match would have returned undefined key
|
||||
return Ok(());
|
||||
match ActingExt::do_key_ext(self, key) {
|
||||
Err(HError::PopupFinnished) => Err(HError::PopupFinnished),
|
||||
_ => self.do_key(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Foldable> Acting for ListView<Vec<F>>
|
||||
where
|
||||
ListView<Vec<F>>: FoldableWidgetExt,
|
||||
Bindings<<ListView<Vec<F>> as ActingExt>::Action>: Default
|
||||
{
|
||||
type Action = FoldAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.fold
|
||||
}
|
||||
|
||||
fn movement(&mut self, movement: &Movement) -> HResult<()> {
|
||||
use Movement::*;
|
||||
|
||||
|
||||
match movement {
|
||||
Up(n) => for _ in 0..*n { self.move_up() },
|
||||
Down(n) => for _ in 0..*n { self.move_down() },
|
||||
_ => { Err(KeyBindError::MovementUndefined)? },
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &FoldAction) -> HResult<()> {
|
||||
use FoldAction::*;
|
||||
|
||||
match action {
|
||||
ToggleFold => self.toggle_fold()
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,6 +19,56 @@ pub trait Listable {
|
|||
fn on_key(&mut self, _key: Key) -> HResult<()> { Ok(()) }
|
||||
}
|
||||
|
||||
use crate::keybind::{Acting, Bindings, FileListAction, Movement};
|
||||
|
||||
|
||||
impl Acting for ListView<Files> {
|
||||
type Action=FileListAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.filelist
|
||||
}
|
||||
|
||||
fn movement(&mut self, movement: &Movement) -> HResult<()> {
|
||||
use Movement::*;
|
||||
|
||||
match movement {
|
||||
Up(n) => { for _ in 0..*n { self.move_up(); }; self.refresh()?; }
|
||||
Down(n) => { for _ in 0..*n { self.move_down(); }; self.refresh()?; }
|
||||
PageUp => self.page_up(),
|
||||
PageDown => self.page_down(),
|
||||
Top => self.move_top(),
|
||||
Bottom => self.move_bottom(),
|
||||
Left | Right => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &Self::Action) -> HResult<()> {
|
||||
use FileListAction::*;
|
||||
|
||||
match action {
|
||||
Search => self.search_file()?,
|
||||
SearchNext => self.search_next()?,
|
||||
SearchPrev => self.search_prev()?,
|
||||
Filter => self.filter()?,
|
||||
Select => self.multi_select_file(),
|
||||
InvertSelection => self.invert_selection(),
|
||||
ClearSelection => self.clear_selections(),
|
||||
FilterSelection => self.toggle_filter_selected(),
|
||||
ToggleTag => self.toggle_tag()?,
|
||||
ToggleHidden => self.toggle_hidden(),
|
||||
ReverseSort => self.reverse_sort(),
|
||||
CycleSort => self.cycle_sort(),
|
||||
ToNextMtime => self.select_next_mtime(),
|
||||
ToPrevMtime => self.select_prev_mtime(),
|
||||
ToggleDirsFirst => self.toggle_dirs_first(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listable for ListView<Files> {
|
||||
fn len(&self) -> usize {
|
||||
self.content.len()
|
||||
|
@ -60,41 +110,7 @@ impl Listable for ListView<Files> {
|
|||
}
|
||||
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
match key {
|
||||
Key::Up | Key::Char('k') => {
|
||||
self.move_up();
|
||||
self.refresh()?;
|
||||
}
|
||||
Key::Char('K') => { for _ in 0..10 { self.move_up() } self.refresh()?; }
|
||||
Key::Char('J') => { for _ in 0..10 { self.move_down() } self.refresh()?; }
|
||||
Key::Down | Key::Char('j') => {
|
||||
self.move_down();
|
||||
self.refresh()?;
|
||||
},
|
||||
Key::PageUp => self.page_up(),
|
||||
Key::PageDown => self.page_down(),
|
||||
Key::Char('<') => self.move_top(),
|
||||
Key::Char('>') => self.move_bottom(),
|
||||
Key::Char('S') => { self.search_file().log(); }
|
||||
Key::Alt('s') => { self.search_next().log(); }
|
||||
Key::Alt('S') => { self.search_prev().log(); }
|
||||
Key::Ctrl('f') => { self.filter().log(); }
|
||||
Key::Left => self.goto_grand_parent()?,
|
||||
Key::Right => self.goto_selected()?,
|
||||
Key::Char(' ') => self.multi_select_file(),
|
||||
Key::Char('v') => self.invert_selection(),
|
||||
Key::Char('V') => self.clear_selections(),
|
||||
Key::Alt('v') => self.toggle_filter_selected(),
|
||||
Key::Char('t') => self.toggle_tag()?,
|
||||
Key::Char('H') => self.toggle_hidden(),
|
||||
Key::Char('r') => self.reverse_sort(),
|
||||
Key::Char('s') => self.cycle_sort(),
|
||||
Key::Char('N') => self.select_next_mtime(),
|
||||
Key::Char('n') => self.select_prev_mtime(),
|
||||
Key::Char('d') => self.toggle_dirs_first(),
|
||||
_ => { HError::undefined_key(key)? }
|
||||
}
|
||||
Ok(())
|
||||
self.do_key(key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@ extern crate systemstat;
|
|||
extern crate mime_guess;
|
||||
extern crate mime;
|
||||
extern crate clap;
|
||||
extern crate strum;
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
|
||||
extern crate osstrtools;
|
||||
extern crate pathbuftools;
|
||||
|
@ -61,7 +64,7 @@ mod trait_ext;
|
|||
mod config_installer;
|
||||
mod imgview;
|
||||
mod mediaview;
|
||||
|
||||
mod keybind;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -527,13 +527,7 @@ impl Widget for MediaView {
|
|||
}
|
||||
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
match key {
|
||||
Key::Alt('>') => self.seek_forward(),
|
||||
Key::Alt('<') => self.seek_backward(),
|
||||
Key::Alt('m') => self.toggle_pause(),
|
||||
Key::Alt('M') => Ok(self.toggle_mute()),
|
||||
_ => HError::undefined_key(key)
|
||||
}
|
||||
self.do_key(key)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,3 +539,27 @@ impl Drop for MediaView {
|
|||
self.core.clear().log();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
use crate::keybind::*;
|
||||
|
||||
impl Acting for MediaView {
|
||||
type Action = MediaAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.media
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &MediaAction) -> HResult<()> {
|
||||
use MediaAction::*;
|
||||
|
||||
match action {
|
||||
SeekForward => self.seek_forward()?,
|
||||
SeekBackward => self.seek_backward()?,
|
||||
TogglePause => self.toggle_pause()?,
|
||||
ToggleMute => self.toggle_mute(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -433,62 +433,7 @@ impl Widget for MiniBuffer {
|
|||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
let prev_input = self.input.clone();
|
||||
|
||||
match key {
|
||||
Key::Esc | Key::Ctrl('c') => {
|
||||
self.clear();
|
||||
self.input_cancelled()?;
|
||||
},
|
||||
Key::Char('\n') => {
|
||||
if self.input != "" {
|
||||
self.history.add(&self.query, &self.input);
|
||||
}
|
||||
self.input_finnished()?;
|
||||
}
|
||||
Key::Char('\t') => {
|
||||
self.complete()?;
|
||||
}
|
||||
Key::F(n) => {
|
||||
let fnstr = format!("${}", n-1);
|
||||
self.input.insert_str(self.position, &fnstr);
|
||||
self.position += 2;
|
||||
}
|
||||
Key::Backspace => {
|
||||
if self.position != 0 {
|
||||
self.input.remove(self.position - 1);
|
||||
self.position -= 1;
|
||||
}
|
||||
}
|
||||
Key::Delete | Key::Ctrl('d') => {
|
||||
if self.position != self.input.len() {
|
||||
self.input.remove(self.position);
|
||||
}
|
||||
}
|
||||
Key::Left | Key::Ctrl('b') => {
|
||||
if self.position != 0 {
|
||||
self.position -= 1;
|
||||
}
|
||||
}
|
||||
Key::Right | Key::Ctrl('f') => {
|
||||
if self.position != self.input.len() {
|
||||
self.position += 1;
|
||||
}
|
||||
}
|
||||
Key::Up | Key::Ctrl('p') | Key::Alt('p') => {
|
||||
self.history_up()?;
|
||||
}
|
||||
Key::Down | Key::Ctrl('n') | Key::Alt('n') => {
|
||||
self.history_down()?;
|
||||
}
|
||||
Key::Ctrl('u') => { self.clear_line()?; },
|
||||
Key::Ctrl('h') => { self.delete_word()?; },
|
||||
Key::Ctrl('a') => { self.position = 0 },
|
||||
Key::Ctrl('e') => { self.position = self.input.len(); },
|
||||
Key::Char(key) => {
|
||||
self.input.insert(self.position, key);
|
||||
self.position += 1;
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
self.do_key(key)?;
|
||||
|
||||
if self.continuous && prev_input != self.input {
|
||||
self.input_updated()?;
|
||||
|
@ -511,3 +456,65 @@ impl Widget for MiniBuffer {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
use crate::keybind::*;
|
||||
|
||||
impl Acting for MiniBuffer {
|
||||
type Action = MiniBufferAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.minibuffer
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &Self::Action) -> HResult<()> {
|
||||
use MiniBufferAction::*;
|
||||
|
||||
match action {
|
||||
InsertChar(ch) => {
|
||||
self.input.insert(self.position, *ch);
|
||||
self.position += 1;
|
||||
}
|
||||
InsertTab(n) => {
|
||||
let fnstr = format!("${}", n-1);
|
||||
self.input.insert_str(self.position, &fnstr);
|
||||
self.position += 2;
|
||||
}
|
||||
Cancel => { self.clear(); self.input_cancelled()? }
|
||||
Finish => {
|
||||
if self.input != "" {
|
||||
self.history.add(&self.query, &self.input);
|
||||
}
|
||||
self.input_finnished()?
|
||||
},
|
||||
Complete => self.complete()?,
|
||||
DeleteChar => {
|
||||
if self.position != self.input.len() {
|
||||
self.input.remove(self.position);
|
||||
}
|
||||
},
|
||||
BackwardDeleteChar => {
|
||||
if self.position != 0 {
|
||||
self.input.remove(self.position - 1);
|
||||
self.position -= 1;
|
||||
}
|
||||
}
|
||||
CursorLeft => {
|
||||
if self.position != 0 {
|
||||
self.position -= 1;
|
||||
}
|
||||
},
|
||||
CursorRight => {
|
||||
if self.position != self.input.len() {
|
||||
self.position += 1;
|
||||
}
|
||||
},
|
||||
HistoryUp => self.history_up()?,
|
||||
HistoryDown => self.history_down()?,
|
||||
ClearLine => self.clear_line()?,
|
||||
DeleteWord => self.delete_word()?,
|
||||
CursorToStart => self.position = 0,
|
||||
CursorToEnd => self.position = self.input.len(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@ pub fn config_path() -> HResult<PathBuf> {
|
|||
Ok(config_path)
|
||||
}
|
||||
|
||||
pub fn bindings_path() -> HResult<PathBuf> {
|
||||
let mut config_path = hunter_path()?;
|
||||
config_path.push("keys");
|
||||
Ok(config_path)
|
||||
}
|
||||
|
||||
pub fn bookmark_path() -> HResult<PathBuf> {
|
||||
let mut bookmark_path = hunter_path()?;
|
||||
bookmark_path.push("bookmarks");
|
||||
|
|
|
@ -656,30 +656,10 @@ impl Widget for ProcView {
|
|||
self.hbox.get_drawlist()
|
||||
}
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
match key {
|
||||
Key::Char('w') | Key::Esc => {
|
||||
self.animator.set_stale().log();
|
||||
self.get_core()?.clear().log();
|
||||
return Err(HError::PopupFinnished) }
|
||||
Key::Char('d') => { self.remove_proc()? }
|
||||
Key::Char('K') => { self.get_listview_mut().kill_proc()? }
|
||||
Key::Up | Key::Char('k') => {
|
||||
self.get_listview_mut().move_up();
|
||||
}
|
||||
Key::Down | Key::Char('j') => {
|
||||
self.get_listview_mut().move_down();
|
||||
}
|
||||
Key::Char('f') => { self.toggle_follow().log(); }
|
||||
Key::Ctrl('j') => { self.scroll_down().log(); },
|
||||
Key::Ctrl('k') => { self.scroll_up().log(); },
|
||||
Key::Ctrl('v') => { self.page_down().log(); },
|
||||
Key::Alt('v') => { self.page_up().log(); },
|
||||
Key::Char('>') => { self.scroll_bottom().log(); },
|
||||
Key::Char('<') => { self.scroll_top().log(); }
|
||||
_ => {}
|
||||
}
|
||||
self.do_key(key)?;
|
||||
self.refresh().log();
|
||||
self.draw().log();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -730,3 +710,67 @@ impl ConcatOsString for Vec<OsString> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
use crate::keybind::*;
|
||||
|
||||
impl Acting for ProcView {
|
||||
type Action = ProcessAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.process
|
||||
}
|
||||
|
||||
fn movement(&mut self, movement: &Movement) -> HResult<()> {
|
||||
self.get_listview_mut().movement(movement)
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &Self::Action) -> HResult<()> {
|
||||
use ProcessAction::*;
|
||||
|
||||
match action {
|
||||
Close => { self.animator.set_stale().log();
|
||||
self.core.clear().log();
|
||||
Err(HError::PopupFinnished)? }
|
||||
Remove => self.remove_proc()?,
|
||||
Kill => self.get_listview_mut().kill_proc()?,
|
||||
FollowOutput => self.toggle_follow()?,
|
||||
ScrollOutputDown => self.scroll_down()?,
|
||||
ScrollOutputUp => self.scroll_up()?,
|
||||
ScrollOutputPageDown => self.page_down()?,
|
||||
ScrollOutputPageUp => self.page_up()?,
|
||||
ScrollOutputBottom => self.scroll_bottom()?,
|
||||
ScrollOutputTop => self.scroll_top()?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Acting for ListView<Vec<Process>> {
|
||||
type Action=ProcessAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.process
|
||||
}
|
||||
|
||||
fn movement(&mut self, movement: &Movement) -> HResult<()> {
|
||||
use Movement::*;
|
||||
|
||||
match movement {
|
||||
Up(n) => { for _ in 0..*n { self.move_up(); }; self.refresh()?; }
|
||||
Down(n) => { for _ in 0..*n { self.move_down(); }; self.refresh()?; }
|
||||
PageUp => self.page_up(),
|
||||
PageDown => self.page_down(),
|
||||
Top => self.move_top(),
|
||||
Bottom => self.move_bottom(),
|
||||
Left | Right => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_action(&mut self, _action: &Self::Action) -> HResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,15 +12,16 @@ use std::ffi::OsString;
|
|||
use std::str::FromStr;
|
||||
|
||||
|
||||
use crate::fail::{HResult, HError};
|
||||
use crate::fail::{HResult, HError, KeyBindError};
|
||||
use crate::widget::{Widget, WidgetCore, Events};
|
||||
use crate::foldview::{Foldable, FoldableWidgetExt};
|
||||
use crate::foldview::{Foldable, FoldableWidgetExt, ActingExt};
|
||||
use crate::listview::ListView;
|
||||
use crate::proclist::ProcView;
|
||||
use crate::files::File;
|
||||
use crate::paths;
|
||||
use crate::term;
|
||||
use crate::term::ScreenExt;
|
||||
use crate::keybind::{Bindings, Movement, QuickActionAction};
|
||||
|
||||
|
||||
pub type QuickActionView = ListView<Vec<QuickActions>>;
|
||||
|
@ -66,47 +67,7 @@ impl FoldableWidgetExt for ListView<Vec<QuickActions>> {
|
|||
}
|
||||
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
match key {
|
||||
Key::Char('a') |
|
||||
Key::Char('h') |
|
||||
Key::Ctrl('c') |
|
||||
Key::Esc => HError::popup_finnished()?,
|
||||
// undefined key causes parent to handle move up/down
|
||||
Key::Char('j') => HError::undefined_key(key)?,
|
||||
Key::Char('k') => HError::undefined_key(key)?,
|
||||
Key::Char('l') => self.run_action(None),
|
||||
key @ Key::Char(_) => {
|
||||
let chr = match key {
|
||||
Key::Char(key) => key,
|
||||
// some other key that becomes None with letter_to_num()
|
||||
_ => 'x'
|
||||
};
|
||||
|
||||
let num = self.letter_to_num(chr);
|
||||
|
||||
if let Some(num) = num {
|
||||
// only select the action at first, to prevent accidents
|
||||
if self.get_selection() != num {
|
||||
self.set_selection(num);
|
||||
return Ok(());
|
||||
// activate the action the second time the key is pressed
|
||||
} else {
|
||||
if self.is_description_selected() {
|
||||
self.toggle_fold()?;
|
||||
} else {
|
||||
self.run_action(Some(num))?;
|
||||
HError::popup_finnished()?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Was a valid key, but not used, don't handle at parent
|
||||
return Ok(());
|
||||
}
|
||||
_ => HError::undefined_key(key)?
|
||||
}?;
|
||||
|
||||
HError::popup_finnished()?
|
||||
ActingExt::do_key_ext(self,key)
|
||||
}
|
||||
|
||||
fn render(&self) -> Vec<String> {
|
||||
|
@ -131,6 +92,50 @@ impl FoldableWidgetExt for ListView<Vec<QuickActions>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ActingExt for QuickActionView {
|
||||
type Action = QuickActionAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.quickaction
|
||||
}
|
||||
|
||||
fn movement(&mut self, movement: &Movement) -> HResult<()> {
|
||||
match movement {
|
||||
Movement::Right => self.run_action(None),
|
||||
_ => Err(KeyBindError::MovementUndefined)?
|
||||
}
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &Self::Action) -> HResult<()> {
|
||||
use crate::keybind::QuickActionAction::*;
|
||||
|
||||
match action {
|
||||
Close => self.popup_finnished(),
|
||||
SelectOrRun(chr) => {
|
||||
let num = self.letter_to_num(*chr);
|
||||
|
||||
if let Some(num) = num {
|
||||
// only select the action at first, to prevent accidents
|
||||
if self.get_selection() != num {
|
||||
self.set_selection(num);
|
||||
return Ok(());
|
||||
// activate the action the second time the key is pressed
|
||||
} else {
|
||||
if self.is_description_selected() {
|
||||
self.toggle_fold()?;
|
||||
} else {
|
||||
self.run_action(Some(num))?;
|
||||
HError::popup_finnished()?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl ListView<Vec<QuickActions>> {
|
||||
fn render(&self) -> Vec<String> {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use termion::event::Key;
|
||||
|
||||
use crate::widget::{Widget, WidgetCore};
|
||||
use crate::fail::{HResult, ErrorLog};
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
use crate::coordinates::Coordinates;
|
||||
|
||||
pub trait Tabbable {
|
||||
fn new_tab(&mut self) -> HResult<()>;
|
||||
fn close_tab(&mut self) -> HResult<()>;
|
||||
fn next_tab(&mut self) -> HResult<()>;
|
||||
fn prev_tab(&mut self) -> HResult<()>;
|
||||
fn goto_tab(&mut self, index: usize) -> HResult<()>;
|
||||
fn on_tab_switch(&mut self) -> HResult<()> {
|
||||
Ok(())
|
||||
|
@ -17,13 +18,7 @@ pub trait Tabbable {
|
|||
fn active_tab_mut(&mut self) -> &mut dyn Widget;
|
||||
fn on_key_sub(&mut self, key: Key) -> HResult<()>;
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
match key {
|
||||
Key::F(n) => self.goto_tab(n as usize -1),
|
||||
Key::Ctrl('t') => self.new_tab(),
|
||||
Key::Ctrl('w') => self.close_tab(),
|
||||
Key::Char('\t') => self.next_tab(),
|
||||
_ => self.on_key_sub(key)
|
||||
}
|
||||
self.on_key_sub(key)
|
||||
}
|
||||
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
|
||||
fn on_config_loaded(&mut self) -> HResult<()> { Ok(()) }
|
||||
|
@ -105,6 +100,15 @@ impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||
}
|
||||
self.on_tab_switch().log();
|
||||
}
|
||||
|
||||
pub fn prev_tab_(&mut self) {
|
||||
if self.active == 0 {
|
||||
self.active = self.widgets.len() - 1;
|
||||
} else {
|
||||
self.active -= 1;
|
||||
}
|
||||
self.on_tab_switch().log();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
||||
|
@ -173,7 +177,35 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||
}
|
||||
|
||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||
Tabbable::on_key(self, key)?;
|
||||
match self.do_key(key) {
|
||||
Err(HError::WidgetUndefinedKeyError{..}) => Tabbable::on_key(self, key)?,
|
||||
e @ _ => e?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
use crate::keybind::*;
|
||||
|
||||
impl<T: Widget> Acting for TabView<T> where TabView<T>: Tabbable {
|
||||
type Action = TabAction;
|
||||
|
||||
fn search_in(&self) -> Bindings<Self::Action> {
|
||||
self.core.config().keybinds.tab
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: &Self::Action) -> HResult<()> {
|
||||
use TabAction::*;
|
||||
|
||||
match action {
|
||||
GotoTab(n) => self.goto_tab(*n)?,
|
||||
NewTab => self.new_tab()?,
|
||||
CloseTab => self.close_tab()?,
|
||||
NextTab => self.next_tab()?,
|
||||
PrevTab => self.prev_tab()?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue