summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinegret43 <67828321+Vinegret43@users.noreply.github.com>2021-10-02 15:20:02 +0300
committerGitHub <noreply@github.com>2021-10-02 08:20:02 -0400
commit5e3839ad3255d7056c00d09e479c234b87730ba8 (patch)
treec64f16062563c73571b24d5e4baf5bef866bf610
parent44c1952831abc4bc8496dfd60e72e47dcdc45caa (diff)
Add a help page (#95)
* add basic help page functionality change search_skim keybind * refactor 'run' function, improve sorting * add search functionality for help page improve some comments in tui_help
-rw-r--r--config/keymap.toml3
-rw-r--r--src/commands/command_keybind.rs7
-rw-r--r--src/commands/command_line.rs4
-rw-r--r--src/commands/help.rs94
-rw-r--r--src/commands/key_command.rs104
-rw-r--r--src/commands/mod.rs1
-rw-r--r--src/commands/rename_file.rs18
-rw-r--r--src/config/keymap/keymapping.rs4
-rw-r--r--src/run.rs11
-rw-r--r--src/ui/widgets/mod.rs2
-rw-r--r--src/ui/widgets/tui_help.rs174
-rw-r--r--src/util/input.rs16
12 files changed, 416 insertions, 22 deletions
diff --git a/config/keymap.toml b/config/keymap.toml
index 606d0c1..a4ae906 100644
--- a/config/keymap.toml
+++ b/config/keymap.toml
@@ -69,7 +69,7 @@ mapcommand = [
{ keys = [ "/" ], command = ":search " },
{ keys = [ "\\" ], command = ":search_glob " },
- { keys = [ "?" ], command = ":search_skim " },
+ { keys = [ "%", "s" ], command = ":search_skim " },
{ keys = [ "n" ], command = "search_next" },
{ keys = [ "N" ], command = "search_prev" },
@@ -86,4 +86,5 @@ mapcommand = [
{ keys = [ "g", "d" ], command = "cd ~/Downloads" },
{ keys = [ "g", "e" ], command = "cd /etc" },
{ keys = [ "g", "h" ], command = "cd ~/" },
+ { keys = [ "?" ], command = "help" }
]
diff --git a/src/commands/command_keybind.rs b/src/commands/command_keybind.rs
index 6fff00f..afe87ef 100644
--- a/src/commands/command_keybind.rs
+++ b/src/commands/command_keybind.rs
@@ -21,7 +21,12 @@ impl std::fmt::Display for CommandKeybind {
}
pub trait AppExecute {
- fn execute(&self, context: &mut AppContext, backend: &mut TuiBackend) -> JoshutoResult<()>;
+ fn execute(
+ &self,
+ context: &mut AppContext,
+ backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
+ ) -> JoshutoResult<()>;
}
pub trait AppCommand: AppExecute + std::fmt::Display + std::fmt::Debug {}
diff --git a/src/commands/command_line.rs b/src/commands/command_line.rs
index 0c80aa2..e9bcc07 100644
--- a/src/commands/command_line.rs
+++ b/src/commands/command_line.rs
@@ -1,6 +1,7 @@
use std::str::FromStr;
use crate::commands::KeyCommand;
+use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::error::JoshutoResult;
use crate::ui::views::TuiTextField;
@@ -11,6 +12,7 @@ use super::AppExecute;
pub fn readline(
context: &mut AppContext,
backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
prefix: &str,
suffix: &str,
) -> JoshutoResult<()> {
@@ -24,7 +26,7 @@ pub fn readline(
if let Some(s) = user_input {
let trimmed = s.trim_start();
let command = KeyCommand::from_str(trimmed)?;
- command.execute(context, backend)
+ command.execute(context, backend, keymap_t)
} else {
Ok(())
}
diff --git a/src/commands/help.rs b/src/commands/help.rs
new file mode 100644
index 0000000..3a01d91
--- /dev/null
+++ b/src/commands/help.rs
@@ -0,0 +1,94 @@
+use termion::event::{Event, Key};
+
+use crate::commands::{CommandKeybind, KeyCommand};
+use crate::config::AppKeyMapping;
+use crate::context::AppContext;
+use crate::error::JoshutoResult;
+use crate::event::AppEvent;
+use crate::ui::widgets;
+use crate::ui::widgets::TuiHelp;
+use crate::ui::TuiBackend;
+use crate::util::input;
+
+pub fn help_loop(
+ context: &mut AppContext,
+ backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
+) -> JoshutoResult<()> {
+ context.flush_event();
+
+ let mut offset = 0;
+ let mut search_query = String::new();
+ let mut sort_by = 1;
+
+ loop {
+ let keymap = if search_query.is_empty() {
+ widgets::get_keymap_table(keymap_t, &search_query, sort_by)
+ } else {
+ widgets::get_keymap_table(keymap_t, &search_query[1..], sort_by)
+ };
+ backend.render(TuiHelp::new(&keymap, &mut offset, &search_query));
+
+ let event = match context.poll_event() {
+ Ok(event) => event,
+ Err(_) => return Ok(()),
+ };
+
+ match event {
+ AppEvent::Termion(event) => {
+ if search_query.is_empty() {
+ match event {
+ Event::Key(Key::Esc) => break,
+ Event::Key(Key::Char('1')) => sort_by = 0,
+ Event::Key(Key::Char('2')) => sort_by = 1,
+ Event::Key(Key::Char('3')) => sort_by = 2,
+ Event::Key(Key::Char('/')) => search_query.push('/'),
+ event => {
+ if let Some(CommandKeybind::SimpleKeybind(command)) =
+ keymap_t.as_ref().get(&event)
+ {
+ match command {
+ KeyCommand::CursorMoveUp(_) => move_offset(&mut offset, -1),
+ KeyCommand::CursorMoveDown(_) => move_offset(&mut offset, 1),
+ KeyCommand::CursorMoveHome => offset = 0,
+ KeyCommand::CursorMoveEnd => offset = 255,
+ KeyCommand::CursorMovePageUp => move_offset(&mut offset, -10),
+ KeyCommand::CursorMovePageDown => move_offset(&mut offset, 10),
+ KeyCommand::CloseTab | KeyCommand::Help => break,
+ _ => (),
+ }
+ }
+ }
+ }
+ } else {
+ match event {
+ Event::Key(Key::Esc) => search_query.clear(),
+ Event::Key(Key::Backspace) => {
+ search_query.pop();
+ }
+ Event::Key(Key::Char(chr)) => search_query.push(chr),
+ _ => (),
+ }
+ }
+ context.flush_event();
+ }
+ _ => input::process_noninteractive(event, context),
+ }
+ }
+
+ Ok(())
+}
+
+// offset is a u8, so if we make it negative program will fail.
+// This function prevents this error
+fn move_offset(offset: &mut u8, moving_amount: i8) {
+ if moving_amount > 0 {
+ *offset += moving_amount as u8;
+ } else if moving_amount < 0 {
+ if *offset > -moving_amount as u8 {
+ *offset -= -moving_amount as u8;
+ } else {
+ *offset = 0;
+ }
+ }
+}
diff --git a/src/commands/key_command.rs b/src/commands/key_command.rs
index bd6c45c..9c99270 100644
--- a/src/commands/key_command.rs
+++ b/src/commands/key_command.rs
@@ -2,6 +2,7 @@ use std::path;
use termion::event::Key;
+use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::error::{JoshutoError, JoshutoErrorKind, JoshutoResult};
use crate::io::IoWorkerOptions;
@@ -76,6 +77,7 @@ pub enum KeyCommand {
NewTab,
CloseTab,
TabSwitch(i32),
+ Help,
}
impl KeyCommand {
@@ -138,6 +140,92 @@ impl KeyCommand {
Self::SortReverse => "sort reverse",
Self::TabSwitch(_) => "tab_switch",
+ Self::Help => "help",
+ }
+ }
+
+ // These comments are displayed at the help page
+ pub fn comment(&self) -> &'static str {
+ match self {
+ Self::BulkRename => "Bulk rename",
+ Self::ChangeDirectory(_) => "Change directory",
+ Self::NewTab => "Open a new tab",
+ Self::CloseTab => "Close current tab",
+ Self::CommandLine(command, _) => match command.trim() {
+ "cd" => "Change directory",
+ "search" => "Open a search prompt",
+ "search_glob" => "Glob search",
+ "rename" => "Rename selected file",
+ "touch" => "Touch file",
+ "mkdir" => "Make a new directory",
+ _ => "Open a command line",
+ },
+
+ Self::CutFiles => "Cut selected files",
+ Self::CopyFiles => "Copy selected files",
+ Self::PasteFiles(IoWorkerOptions {
+ overwrite,
+ skip_exist,
+ }) => match (overwrite, skip_exist) {
+ (true, false) => "Paste, overwrite",
+ (false, true) => "Paste, skip existing files",
+ _ => "Paste",
+ },
+ Self::CopyFileName => "Copy filename",
+ Self::CopyFileNameWithoutExtension => "Copy filename without extension",
+ Self::CopyFilePath => "Copy path to file",
+ Self::CopyDirPath => "Copy directory name",
+
+ Self::CursorMoveUp(_) => "Move cursor up",
+ Self::CursorMoveDown(_) => "Move cursor down",
+ Self::CursorMoveHome => "Move cursor to the very top",
+ Self::CursorMoveEnd => "Move cursor to the ver bottom",
+ Self::CursorMovePageUp => "Move cursor one page up",
+ Self::CursorMovePageDown => "Move cursor one page down",
+
+ Self::ParentCursorMoveUp(_) => "Cursor up in parent list",
+ Self::ParentCursorMoveDown(_) => "Cursor down in parent list",
+
+ Self::DeleteFiles => "Delete selected files",
+ Self::NewDirectory(_) => "Make a new directory",
+ Self::OpenFile => "Open a file",
+ Self::OpenFileWith(_) => "Open using selected program",
+ Self::ParentDirectory => "CD to parent directory",
+
+ Self::Quit => "Quit the program",
+ Self::QuitToCurrentDirectory => "Quit to current directory",
+ Self::ForceQuit => "Force quit",
+ Self::ReloadDirList => "Reload current dir listing",
+ Self::RenameFile(_) => "Rename file",
+ Self::TouchFile(_) => "Touch file",
+ Self::RenameFileAppend => "Rename a file",
+ Self::RenameFilePrepend => "Rename a file",
+
+ Self::SearchString(_) => "Search",
+ Self::SearchGlob(_) => "Search with globbing",
+ Self::SearchSkim => "Search via skim",
+ Self::SearchNext => "Next search entry",
+ Self::SearchPrev => "Previous search entry",
+
+ Self::SelectFiles(_, _) => "Select file",
+ Self::SetMode => "Set file permissions",
+ Self::SubProcess(_, false) => "Run a shell command",
+ Self::SubProcess(_, true) => "Run commmand in background",
+ Self::ShowWorkers(_) => "Show IO workers",
+
+ Self::ToggleHiddenFiles => "Toggle hidden files displaying",
+
+ Self::Sort(sort_type) => match sort_type {
+ SortType::Lexical => "Sort lexically",
+ SortType::Mtime => "Sort by modifiaction time",
+ SortType::Natural => "Sort naturally",
+ SortType::Size => "Sort by size",
+ SortType::Ext => "Sort by extension",
+ },
+ Self::SortReverse => "Reverse sort order",
+
+ Self::TabSwitch(_) => "Swith to the next tab",
+ Self::Help => "Open this help page",
}
}
}
@@ -369,6 +457,7 @@ impl std::str::FromStr for KeyCommand {
)),
},
"toggle_hidden" => Ok(Self::ToggleHiddenFiles),
+ "help" => Ok(Self::Help),
inp => Err(JoshutoError::new(
JoshutoErrorKind::UnrecognizedCommand,
format!("Unrecognized command '{}'", inp),
@@ -378,7 +467,12 @@ impl std::str::FromStr for KeyCommand {
}
impl AppExecute for KeyCommand {
- fn execute(&self, context: &mut AppContext, backend: &mut TuiBackend) -> JoshutoResult<()> {
+ fn execute(
+ &self,
+ context: &mut AppContext,
+ backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
+ ) -> JoshutoResult<()> {
match &*self {
Self::BulkRename => bulk_rename::bulk_rename(context, backend),
Self::ChangeDirectory(p) => {
@@ -388,7 +482,7 @@ impl AppExecute for KeyCommand {
Self::NewTab => tab_ops::new_tab(context),
Self::CloseTab => tab_ops::close_tab(context),
Self::CommandLine(p, s) => {
- command_line::readline(context, backend, p.as_str(), s.as_str())
+ command_line::readline(context, backend, keymap_t, p.as_str(), s.as_str())
}
Self::CutFiles => file_ops::cut(context),
Self::CopyFiles => file_ops::copy(context),
@@ -426,8 +520,8 @@ impl AppExecute for KeyCommand {
Self::ReloadDirList => reload::reload_dirlist(context),
Self::RenameFile(p) => rename_file::rename_file(context, p.as_path()),
- Self::RenameFileAppend => rename_file::rename_file_append(context, backend),
- Self::RenameFilePrepend => rename_file::rename_file_prepend(context, backend),
+ Self::RenameFileAppend => rename_file::rename_file_append(context, backend, keymap_t),
+ Self::RenameFilePrepend => rename_file::rename_file_prepend(context, backend, keymap_t),
Self::TouchFile(arg) => touch_file::touch_file(context, arg.as_str()),
Self::SearchGlob(pattern) => search_glob::search_glob(context, pattern.as_str()),
Self::SearchString(pattern) => search_string::search_string(context, pattern.as_str()),
@@ -453,6 +547,8 @@ impl AppExecute for KeyCommand {
tab_ops::tab_switch(*i, context)?;
Ok(())
}
+
+ Self::Help => help::help_loop(context, backend, keymap_t),
}
}
}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index bcf4b3a..d294b60 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -4,6 +4,7 @@ pub mod command_line;
pub mod cursor_move;
pub mod delete_files;
pub mod file_ops;
+pub mod help;
pub mod new_directory;
pub mod open_file;
pub mod parent_cursor_move;
diff --git a/src/commands/rename_file.rs b/src/commands/rename_file.rs
index 718aef8..1386211 100644
--- a/src/commands/rename_file.rs
+++ b/src/commands/rename_file.rs
@@ -1,5 +1,6 @@
use std::path;
+use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::error::JoshutoResult;
use crate::history::create_dirlist_with_history;
@@ -52,6 +53,7 @@ pub fn rename_file(context: &mut AppContext, dest: &path::Path) -> JoshutoResult
pub fn _rename_file_append(
context: &mut AppContext,
backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
file_name: &str,
) -> JoshutoResult<()> {
let (prefix, suffix): (String, String) = match file_name.rfind('.') {
@@ -61,10 +63,14 @@ pub fn _rename_file_append(
),
None => (format!("rename {}", file_name), "".to_string()),
};
- command_line::readline(context, backend, &prefix, &suffix)
+ command_line::readline(context, backend, keymap_t, &prefix, &suffix)
}
-pub fn rename_file_append(context: &mut AppContext, backend: &mut TuiBackend) -> JoshutoResult<()> {
+pub fn rename_file_append(
+ context: &mut AppContext,
+ backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
+) -> JoshutoResult<()> {
let mut file_name: Option<String> = None;
if let Some(curr_list) = context.tab_context_ref().curr_tab_ref().curr_list_ref() {
@@ -74,7 +80,7 @@ pub fn rename_file_append(context: &mut AppContext, backend: &mut TuiBackend) ->
}
if let Some(file_name) = file_name {
- _rename_file_append(context, backend, file_name.as_str())?;
+ _rename_file_append(context, backend, keymap_t, file_name.as_str())?;
}
Ok(())
}
@@ -82,16 +88,18 @@ pub fn rename_file_append(context: &mut AppContext, backend: &mut TuiBackend) ->
pub fn _rename_file_prepend(
context: &mut AppContext,
backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
file_name: String,
) -> JoshutoResult<()> {
let prefix = String::from("rename ");
let suffix = file_name;
- command_line::readline(context, backend, &prefix, &suffix)
+ command_line::readline(context, backend, keymap_t, &prefix, &suffix)
}
pub fn rename_file_prepend(
context: &mut AppContext,
backend: &mut TuiBackend,
+ keymap_t: &AppKeyMapping,
) -> JoshutoResult<()> {
let mut file_name: Option<String> = None;
@@ -102,7 +110,7 @@ pub fn rename_file_prepend(
}
if let Some(file_name) = file_name {
- _rename_file_prepend(context, backend, file_name)?;
+ _rename_file_prepend(context, backend, keymap_t, file_name)?;
}
Ok(())
}
diff --git a/src/config/keymap/keymapping.rs b/src/config/keymap/keymapping.rs
index 4a2e24e..071bdf7 100644
--- a/src/config/keymap/keymapping.rs
+++ b/src/config/keymap/keymapping.rs
@@ -236,6 +236,10 @@ impl AppKeyMapping {
let keys = [Event::Key(Key::Char('c')), Event::Key(Key::Char('w'))];
insert_keycommand(&mut m, cmd, &keys)?;
+ let cmd = KeyCommand::Help;
+ let keys = [Event::Key(Key::Char('?'))];
+ insert_keycommand(&mut m, cmd, &keys)?;
+
Ok(())
}
}
diff --git a/src/run.rs b/src/run.rs
index cb95499..19a09c6 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -37,9 +37,10 @@ pub fn run(
Ok(event) => event,
Err(_) => return Ok(()), // TODO
};
+
match event {
AppEvent::Termion(Event::Mouse(event)) => {
- input::process_mouse(event, context, backend);
+ input::process_mouse(event, context, backend, &keymap_t);
preview_default::load_preview(context, backend);
}
AppEvent::Termion(key) => {
@@ -49,13 +50,13 @@ pub fn run(
match key {
Event::Unsupported(s) if s.as_slice() == [27, 79, 65] => {
let command = KeyCommand::CursorMoveUp(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, &keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
}
Event::Unsupported(s) if s.as_slice() == [27, 79, 66] => {
let command = KeyCommand::CursorMoveDown(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, &keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
}
@@ -66,7 +67,7 @@ pub fn run(
.push_info(format!("Unmapped input: {}", key.to_string()));
}
Some(CommandKeybind::SimpleKeybind(command)) => {
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, &keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
}
@@ -77,7 +78,7 @@ pub fn run(
};
if let Some(command) = cmd {
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, &keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
}
diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs
index 6319d91..12e65b4 100644
--- a/src/ui/widgets/mod.rs
+++ b/src/ui/widgets/mod.rs
@@ -2,6 +2,7 @@ mod tui_dirlist;
mod tui_dirlist_detailed;
mod tui_file_preview;
mod tui_footer;
+mod tui_help;
mod tui_menu;
mod tui_prompt;
mod tui_tab;
@@ -13,6 +14,7 @@ pub use self::tui_dirlist::TuiDirList;
pub use self::tui_dirlist_detailed::{trim_file_label, TuiDirListDetailed};
pub use self::tui_file_preview::TuiFilePreview;
pub use self::tui_footer::TuiFooter;
+pub use self::tui_help::{get_keymap_table, TuiHelp};
pub use self::tui_menu::TuiMenu;
pub use self::tui_prompt::TuiPrompt;
pub use self::tui_tab::TuiTabBar;
diff --git a/src/ui/widgets/tui_help.rs b/src/ui/widgets/tui_help.rs
new file mode 100644
index 0000000..da4f6ed
--- /dev/null
+++ b/src/ui/widgets/tui_help.rs
@@ -0,0 +1,174 @@
+use tui::buffer::Buffer;
+use tui::layout::{Constraint, Rect};
+use tui::style::{Color, Modifier, Style};
+use tui::widgets::{Cell, Row, Table, Widget};
+
+use crate::commands::CommandKeybind;
+use crate::config::AppKeyMapping;
+use termion::event::{Event, Key};
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+ static ref COMMENT_STYLE: Style = Style::default().add_modifier(Modifier::REVERSED);
+ static ref DEFAULT_STYLE: Style = Style::default();
+ static ref HEADER_STYLE: Style = Style::default().fg(Color::Yellow);
+ static ref KEY_STYLE: Style = Style::default().fg(Color::Green);
+ static ref COMMAND_STYLE: Style = Style::default().fg(Color::Blue);
+}
+
+const TITLE: &str = "Keybindings";
+const FOOTER: &str = "Press <ESC> to return, / to search, 1,2,3 to sort";
+
+pub struct TuiHelp<'a> {
+ // This keymap is constructed with get_keymap_table function
+ keymap: &'a Vec<Row<'a>>,
+ offset: &'a mut u8,
+ search_query: &'a str,
+}
+
+impl<'a> TuiHelp<'a> {
+ pub fn new(keymap: &'a Vec<Row>, offset: &'a mut u8, search_query: &'a str) -> TuiHelp<'a> {
+ TuiHelp {
+ keymap,
+ offset,
+ search_query,
+ }
+ }
+}
+
+impl<'a> Widget for TuiHelp<'a> {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ // Subtracting 2 because we'll draw a title at the top and some
+ // additional information at the bottom of the page
+ let height = (area.bottom() - area.top() - 2) as i16;
+ let width = area.right() - area.left();
+ let max_offset = Ord::max(self.keymap.len() as i16 - height + 2, 0) as u8;
+ if *self.offset > max_offset {
+ *self.offset = max_offset;
+ }
+ let keymap = Vec::from(&self.keymap[(*self.offset as usize)..]);
+
+ let keybindings_area = Rect::new(0, 1, width, height as u16);
+ let mut keybindings_buffer = Buffer::default();
+ keybindings_buffer.resize(keybindings_area);
+ let widths = [
+ Constraint::Length((width as f32 * 0.12) as u16),
+ Constraint::Length((width as f32 * 0.50) as u16),
+ Constraint::Length((width as f32 * 0.38) as u16),
+ ];
+ let table_widget = Table::new(keymap)
+ .header(
+ Row::new(vec!["Key", "Command", "Description"])
+ .style(*HEADER_STYLE)
+ .bottom_margin(1),
+ )
+ .widths(&widths)
+ .column_spacing(1);
+
+ table_widget.render(keybindings_area, &mut keybindings_buffer);
+ buf.merge(&keybindings_buffer);
+ buf.set_stringn(
+ 0,
+ 0,
+ format!("{:^w$}", TITLE, w = width as usize),
+ width as usize,
+ *COMMENT_STYLE,
+ );
+
+ let footer = if self.search_query.is_empty() {
+ format!("{:^w$}", FOOTER, w = width as usize)
+ } else {
+ format!("{:<w$}", self.search_query, w = width as usize)
+ };
+ buf.set_stringn(
+ 0,
+ (height + 1) as u16,
+ &footer,
+ footer.len(),
+ *COMMENT_STYLE,
+ );
+ }
+}
+
+// Translates output from 'get_raw_keymap_table' into format,
+// readable by TUI table widget
+pub fn get_keymap_table<'a>(
+ keymap: &'a AppKeyMapping,
+ search_query: &'a str,
+ sort_by: usize,
+) -> Vec<Row<'a>> {
+ let raw_rows = get_raw_keymap_table(keymap, search_query, sort_by);
+ let mut rows = Vec::new();
+ for row in raw_rows {
+ rows.push(Row::new(vec![
+ Cell::from(row[0].clone()).style(*KEY_STYLE),
+ Cell::from(row[1].clone()).style(*COMMAND_STYLE),
+ Cell::from(row[2].clone()).style(*DEFAULT_STYLE),
+ ]));
+ }
+ rows
+}
+
+// This function is needed because we cannot access Row items, which
+// means that we won't be able to sort binds if we create Rows directly
+pub fn get_raw_keymap_table<'a>(
+ keymap: &'a AppKeyMapping,
+ search_query: &'a str,
+ sort_by: usize,
+) -> Vec<[String; 3]> {
+ let mut rows = Vec::new();
+ for (event, bind) in keymap.as_ref() {
+ let key = key_event_to_string(event);
+ let (command, comment) = match bind {
+ CommandKeybind::SimpleKeybind(command) => (format!("{}", command), command.comment()),
+ CommandKeybind::CompositeKeybind(sub_keymap) => {
+ let mut sub_rows = get_raw_keymap_table(sub_keymap, "", sort_by);
+ for _ in 0..sub_rows.len() {
+ let mut sub_row = sub_rows.pop().unwrap();
+ sub_row[0] = key.clone() + &sub_row[0];
+ if sub_row[0].contains(search_query) || sub_row[1].contains(search_query) {
+ rows.push(sub_row)
+ }
+ }
+ continue;
+ }
+ };
+ if key.contains(search_query) || command.contains(search_query) {
+ rows.push([key, command, comment.to_string()]);
+ }
+ }
+ rows.sort_by_cached_key(|x| x[sort_by].clone());
+ rows
+}
+
+fn key_event_to_string(event: &Event) -> String {
+ match event {
+ Event::Key(key) => match key {
+ Key::Backspace => "Backspace".to_string(),
+ Key::Left => "Left".to_string(),
+ Key::Right => "Right".to_string(),
+ Key::Up => "Up".to_string(),
+ Key::Down => "Down".to_string(),
+ Key::Home => "Home".to_string(),
+ Key::End => "End".to_string(),
+ Key::PageUp => "PageUp".to_string(),
+ Key::PageDown => "PageDown".to_string(),
+ Key::BackTab => "BackTab".to_string(),
+ Key::Delete => "Delete".to_string(),
+ Key::Insert => "Insert".to_string(),
+ Key::Esc => "Esc".to_string(),
+ Key::F(n) => format!("F{}", n),
+ Key::Char(chr) => match chr {
+ ' ' => "Space".to_string(),
+ '\t' => "Tab".to_string(),
+ '\n' => "Enter".to_string(),
+ chr => chr.to_string(),
+ },
+ Key::Alt(chr) => format!("Alt+{}", chr),
+ Key::Ctrl(chr) => format!("Ctrl+{}", chr),
+ _ => "".to_string(),
+ },
+ _ => "".to_string(),
+ }
+}
diff --git a/src/util/input.rs b/src/util/input.rs
index 9aa2231..48e416c 100644
--- a/src/util/input.rs
+++ b/src/util/input.rs
@@ -7,6 +7,7 @@ use termion::event::{MouseButton, MouseEvent};
use tui::layout::{Constraint, Direction, Layout};
use crate::commands::{cursor_move, parent_cursor_move, AppExecute, KeyCommand};
+use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::event::AppEvent;
use crate::fs::JoshutoDirList;
@@ -93,7 +94,12 @@ pub fn process_file_preview(
}
}
-pub fn process_mouse(event: MouseEvent, context: &mut AppContext, backend: &mut ui::TuiBackend) {
+pub fn process_mouse(
+ event: MouseEvent,
+ context: &mut AppContext,
+ backend: &mut ui::TuiBackend,
+ keymap_t: &AppKeyMapping,
+) {
let f_size = backend.terminal.as_ref().unwrap().size().unwrap();
let constraints: &[Constraint; 3] = &context.config_ref().display_options_ref().default_layout;
@@ -113,12 +119,12 @@ pub fn process_mouse(event: MouseEvent, context: &mut AppContext, backend: &mut
MouseEvent::Press(MouseButton::WheelUp, x, _) => {
if x < layout_rect[1].x {
let command = KeyCommand::ParentCursorMoveUp(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
} else if x < layout_rect[2].x {
let command = KeyCommand::CursorMoveUp(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
} else {
@@ -128,12 +134,12 @@ pub fn process_mouse(event: MouseEvent, context: &mut AppContext, backend: &mut
MouseEvent::Press(MouseButton::WheelDown, x, _) => {
if x < layout_rect[1].x {
let command = KeyCommand::ParentCursorMoveDown(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
} else if x < layout_rect[2].x {
let command = KeyCommand::CursorMoveDown(1);
- if let Err(e) = command.execute(context, backend) {
+ if let Err(e) = command.execute(context, backend, keymap_t) {
context.message_queue_mut().push_error(e.to_string());
}
} else {