//! utility functions to help handle the `:select` internal use { super::*, crate::{ app::*, browser::BrowserState, command::TriggerType, display::Screen, path::{self, PathAnchor}, tree::Tree, }, std::path::{Path, PathBuf}, }; /// general implementation for verbs based on the :select internal with optionally /// a bang or an argument. pub fn on_internal( internal_exec: &InternalExecution, input_invocation: Option<&VerbInvocation>, trigger_type: TriggerType, tree: &mut Tree, app_state: & AppState, cc: &CmdContext, ) -> CmdResult { let screen = cc.app.screen; info!( "internal_select.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); 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, &tree.selected_line().path, input_arg, app_state, ); on_path(path, tree, screen, bang) } _ => { // the :select 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 // `:select some/path`). // The given path may be relative hence the need for the // state's selection let path = path::path_from( &tree.selected_line().path, PathAnchor::Unspecified, arg, ); let bang = input_invocation .map(|inv| inv.bang) .unwrap_or(internal_exec.bang); on_path(path, tree, screen, bang) } else { // there's nothing really to do here CmdResult::Keep } } } } /// 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 or 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: ":select {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 :select internal execution was triggered from the // input (which must be a kind of alias for :select) // 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 // `:select 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) => { // This doesn't really make sense: we're selecting the currently // selected path base_path.to_path_buf() } } } pub fn on_path( path: PathBuf, tree: &mut Tree, screen: Screen, in_new_panel: bool, ) -> CmdResult { info!("executing :select on path {:?}", &path); if in_new_panel { warn!("bang in :select isn't supported yet"); } if tree.try_select_path(&path) { tree.make_selection_visible(BrowserState::page_height(screen) as usize); } CmdResult::Keep }