summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/browser_states.rs18
-rw-r--r--src/browser_verbs.rs51
-rw-r--r--src/commands.rs42
-rw-r--r--src/conf.rs9
-rw-r--r--src/help_states.rs12
-rw-r--r--src/help_verbs.rs16
-rw-r--r--src/verb_conf.rs3
-rw-r--r--src/verb_store.rs55
8 files changed, 132 insertions, 74 deletions
diff --git a/src/browser_states.rs b/src/browser_states.rs
index af28388..bffc8ca 100644
--- a/src/browser_states.rs
+++ b/src/browser_states.rs
@@ -71,7 +71,7 @@ impl BrowserState {
)
}
- fn page_height(screen: &Screen) -> i32 {
+ pub fn page_height(screen: &Screen) -> i32 {
i32::from(screen.h) - 2
}
@@ -87,7 +87,7 @@ impl BrowserState {
self.filtered_tree.as_mut().unwrap_or(&mut self.tree)
}
- fn open_selection_stay_in_broot(
+ pub fn open_selection_stay_in_broot(
&mut self,
screen: &mut Screen,
_con: &AppContext,
@@ -130,7 +130,7 @@ impl BrowserState {
}
}
- fn open_selection_quit_broot(
+ pub fn open_selection_quit_broot(
&mut self,
screen: &mut Screen,
con: &AppContext,
@@ -242,14 +242,6 @@ impl AppState for BrowserState {
self.displayed_tree_mut().move_selection(*dy, page_height);
Ok(AppStateCmdResult::Keep)
}
- Action::ScrollPage(dp) => {
- let tree = self.displayed_tree_mut();
- if page_height < tree.lines.len() as i32 {
- let dy = dp * page_height;
- tree.try_scroll(dy, page_height);
- }
- Ok(AppStateCmdResult::Keep)
- }
Action::Click(_, y) => {
let y = *y as i32 - 1; // click position starts at (1, 1)
self.displayed_tree_mut().try_select_y(y);
@@ -267,6 +259,10 @@ impl AppState for BrowserState {
}
Action::OpenSelection => self.open_selection_stay_in_broot(screen, con),
Action::AltOpenSelection => self.open_selection_quit_broot(screen, con),
+ Action::VerbIndex(index) => {
+ let verb = &con.verb_store.verbs[*index];
+ self.execute_verb(verb, &verb.invocation, screen, con)
+ },
Action::VerbInvocate(invocation) => match con.verb_store.search(&invocation.key) {
PrefixSearchResult::Match(verb) => {
self.execute_verb(verb, &invocation, screen, con)
diff --git a/src/browser_verbs.rs b/src/browser_verbs.rs
index d8baf20..5e91910 100644
--- a/src/browser_verbs.rs
+++ b/src/browser_verbs.rs
@@ -6,7 +6,7 @@ use crate::app::AppStateCmdResult;
use crate::app_context::AppContext;
use crate::browser_states::BrowserState;
use crate::commands::Command;
-use crate::external::{self, Launchable};
+use crate::external;
use crate::flat_tree::Tree;
use crate::help_states::HelpState;
use crate::screens::Screen;
@@ -42,30 +42,55 @@ impl VerbExecutor for BrowserState {
if let Some(err) = verb.match_error(invocation) {
return Ok(AppStateCmdResult::DisplayError(err));
}
- let tree = self.displayed_tree();
- let line = &tree.selected_line();
+ let page_height = BrowserState::page_height(screen);
Ok(match verb.execution.as_ref() {
":back" => AppStateCmdResult::PopState,
":focus" => {
- let mut path = tree.selected_line().target();
+ let tree = self.displayed_tree_mut();
+ let line = &tree.selected_line();
+ let mut path = line.target();
if !path.is_dir() {
path = path.parent().unwrap().to_path_buf();
}
focus_path(path, screen, tree)
}
- ":focus_root" => focus_path(PathBuf::from("/"), screen, tree),
+ ":focus_root" => focus_path(PathBuf::from("/"), screen, self.displayed_tree()),
":focus_user_home" => match UserDirs::new() {
- Some(ud) => focus_path(ud.home_dir().to_path_buf(), screen, tree),
+ Some(ud) => focus_path(ud.home_dir().to_path_buf(), screen, self.displayed_tree()),
None => AppStateCmdResult::DisplayError("no user home directory found".to_string()), // does this happen ?
}
":help" => AppStateCmdResult::NewState(Box::new(HelpState::new(screen, con)), Command::new()),
- ":open" => AppStateCmdResult::from(Launchable::opener(line.target())),
- ":parent" => match &line.path.parent() {
- Some(path) => focus_path(path.to_path_buf(), screen, tree),
+ //":open" => AppStateCmdResult::from(Launchable::opener(self.displayed_tree().selected_line().target())),
+ ":open_stay" => self.open_selection_stay_in_broot(screen, con)?,
+ ":open_leave" => self.open_selection_quit_broot(screen, con)?,
+ ":line_down" => {
+ self.displayed_tree_mut().move_selection(1, page_height);
+ AppStateCmdResult::Keep
+ }
+ ":line_up" => {
+ self.displayed_tree_mut().move_selection(-1, page_height);
+ AppStateCmdResult::Keep
+ }
+ ":page_down" => {
+ let tree = self.displayed_tree_mut();
+ if page_height < tree.lines.len() as i32 {
+ tree.try_scroll(page_height, page_height);
+ }
+ AppStateCmdResult::Keep
+ }
+ ":page_up" => {
+ let tree = self.displayed_tree_mut();
+ if page_height < tree.lines.len() as i32 {
+ tree.try_scroll(-page_height, page_height);
+ }
+ AppStateCmdResult::Keep
+ }
+ ":parent" => match &self.displayed_tree().selected_line().path.parent() {
+ Some(path) => focus_path(path.to_path_buf(), screen, self.displayed_tree()),
None => AppStateCmdResult::DisplayError("no parent found".to_string()),
}
- ":print_path" => external::print_path(&line.target(), con)?,
- ":print_tree" => external::print_tree(&tree, screen, con)?,
+ ":print_path" => external::print_path(&self.displayed_tree().selected_line().target(), con)?,
+ ":print_tree" => external::print_tree(&self.displayed_tree(), screen, con)?,
":refresh" => AppStateCmdResult::RefreshState,
":select_first" => {
self.displayed_tree_mut().try_select_first();
@@ -81,7 +106,7 @@ impl VerbExecutor for BrowserState {
":toggle_git_ignore" => self.with_new_options(screen, &|options| {
options.respect_git_ignore = match options.respect_git_ignore {
OptionBool::Auto => {
- if tree.nb_gitignored > 0 {
+ if self.displayed_tree().nb_gitignored > 0 {
OptionBool::No
} else {
OptionBool::Yes
@@ -95,7 +120,7 @@ impl VerbExecutor for BrowserState {
":toggle_sizes" => self.with_new_options(screen, &|o| o.show_sizes ^= true),
":toggle_trim_root" => self.with_new_options(screen, &|o| o.trim_root ^= true),
":quit" => AppStateCmdResult::Quit,
- _ => verb.to_cmd_result(&line.path.clone(), &invocation.args, screen, con)?,
+ _ => verb.to_cmd_result(&self.displayed_tree().selected_line().path.clone(), &invocation.args, screen, con)?,
})
}
}
diff --git a/src/commands.rs b/src/commands.rs
index f3dfb4a..ab5a75c 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -30,11 +30,11 @@ struct CommandParts {
#[derive(Debug, Clone)]
pub enum Action {
MoveSelection(i32), // up (neg) or down (positive) in the list
- ScrollPage(i32), // in number of pages, not lines
OpenSelection, // open the selected line
AltOpenSelection, // alternate open the selected line
VerbEdit(VerbInvocation), // verb invocation, unfinished
- VerbInvocate(VerbInvocation), // verb invocation, after the user hit enter (or used a trigger key)
+ VerbInvocate(VerbInvocation), // verb invocation, after the user hit enter
+ VerbIndex(usize), // verb call, withtout specific argument (using a trigger key)
FuzzyPatternEdit(String), // a pattern being edited
RegexEdit(String, String), // a regex being edited (core & flags)
Back, // back to last app state, or clear pattern
@@ -140,6 +140,7 @@ impl Command {
con: &AppContext,
) {
let mut handled_by_input_field = false;
+ debug!("add_event {:?}", event);
match event {
Event::Click(x, y) => {
if !input_field.apply_event(&event) {
@@ -150,18 +151,10 @@ impl Command {
self.action = Action::DoubleClick(*x, *y);
}
Event::Key(key) => {
- // we start by looking if the key is the trigger key of
- // one of the verbs
- for verb in &con.verb_store.verbs {
- if let Some(verb_key) = verb.key {
- if verb_key == *key {
- // Cloning the invocation when we already know the verb
- // isn't very clean or efficient (it means a second search will
- // occur behind but it's simpler to manage now
- self.action = Action::VerbInvocate(verb.invocation.clone());
- return;
- }
- }
+ // we start by looking if the key is the trigger key of one of the verbs
+ if let Some(index) = con.verb_store.index_of_key(key) {
+ self.action = Action::VerbIndex(index);
+ return;
}
match *key {
KeyEvent::Char('\t') => {
@@ -170,25 +163,20 @@ impl Command {
KeyEvent::BackTab => {
self.action = Action::Previous;
}
+
+ // this may be a call to open_stay, or simply
+ // validating the verb choice in the input
KeyEvent::Char('\n') => {
self.action = Action::from(&self.parts, true);
}
+
+ // Normally redundant due to internal verb but
+ // I'm not yet 100% sure it's Alt('\r') on all platforms
KeyEvent::Alt('\r') | KeyEvent::Alt('\n') => {
self.action = Action::AltOpenSelection;
}
- KeyEvent::Up => {
- self.action = Action::MoveSelection(-1);
- }
- KeyEvent::Down => {
- self.action = Action::MoveSelection(1);
- }
- KeyEvent::PageUp | KeyEvent::Ctrl('u') => {
- self.action = Action::ScrollPage(-1);
- }
- KeyEvent::PageDown | KeyEvent::Ctrl('d') => {
- self.action = Action::ScrollPage(1);
- }
- KeyEvent::Char(c) if c =='?' && (self.raw.is_empty() || self.parts.verb_invocation.is_some()) => {
+
+ KeyEvent::Char('?') if self.raw.is_empty() || self.parts.verb_invocation.is_some() => {
// a '?' opens the help when it's the first char or when it's part of the verb
// invocation
self.action = Action::Help;
diff --git a/src/conf.rs b/src/conf.rs
index b9ded64..80214ee 100644
--- a/src/conf.rs
+++ b/src/conf.rs
@@ -156,19 +156,12 @@ impl Conf {
const DEFAULT_CONF_FILE: &str = r#"
# This configuration file lets you define new commands
-# or change the shortcut of built-in verbs.
+# or change the shortcut or triggering keys of built-in verbs.
# You can change the colors of broot too.
#
# Configuration documentation is available at https://dystroy.org/broot
#
-###############################
-# shortcuts for built-in verbs:
-
-[[verbs]]
-invocation = "p"
-execution = ":parent"
-
#####################
# user defined verbs:
diff --git a/src/help_states.rs b/src/help_states.rs
index aa77fed..295f3af 100644
--- a/src/help_states.rs
+++ b/src/help_states.rs
@@ -16,7 +16,7 @@ use crate::verbs::VerbExecutor;
/// an application state dedicated to help
pub struct HelpState {
- view: MadView,
+ pub view: MadView,
}
impl HelpState {
@@ -30,7 +30,7 @@ impl HelpState {
fn resize_area(&mut self, screen: &Screen) {
let mut area = Area::new(0, 0, screen.w, screen.h - 2);
- area.pad_for_max_width(100);
+ area.pad_for_max_width(110);
self.view.resize(&area);
}
}
@@ -45,6 +45,10 @@ impl AppState for HelpState {
self.resize_area(screen);
Ok(match &cmd.action {
Action::Back => AppStateCmdResult::PopState,
+ Action::VerbIndex(index) => {
+ let verb = &con.verb_store.verbs[*index];
+ self.execute_verb(verb, &verb.invocation, screen, con)?
+ },
Action::VerbInvocate(invocation) => match con.verb_store.search(&invocation.key) {
PrefixSearchResult::Match(verb) => {
self.execute_verb(verb, &invocation, screen, con)?
@@ -55,10 +59,6 @@ impl AppState for HelpState {
self.view.try_scroll_lines(*dy);
AppStateCmdResult::Keep
}
- Action::ScrollPage(dp) => {
- self.view.try_scroll_pages(*dp);
- AppStateCmdResult::Keep
- }
_ => AppStateCmdResult::Keep,
})
}
diff --git a/src/help_verbs.rs b/src/help_verbs.rs
index 7ee6030..998e4db 100644
--- a/src/help_verbs.rs
+++ b/src/help_verbs.rs
@@ -36,7 +36,23 @@ impl VerbExecutor for HelpState {
Command::new(),
),
":help" => AppStateCmdResult::Keep,
+ ":line_down" => {
+ self.view.try_scroll_lines(1);
+ AppStateCmdResult::Keep
+ }
+ ":line_up" => {
+ self.view.try_scroll_lines(-1);
+ AppStateCmdResult::Keep
+ }
":open" => AppStateCmdResult::from(Launchable::opener(Conf::default_location())),
+ ":page_down" => {
+ self.view.try_scroll_pages(1);
+ AppStateCmdResult::Keep
+ }
+ ":page_up" => {
+ self.view.try_scroll_pages(-1);
+ AppStateCmdResult::Keep
+ }
":print_path" => external::print_path(&Conf::default_location(), con)?,
":quit" => AppStateCmdResult::Quit,
":focus_user_home" | ":focus_root" => AppStateCmdResult::PopStateAndReapply,
diff --git a/src/verb_conf.rs b/src/verb_conf.rs
index 41fe733..5cc97e2 100644
--- a/src/verb_conf.rs
+++ b/src/verb_conf.rs
@@ -62,8 +62,7 @@ pub fn parse_key(raw: &str) -> Result<KeyEvent, ConfError> {
("ctrl", Some(minor)) | ("^", Some(minor)) => KeyEvent::Ctrl(
minor.as_str().chars().next().unwrap().to_ascii_lowercase()
),
- // other possible mappings are disabled as they would break
- // basic behaviors of broot
+ // other possible mappings are disabled as they would break basic behaviors of broot
_ => bad_key(raw)?
}),
None => bad_key(raw)
diff --git a/src/verb_store.rs b/src/verb_store.rs
index 21ae064..92b3b76 100644
--- a/src/verb_store.rs
+++ b/src/verb_store.rs
@@ -59,7 +59,7 @@ impl VerbStore {
}
self.add_builtin(
"back",
- None,
+ None, // esc is mapped in commands.rs
None,
"revert to the previous state (mapped to `<esc>`)",
);
@@ -101,6 +101,18 @@ impl VerbStore {
Some("?".to_string()),
"display broot's help",
);
+ self.add_builtin(
+ "line_down",
+ Some(KeyEvent::Down),
+ None,
+ "move one line down"
+ );
+ self.add_builtin(
+ "line_up",
+ Some(KeyEvent::Up),
+ None,
+ "move one line up"
+ );
self.verbs.push(
Verb::create_external(
"mkdir {subpath}",
@@ -128,15 +140,33 @@ impl VerbStore {
.unwrap(),
);
self.add_builtin(
- "open",
+ "open_stay",
+ None, // default mapping directly handled in commands#add_event
None,
+ "open file or directory according to OS settings (stays in broot)",
+ );
+ self.add_builtin(
+ "open_leave",
+ None, // default mapping directly handled in commands#add_event
None,
"open file or directory according to OS settings (quit broot)",
);
self.add_builtin(
- "parent",
+ "page_down",
+ Some(KeyEvent::PageDown),
None,
+ "scroll one page down"
+ );
+ self.add_builtin(
+ "page_up",
+ Some(KeyEvent::PageUp),
+ None,
+ "scroll one page up"
+ );
+ self.add_builtin(
+ "parent",
None,
+ Some("p".to_string()),
"move to the parent directory"
);
self.add_builtin(
@@ -249,10 +279,10 @@ impl VerbStore {
_ => PrefixSearchResult::TooManyMatches,
}
}
- // return the index of the verb having the long name. This function is meant
- // for internal access when it's sure it can't failed (i.e. for a builtin)
- // It looks for verbs by key, starting from the builtins, to
- // ensure it hasn't been overriden.
+ /// return the index of the verb having the long name. This function is meant
+ /// for internal access when it's sure it can't failed (i.e. for a builtin)
+ /// It looks for verbs by key, starting from the builtins, to
+ /// ensure it hasn't been overriden.
pub fn index_of(&self, name: &str) -> usize {
for i in 0..self.verbs.len() {
if self.verbs[i].invocation.key == name {
@@ -261,4 +291,15 @@ impl VerbStore {
}
panic!("invalid verb search");
}
+ /// return the index of the verb which is triggered by the given key, if any
+ pub fn index_of_key(&self, key: &KeyEvent) -> Option<usize> {
+ for i in 0..self.verbs.len() {
+ if let Some(verb_key) = self.verbs[i].key {
+ if verb_key == *key {
+ return Some(i);
+ }
+ }
+ }
+ None
+ }
}