diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/browser_states.rs | 18 | ||||
-rw-r--r-- | src/browser_verbs.rs | 51 | ||||
-rw-r--r-- | src/commands.rs | 42 | ||||
-rw-r--r-- | src/conf.rs | 9 | ||||
-rw-r--r-- | src/help_states.rs | 12 | ||||
-rw-r--r-- | src/help_verbs.rs | 16 | ||||
-rw-r--r-- | src/verb_conf.rs | 3 | ||||
-rw-r--r-- | src/verb_store.rs | 55 |
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 + } } |