diff --git a/Cargo.lock b/Cargo.lock index bbc2c6ab51..63afd65b26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3254,7 +3254,6 @@ name = "nu_plugin_query" version = "0.91.1" dependencies = [ "gjson", - "nu-engine", "nu-plugin", "nu-protocol", "scraper", diff --git a/crates/nu-plugin/src/plugin/context.rs b/crates/nu-plugin/src/plugin/context.rs index f0c654078c..58c550da76 100644 --- a/crates/nu-plugin/src/plugin/context.rs +++ b/crates/nu-plugin/src/plugin/context.rs @@ -4,7 +4,7 @@ use std::{ sync::{atomic::AtomicBool, Arc}, }; -use nu_engine::get_eval_block_with_early_return; +use nu_engine::{get_eval_block_with_early_return, get_full_help}; use nu_protocol::{ ast::Call, engine::{Closure, EngineState, Redirection, Stack}, @@ -32,6 +32,8 @@ pub trait PluginExecutionContext: Send + Sync { fn get_current_dir(&self) -> Result, ShellError>; /// Set an environment variable fn add_env_var(&mut self, name: String, value: Value) -> Result<(), ShellError>; + /// Get help for the current command + fn get_help(&self) -> Result, ShellError>; /// Evaluate a closure passed to the plugin fn eval_closure( &self, @@ -137,6 +139,19 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> { Ok(()) } + fn get_help(&self) -> Result, ShellError> { + let decl = self.engine_state.get_decl(self.call.decl_id); + + Ok(get_full_help( + &decl.signature(), + &decl.examples(), + &self.engine_state, + &mut self.stack.clone(), + false, + ) + .into_spanned(self.call.head)) + } + fn eval_closure( &self, closure: Spanned, @@ -252,6 +267,12 @@ impl PluginExecutionContext for PluginExecutionBogusContext { }) } + fn get_help(&self) -> Result, ShellError> { + Err(ShellError::NushellFailed { + msg: "get_help not implemented on bogus".into(), + }) + } + fn eval_closure( &self, _closure: Spanned, diff --git a/crates/nu-plugin/src/plugin/interface/engine.rs b/crates/nu-plugin/src/plugin/interface/engine.rs index 10df2baf86..397ccae032 100644 --- a/crates/nu-plugin/src/plugin/interface/engine.rs +++ b/crates/nu-plugin/src/plugin/interface/engine.rs @@ -636,6 +636,31 @@ impl EngineInterface { } } + /// Get the help string for the current command. + /// + /// This returns the same string as passing `--help` would, and can be used for the top-level + /// command in a command group that doesn't do anything on its own (e.g. `query`). + /// + /// # Example + /// ```rust,no_run + /// # use nu_protocol::{Value, ShellError}; + /// # use nu_plugin::EngineInterface; + /// # fn example(engine: &EngineInterface) -> Result<(), ShellError> { + /// eprintln!("{}", engine.get_help()?); + /// # Ok(()) + /// # } + /// ``` + pub fn get_help(&self) -> Result { + match self.engine_call(EngineCall::GetHelp)? { + EngineCallResponse::PipelineData(PipelineData::Value(Value::String { val, .. }, _)) => { + Ok(val) + } + _ => Err(ShellError::PluginFailedToDecode { + msg: "Received unexpected response type for EngineCall::GetHelp".into(), + }), + } + } + /// Ask the engine to evaluate a closure. Input to the closure is passed as a stream, and the /// output is available as a stream. /// diff --git a/crates/nu-plugin/src/plugin/interface/engine/tests.rs b/crates/nu-plugin/src/plugin/interface/engine/tests.rs index 521a95dc58..6119ec7791 100644 --- a/crates/nu-plugin/src/plugin/interface/engine/tests.rs +++ b/crates/nu-plugin/src/plugin/interface/engine/tests.rs @@ -1005,6 +1005,24 @@ fn interface_add_env_var() -> Result<(), ShellError> { Ok(()) } +#[test] +fn interface_get_help() -> Result<(), ShellError> { + let test = TestCase::new(); + let manager = test.engine(); + let interface = manager.interface_for_context(0); + + start_fake_plugin_call_responder(manager, 1, move |_| { + EngineCallResponse::value(Value::test_string("help string")) + }); + + let help = interface.get_help()?; + + assert_eq!("help string", help); + + assert!(test.has_unconsumed_write()); + Ok(()) +} + #[test] fn interface_eval_closure_with_stream() -> Result<(), ShellError> { let test = TestCase::new(); diff --git a/crates/nu-plugin/src/plugin/interface/plugin.rs b/crates/nu-plugin/src/plugin/interface/plugin.rs index 69f73a373b..1829edda8d 100644 --- a/crates/nu-plugin/src/plugin/interface/plugin.rs +++ b/crates/nu-plugin/src/plugin/interface/plugin.rs @@ -1018,6 +1018,12 @@ pub(crate) fn handle_engine_call( context.add_env_var(name, value)?; Ok(EngineCallResponse::empty()) } + EngineCall::GetHelp => { + let help = context.get_help()?; + Ok(EngineCallResponse::value(Value::string( + help.item, help.span, + ))) + } EngineCall::EvalClosure { closure, positional, diff --git a/crates/nu-plugin/src/protocol/mod.rs b/crates/nu-plugin/src/protocol/mod.rs index d94285623b..35895ca04c 100644 --- a/crates/nu-plugin/src/protocol/mod.rs +++ b/crates/nu-plugin/src/protocol/mod.rs @@ -447,6 +447,8 @@ pub enum EngineCall { GetCurrentDir, /// Set an environment variable in the caller's scope AddEnvVar(String, Value), + /// Get help for the current command + GetHelp, /// Evaluate a closure with stream input/output EvalClosure { /// The closure to call. @@ -474,6 +476,7 @@ impl EngineCall { EngineCall::GetEnvVars => "GetEnvs", EngineCall::GetCurrentDir => "GetCurrentDir", EngineCall::AddEnvVar(..) => "AddEnvVar", + EngineCall::GetHelp => "GetHelp", EngineCall::EvalClosure { .. } => "EvalClosure", } } @@ -491,6 +494,7 @@ impl EngineCall { EngineCall::GetEnvVars => EngineCall::GetEnvVars, EngineCall::GetCurrentDir => EngineCall::GetCurrentDir, EngineCall::AddEnvVar(name, value) => EngineCall::AddEnvVar(name, value), + EngineCall::GetHelp => EngineCall::GetHelp, EngineCall::EvalClosure { closure, positional, diff --git a/crates/nu_plugin_example/src/commands/main.rs b/crates/nu_plugin_example/src/commands/main.rs index 319eecce92..3dfb137fff 100644 --- a/crates/nu_plugin_example/src/commands/main.rs +++ b/crates/nu_plugin_example/src/commands/main.rs @@ -28,11 +28,10 @@ particularly useful. fn run( &self, _plugin: &Self::Plugin, - _engine: &EngineInterface, + engine: &EngineInterface, call: &EvaluatedCall, _input: &Value, ) -> Result { - Err(LabeledError::new("No subcommand provided") - .with_label("add --help to see a list of subcommands", call.head)) + Ok(Value::string(engine.get_help()?, call.head)) } } diff --git a/crates/nu_plugin_query/Cargo.toml b/crates/nu_plugin_query/Cargo.toml index ad3fda238d..227968c739 100644 --- a/crates/nu_plugin_query/Cargo.toml +++ b/crates/nu_plugin_query/Cargo.toml @@ -18,7 +18,6 @@ bench = false [dependencies] nu-plugin = { path = "../nu-plugin", version = "0.91.1" } nu-protocol = { path = "../nu-protocol", version = "0.91.1" } -nu-engine = { path = "../nu-engine", version = "0.91.1" } gjson = "0.8" scraper = { default-features = false, version = "0.19" } diff --git a/crates/nu_plugin_query/src/query.rs b/crates/nu_plugin_query/src/query.rs index 8d58662b26..b46dccf81e 100644 --- a/crates/nu_plugin_query/src/query.rs +++ b/crates/nu_plugin_query/src/query.rs @@ -2,10 +2,8 @@ use crate::query_json::QueryJson; use crate::query_web::QueryWeb; use crate::query_xml::QueryXml; -use nu_engine::documentation::get_flags_section; use nu_plugin::{EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand}; use nu_protocol::{Category, LabeledError, PluginSignature, Value}; -use std::fmt::Write; #[derive(Default)] pub struct Query; @@ -46,36 +44,10 @@ impl SimplePluginCommand for QueryCommand { fn run( &self, _plugin: &Query, - _engine: &nu_plugin::EngineInterface, + engine: &nu_plugin::EngineInterface, call: &EvaluatedCall, _input: &Value, ) -> Result { - let help = get_brief_subcommand_help(); - Ok(Value::string(help, call.head)) + Ok(Value::string(engine.get_help()?, call.head)) } } - -pub fn get_brief_subcommand_help() -> String { - let sigs: Vec<_> = Query - .commands() - .into_iter() - .map(|cmd| cmd.signature()) - .collect(); - - let mut help = String::new(); - let _ = write!(help, "{}\n\n", sigs[0].sig.usage); - let _ = write!(help, "Usage:\n > {}\n\n", sigs[0].sig.name); - help.push_str("Subcommands:\n"); - - for x in sigs.iter().enumerate() { - if x.0 == 0 { - continue; - } - let _ = writeln!(help, " {} - {}", x.1.sig.name, x.1.sig.usage); - } - - help.push_str(&get_flags_section(None, &sigs[0].sig, |v| { - format!("{:#?}", v) - })); - help -}