use {
super::*,
crate::{
app::{Selection, SelectionType, Status},
errors::ConfError,
keys,
path::{self, PathAnchor},
},
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
std::path::PathBuf,
};
#[derive(Debug)]
pub struct Verb {
pub names: Vec<String>,
pub keys: Vec<KeyEvent>,
pub keys_desc: String,
pub invocation_parser: Option<InvocationParser>,
pub execution: VerbExecution,
pub description: VerbDescription,
pub selection_condition: SelectionType,
pub need_another_panel: bool,
}
impl Verb {
pub fn new(
invocation_str: Option<&str>,
execution: VerbExecution,
description: VerbDescription,
) -> Result<Self, ConfError> {
let invocation_parser = invocation_str.map(InvocationParser::new).transpose()?;
let mut names = Vec::new();
if let Some(ref invocation_parser) = invocation_parser {
names.push(invocation_parser.name().to_string());
}
let need_another_panel = if let VerbExecution::External(ref external) = execution {
external.exec_pattern.has_other_panel_group()
} else {
false
};
Ok(Self {
names,
keys: Vec::new(),
keys_desc: "".to_string(),
invocation_parser,
execution,
description,
selection_condition: SelectionType::Any,
need_another_panel,
})
}
fn update_key_desc(&mut self) {
self.keys_desc = self
.keys
.iter()
.map(|&k| keys::key_event_desc(k))
.collect::<Vec<String>>() .join(", ");
}
pub fn with_key(mut self, key: KeyEvent) -> Self {
self.keys.push(key);
self.update_key_desc();
self
}
pub fn add_keys(&mut self, keys: Vec<KeyEvent>) {
for key in keys {
self.keys.push(key);
}
self.update_key_desc();
}
pub fn with_alt_key(self, chr: char) -> Self {
self.with_key(KeyEvent {
code: KeyCode::Char(chr),
modifiers: KeyModifiers::ALT,
})
}
pub fn with_control_key(self, chr: char) -> Self {
self.with_key(KeyEvent {
code: KeyCode::Char(chr),
modifiers: KeyModifiers::CONTROL,
})
}
pub fn with_char_key(self, chr: char) -> Self {
self.with_key(KeyEvent {
code: KeyCode::Char(chr),
modifiers: KeyModifiers::NONE,
})
}
pub fn with_description(mut self, description: &str) -> Self {
self.description = VerbDescription::from_text(description.to_string());
self
}
pub fn with_shortcut(mut self, shortcut: &str) -> Self {
self.names.push(shortcut.to_string());
self
}
pub fn with_stype(mut self, stype: SelectionType) -> Self {
self.selection_condition = stype;
self
}
pub fn needing_another_panel(mut self) -> Self {
self.need_another_panel = true;
self
}
pub fn check_args(
&self,
invocation: &VerbInvocation,
other_path: &Option<PathBuf>,
) -> Option<String> {
if self.need_another_panel && other_path.is_none() {
Some("This verb needs exactly two panels".to_string())
} else if let Some(ref parser) = self.invocation_parser {
parser.check_args(invocation, other_path)
} else if invocation.args.is_some() {
Some("This verb doesn't take arguments".to_string())
} else {
None
}
}
fn get_status_markdown(
&self,
sel: Selection<'_>,
other_path: &Option<PathBuf>,
invocation: &VerbInvocation,
) -> String {
let name = self.names.get(0).unwrap_or(&invocation.name);
if let VerbExecution::Internal(internal_exec) = &self.execution {
if internal_exec.internal == Internal::focus {
let arg = invocation.args.as_ref().or_else(|| 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());
}
}
let builder = || {
ExecutionStringBuilder::from_invocation(
&self.invocation_parser,
sel,
other_path,
&invocation.args,
)
};
if let VerbExecution::Sequence(seq_ex) = &self.execution {
let exec_desc = builder().shell_exec_string(
&ExecPattern::from_string(&seq_ex.sequence.raw)
);
format!("Hit *enter* to **{}**: `{}`", name, &exec_desc)
} else if let VerbExecution::External(external_exec) = &self.execution {
let exec_desc = builder().shell_exec_string(&external_exec.exec_pattern);
format!("Hit *enter* to **{}**: `{}`", name, &exec_desc)
} else if self.description.code {
format!("Hit *enter* to **{}**: `{}`", name, &self.description.content)
} else {
format!("Hit *enter* to **{}**: {}", name, &self.description.content)
}
}
pub fn get_status(
&self,
sel: Selection<'_>,
other_path: &Option<PathBuf>,
invocation: &VerbInvocation,
) -> Status {
if let Some(err) = self.check_args(invocation, other_path) {
Status::new(err, true)
} else {
Status::new(
self.get_status_markdown(
sel,
other_path,
invocation,
),
false,
)
}
}
pub fn get_arg_selection_type(&self) -> Option<SelectionType> {
self.invocation_parser
.as_ref()
.and_then(|parser| parser.arg_selection_type)
}
pub fn get_arg_anchor(&self) -> PathAnchor {
self.invocation_parser
.as_ref()
.map_or(PathAnchor::Unspecified, |parser| parser.arg_anchor)
}
pub fn get_internal(&self) -> Option<Internal> {
match &self.execution {
VerbExecution::Internal(internal_exec) => Some(internal_exec.internal),
_ => None,
}
}
pub fn is_internal(&self, internal: Internal) -> bool {
self.get_internal() == Some(internal)
}
}