Easier key handling

pull/3/head
Arijit Basu 3 years ago
parent 8221140756
commit 7beaec1763
No known key found for this signature in database
GPG Key ID: 7D7BF809E7378863

2
Cargo.lock generated

@ -1133,7 +1133,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "xplr" name = "xplr"
version = "0.1.8" version = "0.1.9"
dependencies = [ dependencies = [
"criterion", "criterion",
"crossterm", "crossterm",

@ -1,6 +1,6 @@
[package] [package]
name = "xplr" name = "xplr"
version = "0.1.8" # Update app.rs version = "0.1.9" # Update app.rs
authors = ["Arijit Basu <sayanarijit@gmail.com>"] authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018" edition = "2018"
description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf." description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf."

@ -3,43 +3,30 @@ use std::fs;
use xplr::*; use xplr::*;
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
let app = app::create()
.expect("failed to create app")
.change_directory(&"/tmp/xplr_bench".to_string())
.unwrap();
fs::create_dir_all("/tmp/xplr_bench").unwrap(); fs::create_dir_all("/tmp/xplr_bench").unwrap();
(1..10000).for_each(|i| { (1..10000).for_each(|i| {
fs::File::create(format!("/tmp/xplr_bench/{}", i)).unwrap(); fs::File::create(format!("/tmp/xplr_bench/{}", i)).unwrap();
}); });
let app = app::create()
.expect("failed to create app")
.change_directory(&"/tmp/xplr_bench".to_string())
.unwrap();
c.bench_function("focus next item", |b| { c.bench_function("focus next item", |b| {
b.iter(|| { b.iter(|| app.clone().handle(&config::Action::FocusNext))
app.clone()
.handle(&config::Action::Global(config::GlobalAction::FocusNext))
})
}); });
c.bench_function("focus previous item", |b| { c.bench_function("focus previous item", |b| {
b.iter(|| { b.iter(|| app.clone().handle(&config::Action::FocusPrevious))
app.clone().handle(&config::Action::Global(
config::GlobalAction::FocusPrevious,
))
})
}); });
c.bench_function("focus first item", |b| { c.bench_function("focus first item", |b| {
b.iter(|| { b.iter(|| app.clone().handle(&config::Action::FocusFirst))
app.clone()
.handle(&config::Action::Global(config::GlobalAction::FocusFirst))
})
}); });
c.bench_function("focus last item", |b| { c.bench_function("focus last item", |b| {
b.iter(|| { b.iter(|| app.clone().handle(&config::Action::FocusLast))
app.clone()
.handle(&config::Action::Global(config::GlobalAction::FocusLast))
})
}); });
} }
criterion_group!(benches, criterion_benchmark); criterion_group!(benches, criterion_benchmark);

