summaryrefslogtreecommitdiffstats
path: root/src/verb
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2021-05-29 18:50:12 +0200
committerCanop <cano.petrole@gmail.com>2021-05-29 18:50:12 +0200
commit07e95eab0ad33e035c7efe39442f18d1eff6e23c (patch)
tree58f103b4a5c3e00823b531692cc4421a176dacdd /src/verb
parent2cae879e39eee7f9caa3c0546a1114bc5ddf4c02 (diff)
:rename verb (and what makes it possible)
new auto_exec verb argument new standard verb arguments: file-name, file-stem, file-extension and file-dot-extension
Diffstat (limited to 'src/verb')
-rw-r--r--src/verb/builtin.rs7
-rw-r--r--src/verb/execution_builder.rs111
-rw-r--r--src/verb/invocation_parser.rs3
-rw-r--r--src/verb/mod.rs12
-rw-r--r--src/verb/verb.rs18
5 files changed, 122 insertions, 29 deletions
diff --git a/src/verb/builtin.rs b/src/verb/builtin.rs
index 26a12a2..8f27f2f 100644
--- a/src/verb/builtin.rs
+++ b/src/verb/builtin.rs
@@ -79,7 +79,7 @@ pub fn builtin_verbs() -> Vec<Verb> {
internal(close_panel_cancel)
.with_control_key('w'),
external(
- "copy {newpath:path-from-parent}",
+ "copy {newpath}",
"cp -r {file} {newpath:path-from-parent}",
StayInBroot,
)
@@ -122,7 +122,7 @@ pub fn builtin_verbs() -> Vec<Verb> {
)
.with_shortcut("md"),
external(
- "move {newpath:path-from-parent}",
+ "move {newpath}",
"mv {file} {newpath:path-from-parent}",
StayInBroot,
)
@@ -134,10 +134,11 @@ pub fn builtin_verbs() -> Vec<Verb> {
)
.with_shortcut("mvp"),
external(
- "rename {from-filename:new_filename}",
+ "rename {new_filename:file-name}",
"mv {file} {parent}/{new_filename}",
StayInBroot,
)
+ .with_auto_exec(false)
.with_key(F2),
internal_bang(start_end_panel)
.with_control_key('p'),
diff --git a/src/verb/execution_builder.rs b/src/verb/execution_builder.rs
index 6fc483f..e8e5b8a 100644
--- a/src/verb/execution_builder.rs
+++ b/src/verb/execution_builder.rs
@@ -49,10 +49,16 @@ impl<'b> ExecutionStringBuilder<'b> {
invocation_values,
}
}
- fn get_raw_capture_replacement(&self, ec: &Captures<'_>) -> Option<String> {
+ fn get_raw_replacement<F>(
+ &self,
+ f: F
+ ) -> Option<String>
+ where
+ F: Fn(Option<Selection<'_>>) -> Option<String>
+ {
match self.sel_info {
- SelInfo::None => self.get_raw_sel_capture_replacement(ec, None),
- SelInfo::One(sel) => self.get_raw_sel_capture_replacement(ec, Some(sel)),
+ SelInfo::None => f(None),
+ SelInfo::One(sel) => f(Some(sel)),
SelInfo::More(stage) => {
let mut sels = stage.paths().iter()
.map(|path| Selection {
@@ -61,10 +67,10 @@ impl<'b> ExecutionStringBuilder<'b> {
stype: SelectionType::from(path),
is_exe: false,
});
- self.get_raw_sel_capture_replacement(ec, sels.next())
+ f(sels.next())
.filter(|first_rcr| {
for sel in sels {
- let rcr = self.get_raw_sel_capture_replacement(ec, Some(sel));
+ let rcr = f(Some(sel));
if rcr.as_ref() != Some(first_rcr) {
return false;
}
@@ -74,18 +80,55 @@ impl<'b> ExecutionStringBuilder<'b> {
}
}
}
- fn get_raw_sel_capture_replacement(
+ fn get_raw_capture_replacement(&self, ec: &Captures<'_>) -> Option<String> {
+ self.get_raw_replacement(|sel| {
+ self.get_raw_sel_capture_replacement(ec, sel)
+ })
+ }
+ /// return the standard replacement (ie not one from the invocation)
+ fn get_raw_sel_name_standard_replacement(
&self,
- ec: &Captures<'_>,
+ name: &str,
sel: Option<Selection<'_>>,
) -> Option<String> {
- let name = ec.get(1).unwrap().as_str();
+ debug!("repl name : {:?}", name);
match name {
"line" => sel.map(|s| s.line.to_string()),
- "file" => sel.map(|s| s.path).map(path_to_string),
- "directory" => sel.map(|s| path::closest_dir(s.path)).map(path_to_string),
- "parent" => sel.and_then(|s| s.path.parent()).map(path_to_string),
- "other-panel-file" => self.other_file.map(path_to_string),
+ "file" => sel.map(|s| s.path)
+ .map(path_to_string),
+ "file-name" => sel.map(|s| s.path)
+ .and_then(|path| path.file_name())
+ .and_then(|oss| oss.to_str())
+ .map(|s| s.to_string()),
+ "file-stem" => sel.map(|s| s.path)
+ .and_then(|path| path.file_stem())
+ .and_then(|oss| oss.to_str())
+ .map(|s| s.to_string()),
+ "file-extension" => {
+ debug!("expending file extension");
+ sel.map(|s| s.path)
+ .and_then(|path| path.extension())
+ .and_then(|oss| oss.to_str())
+ .map(|s| s.to_string())
+ }
+ "file-dot-extension" => {
+ debug!("expending file dot extension");
+ sel.map(|s| s.path)
+ .and_then(|path| path.extension())
+ .and_then(|oss| oss.to_str())
+ .map(|ext| format!(".{}", ext))
+ .or_else(|| Some("".to_string()))
+ }
+ "directory" => sel.map(|s| path::closest_dir(s.path))
+ .map(path_to_string),
+ "parent" => sel.and_then(|s| s.path.parent())
+ .map(path_to_string),
+ "other-panel-file" => self.other_file
+ .map(path_to_string),
+ "other-panel-filename" => self.other_file
+ .and_then(|path| path.file_name())
+ .and_then(|oss| oss.to_str())
+ .map(|s| s.to_string()),
"other-panel-directory" => self
.other_file
.map(|p| path::closest_dir(p))
@@ -95,7 +138,17 @@ impl<'b> ExecutionStringBuilder<'b> {
.other_file
.and_then(|p| p.parent())
.map(path_to_string),
- _ => {
+ _ => None,
+ }
+ }
+ fn get_raw_sel_capture_replacement(
+ &self,
+ ec: &Captures<'_>,
+ sel: Option<Selection<'_>>,
+ ) -> Option<String> {
+ let name = ec.get(1).unwrap().as_str();
+ self.get_raw_sel_name_standard_replacement(name, sel)
+ .or_else(||{
// it's not one of the standard group names, so we'll look
// into the ones provided by the invocation pattern
self.invocation_values.as_ref()
@@ -117,8 +170,7 @@ impl<'b> ExecutionStringBuilder<'b> {
Some(value.to_string())
}
})
- }
- }
+ })
}
fn get_capture_replacement(&self, ec: &Captures<'_>) -> String {
self.get_raw_capture_replacement(ec)
@@ -132,6 +184,35 @@ impl<'b> ExecutionStringBuilder<'b> {
self.get_raw_sel_capture_replacement(ec, sel)
.unwrap_or_else(|| ec[0].to_string())
}
+ /// fills groups having a default value (after the colon)
+ ///
+ /// This is used to fill the input in case on non auto_exec
+ /// verb triggered with a key
+ pub fn invocation_with_default(
+ &self,
+ verb_invocation: &VerbInvocation,
+ ) -> VerbInvocation {
+ VerbInvocation {
+ name: verb_invocation.name.clone(),
+ args: verb_invocation.args.as_ref().map(|a| {
+ GROUP.replace_all(
+ a.as_str(),
+ |ec: &Captures<'_>| {
+ ec.get(2)
+ .map(|default_name| default_name.as_str())
+ .and_then(|default_name|
+ self.get_raw_replacement(|sel|
+ self.get_raw_sel_name_standard_replacement(default_name, sel)
+ )
+ )
+ .unwrap_or_else(|| "".to_string())
+ },
+ ).to_string()
+ }),
+ bang: verb_invocation.bang,
+ }
+ }
+
/// build a shell compatible command, with escapings
pub fn shell_exec_string(
&self,
diff --git a/src/verb/invocation_parser.rs b/src/verb/invocation_parser.rs
index dc849ef..5a5504a 100644
--- a/src/verb/invocation_parser.rs
+++ b/src/verb/invocation_parser.rs
@@ -23,6 +23,9 @@ pub struct InvocationParser {
pub invocation_pattern: VerbInvocation,
/// a regex to read the arguments in the user input
+ /// This regex declares named groups, with the name being the
+ /// name of the replacement variable (this implies that an
+ /// invocation name's characters are [_0-9a-zA-Z.\[\]])
args_parser: Option<Regex>,
/// whether the path, when non absolute, should be interpreted
diff --git a/src/verb/mod.rs b/src/verb/mod.rs
index 76d59e9..cb480bf 100644
--- a/src/verb/mod.rs
+++ b/src/verb/mod.rs
@@ -38,15 +38,11 @@ lazy_static::lazy_static! {
}
pub fn str_has_selection_group(s: &str) -> bool {
- for group in GROUP.find_iter(s) {
- if matches!(
+ GROUP.find_iter(s)
+ .any(|group| matches!(
group.as_str(),
- "{file}" | "{parent}" | "{directory}"
- ){
- return true;
- }
- }
- false
+ "{file}" | "{file-name}" | "{parent}" | "{directory}",
+ ))
}
pub fn str_has_other_panel_group(s: &str) -> bool {
for group in GROUP.find_iter(s) {
diff --git a/src/verb/verb.rs b/src/verb/verb.rs
index e083691..9dfc09a 100644
--- a/src/verb/verb.rs
+++ b/src/verb/verb.rs
@@ -54,6 +54,10 @@ pub struct Verb {
/// whether we need to have a secondary panel for execution
/// (which is the case when the execution pattern has {other-panel-file})
pub needs_another_panel: bool,
+
+ /// if true (default) verbs are directly executed when
+ /// triggered with a keyboard shortcut
+ pub auto_exec: bool,
}
impl Verb {
@@ -68,18 +72,21 @@ impl Verb {
if let Some(ref invocation_parser) = invocation_parser {
names.push(invocation_parser.name().to_string());
}
- let (needs_selection, needs_another_panel) = match &execution {
+ let (
+ needs_selection,
+ needs_another_panel,
+ ) = match &execution {
VerbExecution::Internal(ie) => (
ie.needs_selection(),
false,
),
VerbExecution::External(ee) => (
ee.exec_pattern.has_selection_group(),
- ee.exec_pattern.has_other_panel_group()
+ ee.exec_pattern.has_other_panel_group(),
),
VerbExecution::Sequence(se) => (
se.sequence.has_selection_group(),
- se.sequence.has_other_panel_group()
+ se.sequence.has_other_panel_group(),
)
};
Ok(Self {
@@ -92,6 +99,7 @@ impl Verb {
selection_condition: SelectionType::Any,
needs_selection,
needs_another_panel,
+ auto_exec: true,
})
}
fn update_key_desc(&mut self) {
@@ -147,6 +155,10 @@ impl Verb {
self.needs_another_panel = true;
self
}
+ pub fn with_auto_exec(mut self, b: bool) -> Self {
+ self.auto_exec = b;
+ self
+ }
/// Assuming the verb has been matched, check whether the arguments
/// are OK according to the regex. Return none when there's no problem