diff options
author | Canop <cano.petrole@gmail.com> | 2020-06-12 21:51:00 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2020-06-12 21:51:44 +0200 |
commit | 32122537cf35473bfe884e20a9ba4d72dbeccac2 (patch) | |
tree | 2d8a4c162d2803ecdbfe7f85e26d605cc88ef486 | |
parent | aaa8e78a5afecc6765a7299804ede6941102ec11 (diff) |
Fix some problems with relative paths in cp and mvv0.15.1
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/browser/browser_state.rs | 3 | ||||
-rw-r--r-- | src/command/completion.rs | 13 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/path.rs | 12 | ||||
-rw-r--r-- | src/path_anchor.rs | 9 | ||||
-rw-r--r-- | src/selection_type.rs | 6 | ||||
-rw-r--r-- | src/verb/builtin.rs | 8 | ||||
-rw-r--r-- | src/verb/external_execution.rs | 11 | ||||
-rw-r--r-- | src/verb/internal_focus.rs | 7 | ||||
-rw-r--r-- | src/verb/verb.rs | 10 |
13 files changed, 65 insertions, 23 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fe521a7..60a0428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +<a name="v0.15.1"></a> +### v0.15.1 - 2020-06-12 +- fix some problems related to relative paths in built in cp and mv + <a name="v0.15.0"></a> ### v0.15.0 - 2020-06-12 #### Major feature: new input syntax - Breaking Change @@ -93,7 +93,7 @@ dependencies = [ [[package]] name = "broot" -version = "0.15.0" +version = "0.15.1" dependencies = [ "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1,6 +1,6 @@ [package] name = "broot" -version = "0.15.0" +version = "0.15.1" authors = ["dystroy <denys.seguret@gmail.com>"] repository = "https://github.com/Canop/broot" documentation = "https://dystroy.org/broot" diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs index e57d905..907f80c 100644 --- a/src/browser/browser_state.rs +++ b/src/browser/browser_state.rs @@ -10,6 +10,7 @@ use { launchable::Launchable, pattern::Pattern, path, + path_anchor::PathAnchor, print, selection_type::SelectionType, skin::PanelSkin, @@ -524,7 +525,7 @@ impl AppState for BrowserState { if let Some(input_invocation) = input_invocation { // we'll go for input arg editing let path = if let Some(input_arg) = &input_invocation.args { - path::path_from(self.root(), input_arg) + path::path_from(self.root(), PathAnchor::Unspecified, input_arg) } else { self.root().to_path_buf() }; diff --git a/src/command/completion.rs b/src/command/completion.rs index c2491e1..0486c4c 100644 --- a/src/command/completion.rs +++ b/src/command/completion.rs @@ -7,6 +7,7 @@ use { AppState, }, path, + path_anchor::PathAnchor, verb::PrefixSearchResult, }, std::io, @@ -101,6 +102,7 @@ impl Completions { } fn for_path( + anchor: PathAnchor, arg: &str, _con: &AppContext, state: &dyn AppState, @@ -108,7 +110,7 @@ impl Completions { let c = regex!(r"^(.*?)([^/]*)$").captures(arg).unwrap(); let parent_part = &c[1]; let child_part = &c[2]; - let parent = path::path_from(state.selected_path(), parent_part); + let parent = path::path_from(state.selected_path(), anchor, parent_part); if !parent.exists() { debug!("no path completion possible because {:?} doesn't exist", &parent); return Ok(Self::None); @@ -133,6 +135,7 @@ impl Completions { } fn for_arg( + verb_name: &str, arg: &str, con: &AppContext, state: &dyn AppState, @@ -143,7 +146,11 @@ impl Completions { if arg.contains(' ') { Self::None } else { - match Self::for_path(arg, con, state) { + let anchor = match con.verb_store.search(verb_name) { + PrefixSearchResult::Match(_, verb) => verb.get_arg_anchor(), + _ => PathAnchor::Unspecified, + }; + match Self::for_path(anchor, arg, con, state) { Ok(c) => c, Err(e) => { warn!("Error while trying to complete path: {:?}", e); @@ -167,7 +174,7 @@ impl Completions { } Some(args) if !args.is_empty() => { // looking into arg completion - Self::for_arg(args, con, state) + Self::for_arg(&invocation.name, args, con, state) } _ => { // nothing possible @@ -34,6 +34,7 @@ pub mod help; pub mod keys; pub mod launchable; pub mod path; +pub mod path_anchor; pub mod pattern; pub mod permissions; pub mod print; diff --git a/src/path.rs b/src/path.rs index 1bfac83..e1ec981 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,4 +1,7 @@ use { + crate::{ + path_anchor::PathAnchor, + }, directories::UserDirs, regex::{self, Captures}, std::{ @@ -11,7 +14,7 @@ use { /// (if it starts with / or ~) or relative to the supplied base_dir. /// (we might want to try detect windows drives in the future, too) /// -pub fn path_from<P: AsRef<Path>>(base_dir: P, input: &str) -> PathBuf { +pub fn path_from<P: AsRef<Path>>(base_dir: P, anchor: PathAnchor, input: &str) -> PathBuf { let tilde = regex!(r"^~(/|$)"); if input.starts_with('/') { // if the input starts with a `/`, we use it as is @@ -34,13 +37,16 @@ pub fn path_from<P: AsRef<Path>>(base_dir: P, input: &str) -> PathBuf { // we put the input behind the source (the selected directory // or its parent) and we normalize so that the user can type // paths with `../` - let base_dir = closest_dir(base_dir.as_ref()); + let base_dir = match anchor { + PathAnchor::Parent => base_dir.as_ref().parent().unwrap_or(base_dir.as_ref()).to_path_buf(), + _ => closest_dir(base_dir.as_ref()), + }; normalize_path(base_dir.join(input)) } } pub fn path_str_from<P: AsRef<Path>>(base_dir: P, input: &str) -> String { - path_from(base_dir, input).to_string_lossy().to_string() + path_from(base_dir, PathAnchor::Unspecified, input).to_string_lossy().to_string() } /// return the closest enclosing directory diff --git a/src/path_anchor.rs b/src/path_anchor.rs new file mode 100644 index 0000000..66e33a3 --- /dev/null +++ b/src/path_anchor.rs @@ -0,0 +1,9 @@ + + +#[derive(Debug, Clone, Copy)] +pub enum PathAnchor { + Unspecified, + Parent, + Directory, +} + diff --git a/src/selection_type.rs b/src/selection_type.rs index e8341f4..be2012c 100644 --- a/src/selection_type.rs +++ b/src/selection_type.rs @@ -8,11 +8,5 @@ pub enum SelectionType { impl SelectionType { pub fn respects(self, constraint: Self) -> bool { constraint == Self::Any || self == constraint - //use SelectionType::*; - //match (self, constraint) { - // (_, Undefined) => true, // no constraint - // (File, File) => true, - // (Directory, Directory) => true - //} } } diff --git a/src/verb/builtin.rs b/src/verb/builtin.rs index fbfd66a..e4ff19a 100644 --- a/src/verb/builtin.rs +++ b/src/verb/builtin.rs @@ -19,8 +19,8 @@ pub fn builtin_verbs() -> Vec<Verb> { .with_key(BACK_TAB) .with_control_key('w'), Verb::external( - "copy {newpath}", - "/bin/cp -r {file} {newpath:path-from-directory}", + "copy {newpath:path-from-parent}", + "/bin/cp -r {file} {newpath:path-from-parent}", StayInBroot, ) .unwrap() @@ -44,8 +44,8 @@ pub fn builtin_verbs() -> Vec<Verb> { .unwrap() .with_shortcut("md"), Verb::external( - "move {newpath}", - "/bin/mv {file} {newpath:path-from-directory}", + "move {newpath:path-from-parent}", + "/bin/mv {file} {newpath:path-from-parent}", StayInBroot, ) .unwrap() diff --git a/src/verb/external_execution.rs b/src/verb/external_execution.rs index dc7ef40..b37e4b2 100644 --- a/src/verb/external_execution.rs +++ b/src/verb/external_execution.rs @@ -13,6 +13,7 @@ use { errors::{ConfError, ProgramError}, launchable::Launchable, path, + path_anchor::PathAnchor, selection_type::SelectionType, }, regex::{Captures, Regex}, @@ -58,6 +59,8 @@ pub struct ExternalExecution { /// to select the argument in another panel) pub arg_selection_type: Option<SelectionType>, + pub arg_anchor: PathAnchor, + // /// whether we need to have a secondary panel for execution // /// (which is the case when an invocation has {other-panel-file}) pub need_another_panel: bool, @@ -72,6 +75,7 @@ impl ExternalExecution { let invocation_pattern = VerbInvocation::from(invocation_str); let mut args_parser = None; let mut arg_selection_type = None; + let mut arg_anchor = PathAnchor::Unspecified; let mut need_another_panel = false; if let Some(args) = &invocation_pattern.args { let spec = GROUP.replace_all(args, r"(?P<$1>.+)"); @@ -86,6 +90,12 @@ impl ExternalExecution { if group.start() == 0 && group.end() == args.len() { // there's one group, covering the whole args arg_selection_type = Some(SelectionType::Any); + let group_str = group.as_str(); + if group_str.ends_with("path-from-parent}") { + arg_anchor = PathAnchor::Parent; + } else if group_str.ends_with("path-from-directory}") { + arg_anchor = PathAnchor::Directory; + } } } } @@ -100,6 +110,7 @@ impl ExternalExecution { exec_pattern: execution_str.to_string(), exec_mode, arg_selection_type, + arg_anchor, need_another_panel, }) } diff --git a/src/verb/internal_focus.rs b/src/verb/internal_focus.rs index b0f8426..19aab74 100644 --- a/src/verb/internal_focus.rs +++ b/src/verb/internal_focus.rs @@ -6,6 +6,7 @@ use { command::TriggerType, display::Screen, path, + path_anchor::PathAnchor, selection_type::SelectionType, task_sync::Dam, tree::TreeOptions, @@ -79,7 +80,7 @@ pub fn on_internal( // `:focus some/path`). // The given path may be relative hence the need for the // state's selection - let path = path::path_from(selected_path, arg); + let path = path::path_from(selected_path, PathAnchor::Unspecified, arg); let bang = input_invocation .map(|inv| inv.bang) .unwrap_or(internal_exec.bang); @@ -93,7 +94,7 @@ pub fn on_internal( // the :focus internal execution was triggered from the // input (which must be a kind of alias for :focus) // so we do exactly what the input asks for - let path = path::path_from(selected_path, input_arg); + let path = path::path_from(selected_path, PathAnchor::Unspecified, input_arg); let bang = input_invocation.bang || internal_exec.bang; return on_path(path, screen, tree_options, bang, con); } @@ -102,7 +103,7 @@ pub fn on_internal( // means the user wants to explore the arg with purpose // of selecting a path let base_dir = selected_path.to_string_lossy(); - let path = path::path_from(&*base_dir, input_arg); + let path = path::path_from(&*base_dir, PathAnchor::Unspecified, input_arg); let arg_type = SelectionType::Any; // We might do better later let purpose = PanelPurpose::ArgEdition { arg_type }; return new_panel_on_path(path, screen, tree_options, purpose, con, HDir::Right); diff --git a/src/verb/verb.rs b/src/verb/verb.rs index 2a5e66d..086c395 100644 --- a/src/verb/verb.rs +++ b/src/verb/verb.rs @@ -5,6 +5,7 @@ use { errors::ConfError, keys, path, + path_anchor::PathAnchor, selection_type::SelectionType, }, crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, @@ -161,7 +162,7 @@ impl Verb { let pb; let arg = invocation.args.as_ref().or_else(|| internal_exec.arg.as_ref()); let arg_path = if let Some(arg) = arg { - pb = path::path_from(path, arg); + pb = path::path_from(path, PathAnchor::Unspecified, arg); &pb } else { path @@ -187,4 +188,11 @@ impl Verb { _ => None, } } + + pub fn get_arg_anchor(&self) -> PathAnchor { + match &self.execution { + VerbExecution::External(external) => external.arg_anchor, + _ => PathAnchor::Unspecified, + } + } } |