summaryrefslogtreecommitdiffstats
path: root/src/verb
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2022-07-10 22:28:15 +0200
committerCanop <cano.petrole@gmail.com>2022-07-10 22:29:23 +0200
commit94ef5f6ed4376df0d1b96b2ba38e8f5b2ca70ed9 (patch)
treed7056f2b4eb07aa4b2d99c0a832b560da90e766e /src/verb
parentd4751c863011bf2583b07942363df3f70db37d97 (diff)
allow :focus based verbs to take a pattern
This allows verb definitions such as the following ones: ```Hjson { invocation: "gos" execution: ":focus" } { invocation: "go {path}" execution: ":focus {path}" } { invocation: "goroot" execution: ":focus /" } { invocation: "gotar {path}" execution: ":focus {path}/target" } ``` Fix #389
Diffstat (limited to 'src/verb')
-rw-r--r--src/verb/execution_builder.rs16
-rw-r--r--src/verb/internal_focus.rs165
-rw-r--r--src/verb/verb.rs39
3 files changed, 160 insertions, 60 deletions
diff --git a/src/verb/execution_builder.rs b/src/verb/execution_builder.rs
index 2d56e9a..bfd4c7d 100644
--- a/src/verb/execution_builder.rs
+++ b/src/verb/execution_builder.rs
@@ -45,7 +45,7 @@ impl<'b> ExecutionStringBuilder<'b> {
invocation_parser: &Option<InvocationParser>,
sel_info: SelInfo<'b>,
app_state: &'b AppState,
- invocation_args: &Option<String>,
+ invocation_args: Option<&String>,
) -> Self {
let invocation_values = invocation_parser
.as_ref()
@@ -223,16 +223,24 @@ impl<'b> ExecutionStringBuilder<'b> {
}
}
+ fn base_dir(&self) -> &Path {
+ self.sel_info
+ .one_sel()
+ .map_or(self.root, |sel| sel.path)
+ }
+
/// build a path
pub fn path(
&self,
pattern: &str,
) -> PathBuf {
- PathBuf::from(
- GROUP.replace_all(
+ path::path_from(
+ self.base_dir(),
+ path::PathAnchor::Unspecified,
+ &GROUP.replace_all(
pattern,
|ec: &Captures<'_>| self.get_capture_replacement(ec),
- ).to_string()
+ )
)
}
/// build a shell compatible command, with escapings
diff --git a/src/verb/internal_focus.rs b/src/verb/internal_focus.rs
index c1e2028..2399a79 100644
--- a/src/verb/internal_focus.rs
+++ b/src/verb/internal_focus.rs
@@ -77,6 +77,81 @@ pub fn new_panel_on_path(
}
}
+/// Compute the path to go to in case of the internal being triggered from
+/// the input.
+///
+/// This path depends on the verb (which may hardcore the path of have a
+/// pattern), from the selection,
+fn path_from_input(
+ verb: &Verb,
+ internal_exec: &InternalExecution,
+ base_path: &Path, // either the selected path or the root path
+ input_arg: Option<&String>,
+ app_state: &AppState,
+) -> PathBuf {
+ match (input_arg, internal_exec.arg.as_ref()) {
+ (Some(input_arg), Some(verb_arg)) => {
+ // The verb probably defines some patttern which uses the input.
+ // For example:
+ // {
+ // invocation: "gotar {path}"
+ // execution: ":focus {path}/target"
+ // }
+ // (or that input is useless)
+ let path_builder = ExecutionStringBuilder::with_invocation(
+ &verb.invocation_parser,
+ SelInfo::from_path(base_path),
+ app_state,
+ Some(input_arg),
+ );
+ path_builder.path(verb_arg)
+ }
+ (Some(input_arg), None) => {
+ // the verb defines nothing
+ // 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
+ path::path_from(base_path, PathAnchor::Unspecified, input_arg)
+ }
+ (None, Some(verb_arg)) => {
+ // the verb defines the path where to go..
+ // the internal_execution specifies the path to use
+ // (it may come from a configured verb whose execution is
+ // `:focus some/path`).
+ // The given path may be relative hence the need for the
+ // state's selection
+ // (we assume a check before ensured it doesn't need an input)
+ path::path_from(base_path, PathAnchor::Unspecified, verb_arg)
+ }
+ (None, None) => {
+ // user only wants to open the selected path, either in the same panel or
+ // in a new one
+ base_path.to_path_buf()
+ }
+ }
+
+}
+
+pub fn get_status_markdown(
+ verb: &Verb,
+ internal_exec: &InternalExecution,
+ sel_info: SelInfo<'_>,
+ invocation: &VerbInvocation,
+ app_state: &AppState,
+) -> String {
+ let base_path = sel_info
+ .one_path()
+ .unwrap_or(&app_state.root);
+ let path = path_from_input(
+ verb,
+ internal_exec,
+ base_path,
+ invocation.args.as_ref(),
+ app_state,
+ );
+ format!("Hit *enter* to focus `{}`", path.to_string_lossy())
+}
+
/// general implementation for verbs based on the :focus internal with optionally
/// a bang or an argument.
pub fn on_internal(
@@ -84,51 +159,61 @@ pub fn on_internal(
input_invocation: Option<&VerbInvocation>,
trigger_type: TriggerType,
selected_path: &Path,
- screen: Screen,
- con: &AppContext,
tree_options: TreeOptions,
+ app_state: & AppState,
+ cc: &CmdContext,
) -> CmdResult {
- if let Some(arg) = &internal_exec.arg {
- // the internal_execution specifies the path to use
- // (it may come from a configured verb whose execution is
- // `:focus some/path`).
- // The given path may be relative hence the need for the
- // state's selection
- let path = path::path_from(selected_path, PathAnchor::Unspecified, arg);
- let bang = input_invocation
+ let con = &cc.app.con;
+ let screen = cc.app.screen;
+ info!(
+ "internal_focus.on_internal internal_exec={:?} input_invocation={:?} trygger_type={:?}",
+ internal_exec,
+ input_invocation,
+ trigger_type,
+ );
+ let bang = input_invocation
.map(|inv| inv.bang)
.unwrap_or(internal_exec.bang);
- return on_path(path, screen, tree_options, bang, con);
- }
- if let Some(input_invocation) = &input_invocation {
- if let Some(input_arg) = &input_invocation.args {
- // user typed a path in the input
- match trigger_type {
- TriggerType::Input => {
- // 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, PathAnchor::Unspecified, input_arg);
- let bang = input_invocation.bang || internal_exec.bang;
- return on_path(path, screen, tree_options, bang, con);
- }
- _ => {
- // the :focus internal was triggered by a key, and without internal arg,
- // which 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, 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);
- }
+ let input_arg = input_invocation.as_ref()
+ .and_then(|invocation| invocation.args.as_ref());
+ match trigger_type {
+ TriggerType::Input(verb) => {
+ let path = path_from_input(
+ verb,
+ internal_exec,
+ selected_path,
+ input_arg,
+ app_state,
+ );
+ on_path(path, screen, tree_options, bang, con)
+ }
+ _ => {
+ // the :focus internal was triggered by a key
+ if let Some(arg) = &internal_exec.arg {
+ // the internal_execution specifies the path to use
+ // (it may come from a configured verb whose execution is
+ // `:focus some/path`).
+ // The given path may be relative hence the need for the
+ // state's selection
+ let path = path::path_from(selected_path, PathAnchor::Unspecified, arg);
+ let bang = input_invocation
+ .map(|inv| inv.bang)
+ .unwrap_or(internal_exec.bang);
+ on_path(path, screen, tree_options, bang, con)
+ } else if let Some(input_arg) = input_arg {
+ // the :focus internal was triggered by a key, and without internal arg,
+ // which 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, PathAnchor::Unspecified, input_arg);
+ let arg_type = SelectionType::Any; // We might do better later
+ let purpose = PanelPurpose::ArgEdition { arg_type };
+ new_panel_on_path(path, screen, tree_options, purpose, con, HDir::Right)
+ } else {
+ // user only wants to open the selected path, either in the same panel or
+ // in a new one
+ on_path(selected_path.to_path_buf(), screen, tree_options, bang, con)
}
}
}
- // user only wants to open the selected path, either in the same panel or
- // in a new one
- let bang = input_invocation
- .map(|inv| inv.bang)
- .unwrap_or(internal_exec.bang);
- on_path(selected_path.to_path_buf(), screen, tree_options, bang, con)
}
diff --git a/src/verb/verb.rs b/src/verb/verb.rs
index d315975..e6c6f8b 100644
--- a/src/verb/verb.rs
+++ b/src/verb/verb.rs
@@ -4,10 +4,14 @@ use {
app::*,
errors::ConfError,
keys::KEY_FORMAT,
- path::{self, PathAnchor},
+ path::PathAnchor,
},
crossterm::event::KeyEvent,
- std::path::PathBuf,
+ std::{
+ cmp::PartialEq,
+ path::PathBuf,
+ ptr,
+ },
};
/// what makes a verb.
@@ -20,6 +24,9 @@ use {
/// - internal behaviors (focusing a path, going back, showing the help, etc.)
/// Some verbs are builtins, some other ones are created by configuration.
/// Both builtins and configured vers can be internal or external based.
+///
+/// Verbs can't be cloned. Two verbs are equal if they have the same address
+/// in memory.
#[derive(Debug)]
pub struct Verb {
/// names (like "cd", "focus", "focus_tab", "c") by which
@@ -67,6 +74,12 @@ pub struct Verb {
pub show_in_doc: bool
}
+impl PartialEq for Verb {
+ fn eq(&self, other: &Self) -> bool {
+ ptr::eq(self, other)
+ }
+}
+
impl Verb {
pub fn new(
@@ -221,19 +234,13 @@ impl Verb {
// thus I hardcode the test here.
if let VerbExecution::Internal(internal_exec) = &self.execution {
if internal_exec.internal == Internal::focus {
- if let Some(sel) = sel_info.one_sel() {
- let arg = invocation.args.as_ref().or(internal_exec.arg.as_ref());
- let pb;
- let arg_path = if let Some(arg) = arg {
- pb = path::path_from(sel.path, PathAnchor::Unspecified, arg);
- &pb
- } else {
- sel.path
- };
- return format!("Hit *enter* to {} `{}`", name, arg_path.to_string_lossy());
- } else {
- return "You can't focus without selection".to_string();
- }
+ return internal_focus::get_status_markdown(
+ self,
+ internal_exec,
+ sel_info,
+ invocation,
+ app_state,
+ );
}
}
@@ -242,7 +249,7 @@ impl Verb {
&self.invocation_parser,
sel_info,
app_state,
- &invocation.args,
+ invocation.args.as_ref(),
)
};
if let VerbExecution::Sequence(seq_ex) = &self.execution {