diff options
author | Canop <cano.petrole@gmail.com> | 2021-06-10 22:35:31 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2021-06-10 22:35:31 +0200 |
commit | 8b7bbf146c96de074c242abfdad40d21833d8a5a (patch) | |
tree | b3d93918cbb9b21eea3f59a45072fd9b796e0714 /src | |
parent | 6ea38c86908ca169f871ed6f5971e9fa44b84e7a (diff) |
`working_dir` verb attribute
It makes it possible to set the working dir of a new executable to
something different than the current directory. For example
working_dir: "{directory}/.git"
Fix #396
Diffstat (limited to 'src')
-rw-r--r-- | src/conf/verb_conf.rs | 9 | ||||
-rw-r--r-- | src/launchable.rs | 1 | ||||
-rw-r--r-- | src/verb/exec_pattern.rs | 1 | ||||
-rw-r--r-- | src/verb/execution_builder.rs | 12 | ||||
-rw-r--r-- | src/verb/external_execution.rs | 51 |
5 files changed, 50 insertions, 24 deletions
diff --git a/src/conf/verb_conf.rs b/src/conf/verb_conf.rs index bcbde54..5923b9b 100644 --- a/src/conf/verb_conf.rs +++ b/src/conf/verb_conf.rs @@ -41,6 +41,8 @@ pub struct VerbConf { set_working_dir: Option<bool>, + working_dir: Option<String>, + description: Option<String>, auto_exec: Option<bool>, @@ -64,11 +66,16 @@ impl TryFrom<&VerbConf> for Verb { let cmd_separator = vc.cmd_separator.as_ref().filter(|i| !i.is_empty()); let execution = vc.execution.as_ref().filter(|i| !i.is_empty()); let make_external_execution = |s| { + let working_dir = match (vc.set_working_dir, &vc.working_dir) { + (Some(false), _) => None, + (_, Some(s)) => Some(s.clone()), + (_, None) => Some("{directory}".to_owned()), + }; ExternalExecution::new( s, ExternalExecutionMode::from_conf(vc.from_shell, vc.leave_broot), ) - .with_set_working_dir(vc.set_working_dir) + .with_working_dir(working_dir) }; let execution = match (execution, internal, external, cmd) { // old definition with "execution": we guess whether it's an internal or diff --git a/src/launchable.rs b/src/launchable.rs index b26e59f..3666fa5 100644 --- a/src/launchable.rs +++ b/src/launchable.rs @@ -135,6 +135,7 @@ impl Launchable { args, mouse_capture_disabled, } => { + debug!("working_dir: {:?}", &working_dir); // we restore the normal terminal in case the executable // is a terminal application, and we'll switch back to // broot's alternate terminal when we're back to broot diff --git a/src/verb/exec_pattern.rs b/src/verb/exec_pattern.rs index d320958..addf0b1 100644 --- a/src/verb/exec_pattern.rs +++ b/src/verb/exec_pattern.rs @@ -9,6 +9,7 @@ use { }, }; +/// A pattern which can be expanded into an executable #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] pub enum ExecPattern { diff --git a/src/verb/execution_builder.rs b/src/verb/execution_builder.rs index e8e5b8a..4b2f61b 100644 --- a/src/verb/execution_builder.rs +++ b/src/verb/execution_builder.rs @@ -213,6 +213,18 @@ impl<'b> ExecutionStringBuilder<'b> { } } + /// build a path + pub fn path( + &self, + pattern: &str, + ) -> PathBuf { + PathBuf::from( + GROUP.replace_all( + pattern, + |ec: &Captures<'_>| self.get_capture_replacement(ec), + ).to_string() + ) + } /// build a shell compatible command, with escapings pub fn shell_exec_string( &self, diff --git a/src/verb/external_execution.rs b/src/verb/external_execution.rs index d20b197..af4a245 100644 --- a/src/verb/external_execution.rs +++ b/src/verb/external_execution.rs @@ -5,15 +5,14 @@ use { display::W, errors::ProgramError, launchable::Launchable, - path, }, std::{ fs::OpenOptions, io::Write, + path::PathBuf, }, }; - /// Definition of how the user input should be interpreted /// to be executed in an external command. #[derive(Debug, Clone)] @@ -34,9 +33,9 @@ pub struct ExternalExecution { /// how the external process must be launched pub exec_mode: ExternalExecutionMode, - /// whether the working dir of the external process must be set - /// to the current directory - pub set_working_dir: bool, + /// the working directory of the new process, or none if we don't + /// want to set it + pub working_dir: Option<String>, } impl ExternalExecution { @@ -47,14 +46,12 @@ impl ExternalExecution { Self { exec_pattern, exec_mode, - set_working_dir: false, + working_dir: None, } } - pub fn with_set_working_dir(mut self, b: Option<bool>) -> Self { - if let Some(b) = b { - self.set_working_dir = b; - } + pub fn with_working_dir(mut self, b: Option<String>) -> Self { + self.working_dir = b; self } @@ -84,6 +81,23 @@ impl ExternalExecution { } } + fn working_dir_path( + &self, + builder: &ExecutionStringBuilder<'_>, + ) -> Option<PathBuf> { + self.working_dir + .as_ref() + .map(|pattern| builder.path(pattern)) + .filter(|pb| { + if pb.exists() { + true + } else { + warn!("workding dir doesn't exist: {:?}", pb); + false + } + }) + } + /// build the cmd result as an executable which will be called /// from the parent shell (meaning broot must quit) fn cmd_result_exec_from_parent_shell( @@ -134,10 +148,7 @@ impl ExternalExecution { } let launchable = Launchable::program( builder.exec_token(&self.exec_pattern), - builder.sel_info - .one_sel() - .filter(|_| self.set_working_dir) - .map(|sel| path::closest_dir(sel.path)), + self.working_dir_path(&builder), con, )?; Ok(CmdResult::from(launchable)) @@ -151,15 +162,13 @@ impl ExternalExecution { builder: ExecutionStringBuilder<'_>, con: &AppContext, ) -> Result<CmdResult, ProgramError> { + let working_dir_path = self.working_dir_path(&builder); match &builder.sel_info { SelInfo::None | SelInfo::One(_) => { // zero or one selection -> only one execution let launchable = Launchable::program( builder.exec_token(&self.exec_pattern), - builder.sel_info - .one_sel() - .filter(|_| self.set_working_dir) - .map(|sel| path::closest_dir(sel.path)), + working_dir_path, con, )?; info!("Executing not leaving, launchable {:?}", launchable); @@ -180,11 +189,7 @@ impl ExternalExecution { for sel in sels { let launchable = Launchable::program( builder.sel_exec_token(&self.exec_pattern, Some(sel)), - if self.set_working_dir { - Some(path::closest_dir(sel.path)) - } else { - None - }, + working_dir_path.clone(), con, )?; if let Err(e) = launchable.execute(Some(w)) { |