@ -1,6 +1,4 @@
use crate::config::{ use crate::config::{Action, CommandConfig, Config, Mode};
Action, CommandConfig, Config, ExploreModeAction, GlobalAction, Mode, SelectModeAction,
};
use crate::error::Error; use crate::error::Error;
use crate::input::Key; use crate::input::Key;
use dirs; use dirs;
@ -142,8 +140,8 @@ impl DirectoryBuffer {
&config.general.normal_ui &config.general.normal_ui
}; };
let is_first_item = net_idx == 0; let is_first = net_idx == 0;
let is_last_item = net_idx == total.max(1) - 1; let is_last = net_idx == total.max(1) - 1;
let tree = config let tree = config
.general .general
@ -151,9 +149,9 @@ impl DirectoryBuffer {
.tree .tree
.clone() .clone()
.map(|t| { .map(|t| {
if is_last_item { if is_last {
t.2.format.clone() t.2.format.clone()
} else if is_first_item { } else if is_first {
t.0.format.clone() t.0.format.clone()
} else { } else {
t.1.format.clone() t.1.format.clone()
@ -191,8 +189,8 @@ impl DirectoryBuffer {
suffix: ui.suffix.clone(), suffix: ui.suffix.clone(),
tree: tree.into(), tree: tree.into(),
is_symlink, is_symlink,
is_first_item, is_first,
is_last_item, is_last,
is_dir, is_dir,
is_file, is_file,
is_readonly, is_readonly,
@ -201,7 +199,7 @@ impl DirectoryBuffer {
index: net_idx + 1, index: net_idx + 1,
focus_relative_index, focus_relative_index,
buffer_relative_index: rel_idx + 1, buffer_relative_index: rel_idx + 1,
total_items: total, total: total,
}; };
(abs.to_owned(), m) (abs.to_owned(), m)
}) })
@ -223,7 +221,7 @@ impl DirectoryBuffer {
}) })
} }
pub fn focused_item(&self) -> Option<(PathBuf, DirectoryItemMetadata)> { pub fn focused(&self) -> Option<(PathBuf, DirectoryItemMetadata)> {
self.focus.and_then(|f| { self.focus.and_then(|f| {
self.items self.items
.get(Self::relative_focus(f)) .get(Self::relative_focus(f))
@ -242,8 +240,8 @@ pub struct DirectoryItemMetadata {
pub prefix: String, pub prefix: String,
pub suffix: String, pub suffix: String,
pub tree: String, pub tree: String,
pub is_first_item: bool, pub is_first: bool,
pub is_last_item: bool, pub is_last: bool,
pub is_symlink: bool, pub is_symlink: bool,
pub is_dir: bool, pub is_dir: bool,
pub is_file: bool, pub is_file: bool,
@ -253,7 +251,7 @@ pub struct DirectoryItemMetadata {
pub index: usize, pub index: usize,
pub focus_relative_index: String, pub focus_relative_index: String,
pub buffer_relative_index: usize, pub buffer_relative_index: usize,
pub total_items: usize, pub total: usize,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -264,6 +262,7 @@ pub struct App {
pub saved_buffers: HashMap<PathBuf, Option<usize>>, pub saved_buffers: HashMap<PathBuf, Option<usize>>,
pub selected_paths: HashSet<PathBuf>, pub selected_paths: HashSet<PathBuf>,
pub mode: Mode, pub mode: Mode,
pub parsed_key_bindings: HashMap<Key, (String, Vec<Action>)>,
pub show_hidden: bool, pub show_hidden: bool,
pub call: Option<CommandConfig>, pub call: Option<CommandConfig>,
pub result: Option<String>, pub result: Option<String>,
@ -288,6 +287,8 @@ impl App {
directory_buffer.focus.clone(), directory_buffer.focus.clone(),
); );
let parsed_key_bindings = config.key_bindings.clone().filtered(&mode);
Ok(Self { Ok(Self {
version: VERSION.into(), version: VERSION.into(),
config: config.to_owned(), config: config.to_owned(),
@ -295,6 +296,7 @@ impl App {
saved_buffers, saved_buffers,
selected_paths: selected_paths.to_owned(), selected_paths: selected_paths.to_owned(),
mode, mode,
parsed_key_bindings,
show_hidden, show_hidden,
result: None, result: None,
call: None, call: None,
@ -342,7 +344,7 @@ impl App {
) )
} }
pub fn focus_first_item(self) -> Result<Self, Error> { pub fn focus_first(self) -> Result<Self, Error> {
let focus = if self.directory_buffer.total == 0 { let focus = if self.directory_buffer.total == 0 {
None None
} else { } else {
@ -360,7 +362,7 @@ impl App {
) )
} }
pub fn focus_last_item(self) -> Result<Self, Error> { pub fn focus_last(self) -> Result<Self, Error> {
let focus = if self.directory_buffer.total == 0 { let focus = if self.directory_buffer.total == 0 {
None None
} else { } else {
@ -387,7 +389,7 @@ impl App {
Ok(self) Ok(self)
} }
pub fn focus_next_item(self) -> Result<Self, Error> { pub fn focus_next(self) -> Result<Self, Error> {
let len = self.directory_buffer.total; let len = self.directory_buffer.total;
let focus = self let focus = self
.directory_buffer .directory_buffer
@ -406,7 +408,7 @@ impl App {
) )
} }
pub fn focus_previous_item(self) -> Result<Self, Error> { pub fn focus_previous(self) -> Result<Self, Error> {
let len = self.directory_buffer.total; let len = self.directory_buffer.total;
let focus = if len == 0 { let focus = if len == 0 {
None None
@ -499,7 +501,7 @@ impl App {
pub fn enter(self) -> Result<Self, Error> { pub fn enter(self) -> Result<Self, Error> {
let pwd = self let pwd = self
.directory_buffer .directory_buffer
.focused_item() .focused()
.map(|(p, _)| p) .map(|(p, _)| p)
.map(|p| { .map(|p| {
if p.is_dir() { if p.is_dir() {
@ -531,7 +533,7 @@ impl App {
pub fn select(self) -> Result<Self, Error> { pub fn select(self) -> Result<Self, Error> {
let selected_paths = self let selected_paths = self
.directory_buffer .directory_buffer
.focused_item() .focused()
.map(|(p, _)| { .map(|(p, _)| {
let mut selected_paths = self.selected_paths.clone(); let mut selected_paths = self.selected_paths.clone();
selected_paths.insert(p); selected_paths.insert(p);
@ -548,7 +550,7 @@ impl App {
pub fn toggle_selection(self) -> Result<Self, Error> { pub fn toggle_selection(self) -> Result<Self, Error> {
let selected_paths = self let selected_paths = self
.directory_buffer .directory_buffer
.focused_item() .focused()
.map(|(p, _)| { .map(|(p, _)| {
let mut selected_paths = self.selected_paths.clone(); let mut selected_paths = self.selected_paths.clone();
if selected_paths.contains(&p) { if selected_paths.contains(&p) {
@ -582,7 +584,7 @@ impl App {
let mut app = self; let mut app = self;
app.result = app app.result = app
.directory_buffer .directory_buffer
.focused_item() .focused()
.and_then(|(p, _)| p.to_str().map(|s| s.to_string())); .and_then(|(p, _)| p.to_str().map(|s| s.to_string()));
Ok(app) Ok(app)
} }
@ -623,148 +625,34 @@ impl App {
Err(Error::Terminated) Err(Error::Terminated)
} }
pub fn actions_from_key(&self, key: Key) -> Option<Vec<Action>> { pub fn actions_from_key(&self, key: &Key) -> Option<Vec<Action>> {
self.config self.parsed_key_bindings.get(key).map(|(_, a)| a.to_owned())
.key_bindings
.global
.get(&key)
.map(|m| {
m.actions
.iter()
.map(|a| Action::Global(a.clone()))
.collect()
})
.or_else(|| match &self.mode {
Mode::Explore => self.config.key_bindings.explore_mode.get(&key).map(|m| {
m.actions
.iter()
.map(|a| Action::ExploreMode(a.clone()))
.collect()
}),
Mode::Select => self.config.key_bindings.select_mode.get(&key).map(|m| {
m.actions
.iter()
.map(|a| Action::SelectMode(a.clone()))
.collect()
}),
Mode::ExploreSubmode(sub) => self
.config
.key_bindings
.explore_submodes
.get(sub)
.and_then(|kb| {
kb.get(&key).map(|m| {
m.actions
.iter()
.map(|a| Action::ExploreMode(a.clone()))
.collect()
})
}),
Mode::SelectSubmode(sub) => self
.config
.key_bindings
.select_submodes
.get(sub)
.and_then(|kb| {
kb.get(&key).map(|m| {
m.actions
.iter()
.map(|a| Action::SelectMode(a.clone()))
.collect()
})
}),
})
} }
pub fn handle(self, action: &Action) -> Result<Self, Error> { pub fn handle(self, action: &Action) -> Result<Self, Error> {
match action { match action {
// Global actions Action::ToggleShowHidden => self.toggle_hidden(),
Action::Global(GlobalAction::ToggleShowHidden) => self.toggle_hidden(), Action::Back => self.back(),
Action::Global(GlobalAction::Back) => self.back(), Action::Enter => self.enter(),
Action::Global(GlobalAction::Enter) => self.enter(), Action::FocusPrevious => self.focus_previous(),
Action::Global(GlobalAction::FocusNext) => self.focus_next_item(), Action::FocusNext => self.focus_next(),
Action::Global(GlobalAction::FocusPrevious) => self.focus_previous_item(), Action::FocusFirst => self.focus_first(),
Action::Global(GlobalAction::FocusFirst) => self.focus_first_item(), Action::FocusLast => self.focus_last(),
Action::Global(GlobalAction::FocusLast) => self.focus_last_item(), Action::FocusPathByIndex(i) => self.focus_by_index(i),
Action::Global(GlobalAction::FocusPath(path)) => self.focus_path(&path.into()), Action::FocusPathByBufferRelativeIndex(i) => self.focus_by_buffer_relative_index(i),
Action::Global(GlobalAction::FocusPathByIndex(n)) => self.focus_by_index(n), Action::FocusPathByFocusRelativeIndex(i) => self.focus_by_focus_relative_index(i),
Action::Global(GlobalAction::FocusPathByBufferRelativeIndex(n)) => { Action::FocusPath(p) => self.focus_path(&p.into()),
self.focus_by_buffer_relative_index(&n) Action::ChangeDirectory(d) => self.change_directory(d.into()),
} Action::Call(c) => self.call(c),
Action::Global(GlobalAction::FocusPathByFocusRelativeIndex(n)) => { Action::EnterSubmode(s) => self.enter_submode(s),
self.focus_by_focus_relative_index(&n) Action::ExitSubmode => self.exit_submode(),
} Action::Select => self.select(),
Action::Global(GlobalAction::ChangeDirectory(dir)) => self.change_directory(&dir), Action::ToggleSelection => self.toggle_selection(),
Action::Global(GlobalAction::Call(cmd)) => self.call(&cmd), Action::PrintFocused => self.print_focused(),
Action::Global(GlobalAction::PrintFocused) => self.print_focused(), Action::PrintSelected => self.print_selected(),
Action::Global(GlobalAction::PrintPwd) => self.print_pwd(), Action::PrintAppState => self.print_app_state(),
Action::Global(GlobalAction::PrintAppState) => self.print_app_state(), Action::Quit => self.quit(),
Action::Global(GlobalAction::Quit) => self.quit(), Action::Terminate => self.terminate(),
Action::Global(GlobalAction::Terminate) => self.terminate(),
// Explore mode
Action::ExploreMode(ExploreModeAction::ToggleShowHidden) => self.toggle_hidden(),
Action::ExploreMode(ExploreModeAction::Back) => self.back(),
Action::ExploreMode(ExploreModeAction::Enter) => self.enter(),
Action::ExploreMode(ExploreModeAction::FocusNext) => self.focus_next_item(),
Action::ExploreMode(ExploreModeAction::FocusPrevious) => self.focus_previous_item(),
Action::ExploreMode(ExploreModeAction::FocusFirst) => self.focus_first_item(),
Action::ExploreMode(ExploreModeAction::FocusLast) => self.focus_last_item(),
Action::ExploreMode(ExploreModeAction::FocusPath(path)) => {
self.focus_path(&path.into())
}
Action::ExploreMode(ExploreModeAction::FocusPathByIndex(n)) => self.focus_by_index(n),
Action::ExploreMode(ExploreModeAction::FocusPathByBufferRelativeIndex(n)) => {
self.focus_by_buffer_relative_index(&n)
}
Action::ExploreMode(ExploreModeAction::FocusPathByFocusRelativeIndex(n)) => {
self.focus_by_focus_relative_index(&n)
}
Action::ExploreMode(ExploreModeAction::ChangeDirectory(dir)) => {
self.change_directory(&dir)
}
Action::ExploreMode(ExploreModeAction::Call(cmd)) => self.call(&cmd),
Action::ExploreMode(ExploreModeAction::Select) => self.select(),
Action::ExploreMode(ExploreModeAction::EnterSubmode(submode)) => {
self.enter_submode(submode)
}
Action::ExploreMode(ExploreModeAction::ExitSubmode) => self.exit_submode(),
Action::ExploreMode(ExploreModeAction::PrintFocused) => self.print_focused(),
Action::ExploreMode(ExploreModeAction::PrintPwd) => self.print_pwd(),
Action::ExploreMode(ExploreModeAction::PrintAppState) => self.print_app_state(),
Action::ExploreMode(ExploreModeAction::Quit) => self.quit(),
// Select mode
Action::SelectMode(SelectModeAction::ToggleShowHidden) => self.toggle_hidden(),
Action::SelectMode(SelectModeAction::Back) => self.back(),
Action::SelectMode(SelectModeAction::Enter) => self.enter(),
Action::SelectMode(SelectModeAction::FocusNext) => self.focus_next_item(),
Action::SelectMode(SelectModeAction::FocusPrevious) => self.focus_previous_item(),
Action::SelectMode(SelectModeAction::FocusFirst) => self.focus_first_item(),
Action::SelectMode(SelectModeAction::FocusLast) => self.focus_last_item(),
Action::SelectMode(SelectModeAction::FocusPath(path)) => self.focus_path(&path.into()),
Action::SelectMode(SelectModeAction::FocusPathByIndex(n)) => self.focus_by_index(n),
Action::SelectMode(SelectModeAction::FocusPathByBufferRelativeIndex(n)) => {
self.focus_by_buffer_relative_index(&n)
}
Action::SelectMode(SelectModeAction::FocusPathByFocusRelativeIndex(n)) => {
self.focus_by_focus_relative_index(&n)
}
Action::SelectMode(SelectModeAction::ChangeDirectory(dir)) => {
self.change_directory(&dir)
}
Action::SelectMode(SelectModeAction::Call(cmd)) => self.call(&cmd),
Action::SelectMode(SelectModeAction::ToggleSelection) => self.toggle_selection(),
Action::SelectMode(SelectModeAction::EnterSubmode(submode)) => {
self.enter_submode(submode)
}
Action::SelectMode(SelectModeAction::ExitSubmode) => self.exit_submode(),
Action::SelectMode(SelectModeAction::PrintSelected) => self.print_selected(),
Action::SelectMode(SelectModeAction::PrintAppState) => self.print_app_state(),
Action::SelectMode(SelectModeAction::Quit) => self.quit(),
} }
} }
} }

@ -15,6 +15,69 @@ pub enum Mode {
SelectSubmode(String), SelectSubmode(String),
} }
impl Mode {
pub fn does_support(self, action: &Action) -> bool {
match (self, action) {
// Special
(_, Action::Terminate) => true,
// Explore mode
(Self::Explore, Action::Back) => true,
(Self::Explore, Action::Call(_)) => true,
(Self::Explore, Action::ChangeDirectory(_)) => true,
(Self::Explore, Action::Enter) => true,
(Self::Explore, Action::EnterSubmode(_)) => true,
(Self::Explore, Action::ExitSubmode) => false,
(Self::Explore, Action::FocusFirst) => true,
(Self::Explore, Action::FocusLast) => true,
(Self::Explore, Action::FocusNext) => true,
(Self::Explore, Action::FocusPath(_)) => true,
(Self::Explore, Action::FocusPathByBufferRelativeIndex(_)) => true,
(Self::Explore, Action::FocusPathByFocusRelativeIndex(_)) => true,
(Self::Explore, Action::FocusPathByIndex(_)) => true,
(Self::Explore, Action::FocusPrevious) => true,
(Self::Explore, Action::PrintAppState) => true,
(Self::Explore, Action::PrintFocused) => true,
(Self::Explore, Action::PrintSelected) => false,
(Self::Explore, Action::Quit) => true,
(Self::Explore, Action::Select) => true,
(Self::Explore, Action::ToggleSelection) => false,
(Self::Explore, Action::ToggleShowHidden) => true,
// Explore submode
(Self::ExploreSubmode(_), Action::ExitSubmode) => true,
(Self::ExploreSubmode(_), a) => Self::does_support(Self::Explore, a),
// Select mode
(Self::Select, Action::Back) => true,
(Self::Select, Action::Call(_)) => true,
(Self::Select, Action::ChangeDirectory(_)) => true,
(Self::Select, Action::Enter) => true,
(Self::Select, Action::EnterSubmode(_)) => true,
(Self::Select, Action::ExitSubmode) => true,
(Self::Select, Action::FocusFirst) => true,
(Self::Select, Action::FocusLast) => true,
(Self::Select, Action::FocusNext) => true,
(Self::Select, Action::FocusPath(_)) => true,
(Self::Select, Action::FocusPathByBufferRelativeIndex(_)) => true,
(Self::Select, Action::FocusPathByFocusRelativeIndex(_)) => true,
(Self::Select, Action::FocusPathByIndex(_)) => true,
(Self::Select, Action::FocusPrevious) => true,
(Self::Select, Action::PrintAppState) => true,
(Self::Select, Action::PrintFocused) => false,
(Self::Select, Action::PrintSelected) => true,
(Self::Select, Action::Quit) => true,
(Self::Select, Action::Select) => false,
(Self::Select, Action::ToggleSelection) => true,
(Self::Select, Action::ToggleShowHidden) => true,
// Select submode
(Self::SelectSubmode(_), Action::ExitSubmode) => true,
(Self::SelectSubmode(_), a) => Self::does_support(Self::Select, a),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Format { pub enum Format {
Line, Line,
@ -39,33 +102,7 @@ pub struct CommandConfig {
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GlobalAction { pub enum Action {
// Common actions
ToggleShowHidden,
Back,
Enter,
FocusPrevious,
FocusNext,
FocusFirst,
FocusLast,
FocusPath(String),
FocusPathByIndex(usize),
FocusPathByBufferRelativeIndex(usize),
FocusPathByFocusRelativeIndex(isize),
ChangeDirectory(String),
Call(CommandConfig),
// Quit options
PrintFocused,
PrintPwd,
PrintAppState,
Quit,
Terminate,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ExploreModeAction {
// Common actions
ToggleShowHidden, ToggleShowHidden,
Back, Back,
Enter, Enter,
@ -79,99 +116,80 @@ pub enum ExploreModeAction {
FocusPath(String), FocusPath(String),
ChangeDirectory(String), ChangeDirectory(String),
Call(CommandConfig), Call(CommandConfig),
// Explore mode exclusive options
EnterSubmode(String), EnterSubmode(String),
ExitSubmode, ExitSubmode,
Select, Select,
// Unselect, // Unselect,
// SelectAll, // SelectAll,
// SelectAllRecursive, // SelectAllRecursive,
// Quit options
PrintFocused,
PrintPwd,
PrintAppState,
Quit,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SelectModeAction {
// Common actions
ToggleShowHidden,
Back,
Enter,
FocusPrevious,
FocusNext,
FocusFirst,
FocusLast,
FocusPathByIndex(usize),
FocusPathByBufferRelativeIndex(usize),
FocusPathByFocusRelativeIndex(isize),
FocusPath(String),
ChangeDirectory(String),
Call(CommandConfig),
// Select mode exclusive options
EnterSubmode(String),
ExitSubmode,
// Select,
// Unselect,
// SelectAll,
// SelectAllRecursive,
// UnselectAll, // UnselectAll,
// UnSelectAllRecursive, // UnSelectAllRecursive,
ToggleSelection, ToggleSelection,
// ClearSelectedPaths, // ClearSelectedPaths,
// Quit options // Quit options
PrintFocused,
PrintSelected, PrintSelected,
PrintAppState, PrintAppState,
Quit, Quit,
} Terminate,
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
Global(GlobalAction),
ExploreMode(ExploreModeAction),
SelectMode(SelectModeAction),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GlobalActionMenu {
#[serde(default)]
pub help: String,
pub actions: Vec<GlobalAction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExploreModeActionMenu {
#[serde(default)]
pub help: String,
pub actions: Vec<ExploreModeAction>,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SelectModeActionMenu { pub struct ActionMenu {
#[serde(default)] #[serde(default)]
pub help: String, pub help: String,
pub actions: Vec<SelectModeAction>, pub actions: Vec<Action>,
} }
pub type ExploreSubmodeActionMenu = HashMap<Key, ExploreModeActionMenu>; pub type SubmodeActionMenu = HashMap<Key, ActionMenu>;
pub type SelectSubmodeActionMenu = HashMap<Key, SelectModeActionMenu>;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyBindings { pub struct KeyBindings {
pub global: HashMap<Key, GlobalActionMenu>, pub global: HashMap<Key, ActionMenu>,
#[serde(default)] #[serde(default)]
pub explore_mode: HashMap<Key, ExploreModeActionMenu>, pub explore_mode: HashMap<Key, ActionMenu>,
#[serde(default)] #[serde(default)]
pub explore_submodes: HashMap<String, ExploreSubmodeActionMenu>, pub explore_submodes: HashMap<String, SubmodeActionMenu>,
#[serde(default)] #[serde(default)]
pub select_mode: HashMap<Key, SelectModeActionMenu>, pub select_mode: HashMap<Key, ActionMenu>,
#[serde(default)] #[serde(default)]
pub select_submodes: HashMap<String, SelectSubmodeActionMenu>, pub select_submodes: HashMap<String, SubmodeActionMenu>,
}
impl KeyBindings {
pub fn filtered(&self, mode: &Mode) -> HashMap<Key, (String, Vec<Action>)> {
let mode_bindings: Option<HashMap<Key, ActionMenu>> = match mode {
Mode::Explore => Some(self.explore_mode.clone()),
Mode::ExploreSubmode(s) => self.explore_submodes.clone().get(s).map(|a| a.to_owned()),
Mode::Select => Some(self.select_mode.clone()),
Mode::SelectSubmode(s) => self.select_submodes.clone().get(s).map(|a| a.to_owned()),
};
let kb = self.global.clone().into_iter();
let kb: HashMap<Key, ActionMenu> = if let Some(modal_kb) = mode_bindings {
kb.chain(modal_kb.into_iter()).collect()
} else {
kb.collect()
};
kb.into_iter()
.map(|(k, am)| {
(
k.clone(),
(
am.help,
am.actions
.into_iter()
.filter(|a| mode.clone().does_support(a))
.collect::<Vec<Action>>(),
),
)
})
.filter(|(_, (_, actions))| !actions.is_empty())
.collect()
}
} }
impl Default for KeyBindings { impl Default for KeyBindings {

@ -52,7 +52,7 @@ fn main() -> Result<(), Error> {
let mut result = Ok(()); let mut result = Ok(());
'outer: for key in keys { 'outer: for key in keys {
if let Some(actions) = app.actions_from_key(key) { if let Some(actions) = app.actions_from_key(&key) {
for action in actions.iter() { for action in actions.iter() {
app = match app.handle(action) { app = match app.handle(action) {
Ok(mut a) => { Ok(mut a) => {
@ -71,7 +71,7 @@ fn main() -> Result<(), Error> {
if let Some(cmd) = a.call.clone() { if let Some(cmd) = a.call.clone() {
term::disable_raw_mode().unwrap(); term::disable_raw_mode().unwrap();
std::mem::drop(terminal); std::mem::drop(terminal);
if let Some((_, meta)) = a.directory_buffer.focused_item() { if let Some((_, meta)) = a.directory_buffer.focused() {
let _ = std::process::Command::new(cmd.command.clone()) let _ = std::process::Command::new(cmd.command.clone())
.current_dir(&a.directory_buffer.pwd) .current_dir(&a.directory_buffer.pwd)
.args( .args(

@ -6,7 +6,7 @@ fn test_key_down() {
assert_eq!(app.directory_buffer.focus, Some(0)); assert_eq!(app.directory_buffer.focus, Some(0));
let actions = app.actions_from_key(input::Key::Down).unwrap(); let actions = app.actions_from_key(&input::Key::Down).unwrap();
for action in actions { for action in actions {
app = app.handle(&action).unwrap() app = app.handle(&action).unwrap()

Loading…
Cancel
Save