summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2020-06-12 21:51:00 +0200
committerCanop <cano.petrole@gmail.com>2020-06-12 21:51:44 +0200
commit32122537cf35473bfe884e20a9ba4d72dbeccac2 (patch)
tree2d8a4c162d2803ecdbfe7f85e26d605cc88ef486
parentaaa8e78a5afecc6765a7299804ede6941102ec11 (diff)
Fix some problems with relative paths in cp and mvv0.15.1
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/browser/browser_state.rs3
-rw-r--r--src/command/completion.rs13
-rw-r--r--src/lib.rs1
-rw-r--r--src/path.rs12
-rw-r--r--src/path_anchor.rs9
-rw-r--r--src/selection_type.rs6
-rw-r--r--src/verb/builtin.rs8
-rw-r--r--src/verb/external_execution.rs11
-rw-r--r--src/verb/internal_focus.rs7
-rw-r--r--src/verb/verb.rs10
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
diff --git a/Cargo.lock b/Cargo.lock
index 2a68617..233acb8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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)",
diff --git a/Cargo.toml b/Cargo.toml
index dba8281..3464d46 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/src/lib.rs b/src/lib.rs
index 5b601fd..889c4cf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
+ }
+ }
}