diff options
author | Canop <cano.petrole@gmail.com> | 2020-05-04 22:08:36 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2020-05-04 22:08:36 +0200 |
commit | a0ed4966a4afa9af4044f3bf303b442bfccb46fd (patch) | |
tree | 173a73135ea72a71d81e6b8f8d7ce001a5f008b3 /src | |
parent | c1dcc8124bf9ce0de8e9fbe94c02706ec4b8b108 (diff) |
internals can accept an argument. :focus does accept one
Both in input and in configuration.
This allowed to remove the :focus_user_home and :focus_root
internals.
It's now possible to define a verb like
```
[[verbs]]
key = "ctrl-h"
invocation = "home"
execution = ":focus ~/dev"
```
which can be called as ctrl-h, :home, but also :!home
(in order to open the ~/dev directory in a new panel)
Diffstat (limited to 'src')
31 files changed, 414 insertions, 315 deletions
diff --git a/src/app/state.rs b/src/app/state.rs index 346a57a..0f6b92e 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -1,12 +1,12 @@ use { super::*, crate::{ - command::Command, + command::{Command, TriggerType}, display::{Screen, W}, errors::ProgramError, selection_type::SelectionType, task_sync::Dam, - verb::{Internal, PrefixSearchResult, VerbExecution, VerbInvocation}, + verb::{InternalExecution, PrefixSearchResult, VerbExecution, VerbInvocation}, }, std::path::Path, termimad::Area, @@ -62,9 +62,9 @@ pub trait AppState { /// by a key shorctut) fn on_internal( &mut self, - internal: Internal, - bang: bool, - invocation: Option<&VerbInvocation>, + internal_exec: &InternalExecution, + input_invocation: Option<&VerbInvocation>, + trigger_type: TriggerType, screen: &mut Screen, con: &AppContext, ) -> Result<AppStateCmdResult, ProgramError>; @@ -87,13 +87,13 @@ pub trait AppState { } => { let verb = &con.verb_store.verbs[*index]; match &verb.execution { - VerbExecution::Internal { internal, bang } => { - let bang = input_invocation - .as_ref() - .map(|inv| inv.bang) - .unwrap_or(*bang); - self.on_internal(*internal, bang, input_invocation.as_ref(), screen, con) - } + VerbExecution::Internal(internal_exec) => self.on_internal( + internal_exec, + input_invocation.as_ref(), + TriggerType::Other, + screen, + con, + ), VerbExecution::External(external) => external.to_cmd_result( self.selected_path(), if let Some(inv) = &input_invocation { @@ -108,17 +108,26 @@ pub trait AppState { Command::Internal { internal, input_invocation, - } => self.on_internal(*internal, false, input_invocation.as_ref(), screen, con), + } => self.on_internal( + &InternalExecution::from_internal(*internal), + input_invocation.as_ref(), + TriggerType::Other, + screen, + con, + ), Command::VerbInvocate(invocation) => match con.verb_store.search(&invocation.name) { PrefixSearchResult::Match(verb) => { if let Some(err) = verb.check_args(invocation) { Ok(AppStateCmdResult::DisplayError(err)) } else { match &verb.execution { - VerbExecution::Internal { internal, bang } => { - let bang = invocation.bang || *bang; - self.on_internal(*internal, bang, Some(invocation), screen, con) - } + VerbExecution::Internal(internal_exec) => self.on_internal( + internal_exec, + Some(invocation), + TriggerType::Input, + screen, + con, + ), VerbExecution::External(external) => { external.to_cmd_result(self.selected_path(), &invocation.args, con) } @@ -130,31 +139,7 @@ pub trait AppState { Command::None | Command::VerbEdit(_) => { // we do nothing here, the real job is done in get_status Ok(AppStateCmdResult::Keep) - } // Command::ArgTab(verb_invocation) => { - // match con.verb_store.search(&verb_invocation.name) { - // PrefixSearchResult::Match(verb) => { - // let arg_selection_type = verb.get_arg_selection_type(); - // if let Some(sel_type) = arg_selection_type { - // // this verb expects a path, so tab makes sense - // if let Some(arg) = verb_invocation.arg { - // // the user started typing an arg - - // } else { - - // } - // } else { - // // we don't know what to do with a tab here, we - // // do nothing - // Ok(AppStateCmdResult::DisplayError( - // "no tab completion available for this verb" - // )) - // } - - // Ok(AppStateCmdResult::Keep) - // } - // _ => Ok(AppStateCmdResult::verb_not_found(&verb_invocation.name)), - // } - // } + } } } diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs index 0974f49..4c54d61 100644 --- a/src/browser/browser_state.rs +++ b/src/browser/browser_state.rs @@ -1,22 +1,21 @@ use { crate::{ app::{AppContext, AppState, AppStateCmdResult, Status}, - command::Command, + command::{Command, TriggerType}, display::{DisplayableTree, Screen, FLAGS_AREA_WIDTH, W}, errors::{ProgramError, TreeBuildError}, - tree::{TreeLineType, Tree, TreeOptions}, git, help::HelpState, launchable::Launchable, - pattern::Pattern, path, + pattern::Pattern, print, selection_type::SelectionType, task_sync::Dam, + tree::*, tree_build::TreeBuilder, - verb::{Internal, PrefixSearchResult, VerbInvocation, CD}, + verb::*, }, - directories::UserDirs, open, std::{ fs::OpenOptions, @@ -26,19 +25,6 @@ use { termimad::Area, }; -fn focus_path( - path: PathBuf, - screen: &mut Screen, - tree: &Tree, - in_new_panel: bool, -) -> AppStateCmdResult { - let path = path::closest_dir(&path); - AppStateCmdResult::from_optional_state( - BrowserState::new(path, tree.options.clone(), screen, &Dam::unlimited()), - in_new_panel, - ) -} - /// An application state dedicated to displaying a tree. /// It's the first and main screen of broot. pub struct BrowserState { @@ -367,16 +353,19 @@ impl AppState for BrowserState { fn on_internal( &mut self, - internal: Internal, - bang: bool, + internal_exec: &InternalExecution, input_invocation: Option<&VerbInvocation>, + trigger_type: TriggerType, screen: &mut Screen, con: &AppContext, ) -> Result<AppStateCmdResult, ProgramError> { let page_height = BrowserState::page_height(screen); + let bang = input_invocation + .map(|inv| inv.bang) + .unwrap_or(internal_exec.bang); use Internal::*; - Ok(match internal { - back => { + Ok(match internal_exec.internal { + Internal::back => { if self.filtered_tree.is_some() { self.filtered_tree = None; AppStateCmdResult::Keep @@ -387,119 +376,101 @@ impl AppState for BrowserState { AppStateCmdResult::PopState } } - close_panel => AppStateCmdResult::PopPanel, - complete => AppStateCmdResult::DisplayError("not yet implemented".to_string()), - focus => { - if let Some(invocation) = input_invocation { - if let Some(arg) = &invocation.args { - if invocation.name == internal.name() { - // TODO the name test is a hack, we should - // use the trigger type of the command - debug!("case A"); - let tree = self.displayed_tree(); - let base_dir = tree.selected_line().path.to_string_lossy(); - let path = path::path_from(&base_dir, &arg); - let path = PathBuf::from(path); - focus_path(path, screen, tree, bang) - } else { - // user would like to open for arg edition - // the current arg as a tree panel - // TODO add purpose - debug!("case B"); - AppStateCmdResult::DisplayError("arg panel not yet implemented".to_string()) - } - } else { - // user wants to open for arg edition the selected - // tree line - // TODO add purpose - debug!("case C"); - let tree = self.displayed_tree(); - let path = tree.selected_line().target(); - focus_path(path, screen, tree, bang) - } - } else { - // just opening a new panel on the selected tree line (without purpose) - debug!("case D"); - let tree = self.displayed_tree(); - let path = tree.selected_line().target(); - focus_path(path, screen, tree, bang) - } + Internal::close_panel => AppStateCmdResult::PopPanel, + Internal::complete => { + AppStateCmdResult::DisplayError("not yet implemented".to_string()) } - focus_root => focus_path(PathBuf::from("/"), screen, self.displayed_tree(), bang), - up_tree => match self.displayed_tree().root().parent() { - Some(path) => focus_path(path.to_path_buf(), screen, self.displayed_tree(), bang), - None => AppStateCmdResult::DisplayError("no parent found".to_string()), - }, - focus_user_home => match UserDirs::new() { - Some(ud) => focus_path( - ud.home_dir().to_path_buf(), + Internal::focus => internal_focus::on_internal( + internal_exec, + input_invocation, + trigger_type, + self.selected_path(), + screen, + con, + self.displayed_tree().options.clone(), + ), + Internal::up_tree => match self.displayed_tree().root().parent() { + Some(path) => internal_focus::on_path( + path.to_path_buf(), screen, - self.displayed_tree(), + self.displayed_tree().options.clone(), bang, ), - None => AppStateCmdResult::DisplayError("no user home directory found".to_string()), + None => AppStateCmdResult::DisplayError("no parent found".to_string()), }, - help => AppStateCmdResult::NewState { + Internal::help => AppStateCmdResult::NewState { state: Box::new(HelpState::new(screen, con)), in_new_panel: bang, }, - open_stay => self.open_selection_stay_in_broot(screen, con, bang)?, - open_leave => self.open_selection_quit_broot(con)?, - line_down => { + Internal::open_stay => self.open_selection_stay_in_broot(screen, con, bang)?, + Internal::open_leave => self.open_selection_quit_broot(con)?, + Internal::line_down => { self.displayed_tree_mut().move_selection(1, page_height); AppStateCmdResult::Keep } - line_up => { + Internal::line_up => { self.displayed_tree_mut().move_selection(-1, page_height); AppStateCmdResult::Keep } - page_down => { + Internal::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 => { + Internal::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 => self.go_to_parent(screen, bang), - print_path => print::print_path(&self.displayed_tree().selected_line().target(), con)?, - print_relative_path => { + Internal::parent => self.go_to_parent(screen, bang), + Internal::print_path => { + print::print_path(&self.displayed_tree().selected_line().target(), con)? + } + Internal::print_relative_path => { print::print_relative_path(&self.displayed_tree().selected_line().target(), con)? } - print_tree => print::print_tree(&self.displayed_tree(), screen, con)?, - refresh => AppStateCmdResult::RefreshState { clear_cache: true }, - select_first => { + Internal::print_tree => print::print_tree(&self.displayed_tree(), screen, con)?, + Internal::refresh => AppStateCmdResult::RefreshState { clear_cache: true }, + Internal::select_first => { self.displayed_tree_mut().try_select_first(); AppStateCmdResult::Keep } - select_last => { + Internal::select_last => { self.displayed_tree_mut().try_select_last(); AppStateCmdResult::Keep } - toggle_dates => self.with_new_options(screen, &|o| o.show_dates ^= true, bang), - toggle_files => { + Internal::toggle_dates => { + self.with_new_options(screen, &|o| o.show_dates ^= true, bang) + } + Internal::toggle_files => { self.with_new_options(screen, &|o: &mut TreeOptions| o.only_folders ^= true, bang) } - toggle_hidden => self.with_new_options(screen, &|o| o.show_hidden ^= true, bang), - toggle_git_ignore => { + Internal::toggle_hidden => { + self.with_new_options(screen, &|o| o.show_hidden ^= true, bang) + } + Internal::toggle_git_ignore => { self.with_new_options(screen, &|o| o.respect_git_ignore ^= true, bang) } - toggle_git_file_info => { + Internal::toggle_git_file_info => { self.with_new_options(screen, &|o| o.show_git_file_info ^= true, bang) } - toggle_git_status => { + Internal::toggle_git_status => { self.with_new_options(screen, &|o| o.filter_by_git_status ^= true, bang) } - toggle_perm => self.with_new_options(screen, &|o| o.show_permissions ^= true, bang), - toggle_sizes => self.with_new_options(screen, &|o| o.show_sizes ^= true, bang), - toggle_trim_root => self.with_new_options(screen, &|o| o.trim_root ^= true, bang), - total_search => { + Internal::toggle_perm => { + self.with_new_options(screen, &|o| o.show_permissions ^= true, bang) + } + Internal::toggle_sizes => { + self.with_new_options(screen, &|o| o.show_sizes ^= true, bang) + } + Internal::toggle_trim_root => { + self.with_new_options(screen, &|o| o.trim_root ^= true, bang) + } + Internal::total_search => { if let Some(tree) = &self.filtered_tree { if tree.total_search { AppStateCmdResult::DisplayError( @@ -516,7 +487,7 @@ impl AppState for BrowserState { ) } } - quit => AppStateCmdResult::Quit, + Internal::quit => AppStateCmdResult::Quit, }) } diff --git a/src/command/event.rs b/src/command/event.rs index b004fb7..8f9720d 100644 --- a/src/command/event.rs +++ b/src/command/event.rs @@ -62,10 +62,7 @@ pub fn to_command( input_invocation: parts.verb_invocation, }; } else { - debug!( - "verb {} not allowed on current selection", - &con.verb_store.verbs[index].name - ); + debug!("verb not allowed on current selection"); } } diff --git a/src/conf.rs b/src/conf.rs index 99278fa..41d2c81 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -104,7 +104,7 @@ impl Conf { // reading verbs if let Some(Value::Array(verbs_value)) = &root.get("verbs") { for verb_value in verbs_value.iter() { - let invocation = string_field(verb_value, "invocation").unwrap_or("".to_string()); + let invocation = string_field(verb_value, "invocation"); let key = string_field(verb_value, "key") .map(|s| keys::parse_key(&s)) .transpose()?; diff --git a/src/display/displayable_tree.rs b/src/display/displayable_tree.rs index 967cea6..eb08ada 100644 --- a/src/display/displayable_tree.rs +++ b/src/display/displayable_tree.rs @@ -3,10 +3,10 @@ use { crate::{ errors::ProgramError, file_sizes::FileSize, - tree::{TreeLineType, Tree, TreeLine}, pattern::Pattern, skin::Skin, task_sync::ComputationResult, + tree::{Tree, TreeLine, TreeLineType}, }, chrono::{offset::Local, DateTime}, crossterm::{ diff --git a/src/errors.rs b/src/errors.rs index ca51dbb..5176c4f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -31,6 +31,7 @@ custom_error! {pub ConfError UnknownInternal {verb: String} = "not a known internal: {}", InvalidKey {raw: String} = "not a valid key: {}", ReservedKey {key: String} = "reserved key: {}", + UnexpectedInternalArg { invocation: String} = "unexpected argument for internal: {}", } // error which can be raised when parsing a regex the diff --git a/src/help/help_content.rs b/src/help/help_content.rs index 064b0ca..aa16a9b 100644 --- a/src/help/help_content.rs +++ b/src/help/help_content.rs @@ -1,5 +1,5 @@ use { - crate::{app::AppContext, verb::VerbExecution}, + crate::app::AppContext, minimad::{Text, TextTemplate}, }; @@ -62,30 +62,29 @@ pub fn build_text(con: &AppContext) -> Text<'_> { for verb in &con.verb_store.verbs { let sub = expander .sub("verb-rows") - .set("name", &verb.name) + .set( + "name", + if let Some(name) = verb.names.get(0) { + &name + } else { + "" + }, + ) .set( "shortcut", - if let Some(sk) = &verb.shortcut { - &sk + if let Some(shortcut) = verb.names.get(1) { + &shortcut } else { "" - }, // TODO use as_deref when it's available + }, ) .set("key", &verb.keys_desc); - if let Some(description) = &verb.description { - sub.set_md("description", description); - sub.set("execution", ""); + if verb.description.code { + sub.set("description", ""); + sub.set("execution", &verb.description.content); } else { - match &verb.execution { - VerbExecution::Internal { internal, .. } => { - sub.set_md("description", internal.description()); - sub.set("execution", ""); - } - VerbExecution::External(external) => { - sub.set("description", ""); - sub.set("execution", &external.exec_pattern); // we should maybe also show the invoc pattern - } - } + sub.set("description", &verb.description.content); + sub.set("execution", ""); } } expander.expand() diff --git a/src/help/help_state.rs b/src/help/help_state.rs index 62bf15a..ee2aea2 100644 --- a/src/help/help_state.rs +++ b/src/help/help_state.rs @@ -3,7 +3,7 @@ use { crate::{ app::{AppContext, AppState, AppStateCmdResult, Status}, browser::BrowserState, - command::Command, + command::{Command, TriggerType}, conf::{self, Conf}, display::{Screen, W}, errors::ProgramError, @@ -12,7 +12,7 @@ use { selection_type::SelectionType, task_sync::Dam, tree::TreeOptions, - verb::{Internal, PrefixSearchResult, VerbInvocation}, + verb::*, }, crossterm::{ terminal::{Clear, ClearType}, @@ -133,14 +133,17 @@ impl AppState for HelpState { fn on_internal( &mut self, - internal: Internal, - bang: bool, - _invocation: Option<&VerbInvocation>, + internal_exec: &InternalExecution, + input_invocation: Option<&VerbInvocation>, + trigger_type: TriggerType, screen: &mut Screen, con: &AppContext, ) -> Result<AppStateCmdResult, ProgramError> { use Internal::*; - Ok(match internal { + let bang = input_invocation + .map(|inv| inv.bang) + .unwrap_or(internal_exec.bang); + Ok(match internal_exec.internal { back => AppStateCmdResult::PopState, focus | parent => AppStateCmdResult::from_optional_state( BrowserState::new( @@ -168,7 +171,9 @@ impl AppState for HelpState { Err(e) => AppStateCmdResult::DisplayError(format!("{:?}", e)), }, open_leave => { - AppStateCmdResult::from(Launchable::opener(Conf::default_location().to_path_buf())) + AppStateCmdResult::from(Launchable::opener( + Conf::default_location().to_path_buf() + )) } page_down => { self.scroll += self.text_area.height as i32; @@ -181,9 +186,9 @@ impl AppState for HelpState { print_path => print::print_path(&Conf::default_location(), con)?, print_relative_path => print::print_relative_path(&Conf::default_location(), con)?, quit => AppStateCmdResult::Quit, - focus_user_home | focus_root | toggle_dates | toggle_files | toggle_hidden - | toggle_git_ignore | toggle_git_file_info | toggle_git_status | toggle_perm - | toggle_sizes | toggle_trim_root => AppStateCmdResult::PopStateAndReapply, + toggle_dates | toggle_files | toggle_hidden | toggle_git_ignore + | toggle_git_file_info | toggle_git_status | toggle_perm | toggle_sizes + | toggle_trim_root => AppStateCmdResult::PopStateAndReapply, _ => AppStateCmdResult::Keep, }) } diff --git a/src/launchable.rs b/src/launchable.rs index b8880e1..08a890e 100644 --- a/src/launchable.rs +++ b/src/launchable.rs @@ -2,8 +2,8 @@ use { crate::{ display::{DisplayableTree, Screen}, errors::ProgramError, - tree::Tree, skin::Skin, + tree::Tree, }, open, std::{env, io, path::PathBuf, process::Command}, @@ -37,6 +37,6 @@ pub mod selection_type; pub mod shell_install; pub mod skin; pub mod task_sync; -pub mod tree_build; pub mod tree; +pub mod tree_build; pub mod verb; diff --git a/src/path.rs b/src/path.rs index 518e00f..4ef1eaa 100644 --- a/src/path.rs +++ b/src/path.rs @@ -3,10 +3,7 @@ use { regex::{self, Captures, Regex}, std::{ collections::HashMap, - path::{ - Path, - PathBuf, - }, + path::{Path, PathBuf}, }, }; @@ -46,9 +43,7 @@ pub fn closest_dir(mut path: &Path) -> PathBuf { return path.to_path_buf(); } match path.parent() { - Some(parent) => { - path = parent - } + Some(parent) => path = parent, None => { debug!("no existing parent"); // unexpected return path.to_path_buf(); diff --git a/src/print.rs b/src/print.rs index 106d922..2919c76 100644 --- a/src/print.rs +++ b/src/print.rs @@ -5,9 +5,9 @@ use { app::{AppContext, AppStateCmdResult}, display::{DisplayableTree, Screen}, errors::ProgramError, - tree::Tree, launchable::Launchable, skin::Skin, + tree::Tree, }, pathdiff, std::{ diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 0560579..7b0e4f0 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,12 +1,8 @@ - mod tree; mod tree_line; mod tree_line_type; mod tree_options; pub use { - tree::Tree, - tree_line::TreeLine, - tree_line_type::TreeLineType, - tree_options::TreeOptions, + tree::Tree, tree_line::TreeLine, tree_line_type::TreeLineType, tree_options::TreeOptions, }; diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 0a07282..c8e8355 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -1,20 +1,16 @@ //! In the tree structure, every "node" is just a line, there's //! no link from a child to its parent or from a parent to its children. use { + super::*, crate::{ - errors, - file_sizes::FileSize, - git::TreeGitStatus, - task_sync::ComputationResult, - task_sync::Dam, - tree_build::TreeBuilder, + errors, file_sizes::FileSize, git::TreeGitStatus, task_sync::ComputationResult, + task_sync::Dam, tree_build::TreeBuilder, }, std::{ cmp::Ord, mem, path::{Path, PathBuf}, }, - super::*, }; #[cfg(windows)] diff --git a/src/tree/tree_line.rs b/src/tree/tree_line.rs index 7a81829..30d7639 100644 --- a/src/tree/tree_line.rs +++ b/src/tree/tree_line.rs @@ -1,25 +1,18 @@ //! In the tree structure, every "node" is just a line, there's //! no link from a child to its parent or from a parent to its children. use { - crate::{ - file_sizes::FileSize, - git::LineGitStatus, - selection_type::SelectionType, - }, |