Add search terms to Command and Signature (#4980)

* Add search terms to command

* Rename Signature desc to usage

To be named uniformly with extra_usage

* Throw in foldl search term for reduce

* Add missing usage to post

* Add search terms to signature

* Try to add capnp Signature serialization
This commit is contained in:
Jakub Žádník 2022-03-27 22:25:30 +03:00 committed by GitHub
parent 0c9dd6a29a
commit 2873e943b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 269 additions and 100 deletions

View File

@ -17,7 +17,6 @@ impl Command for Do {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("do") Signature::build("do")
.desc(self.usage())
.required("block", SyntaxShape::Any, "the block to run") .required("block", SyntaxShape::Any, "the block to run")
.switch( .switch(
"ignore-errors", "ignore-errors",

View File

@ -7,6 +7,8 @@ use nu_protocol::{
use nu_engine::{get_full_help, CallExt}; use nu_engine::{get_full_help, CallExt};
use std::borrow::Borrow;
#[derive(Clone)] #[derive(Clone)]
pub struct Help; pub struct Help;
@ -96,14 +98,22 @@ fn help(
let mut vals = vec![]; let mut vals = vec![];
let decl = engine_state.get_decl(decl_id); let decl = engine_state.get_decl(decl_id);
let sig = decl.signature(); let sig = decl.signature().update_from_command(decl.borrow());
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
let matches_term = if search_terms.is_empty() {
search_terms
.iter()
.any(|term| term.to_lowercase().contains(&search_string))
} else {
false
};
let key = sig.name.clone();
let c = sig.usage.clone();
let e = sig.extra_usage.clone();
if key.to_lowercase().contains(&search_string) if key.to_lowercase().contains(&search_string)
|| c.to_lowercase().contains(&search_string) || usage.to_lowercase().contains(&search_string)
|| e.to_lowercase().contains(&search_string) || matches_term
{ {
cols.push("name".into()); cols.push("name".into());
vals.push(Value::String { vals.push(Value::String {
@ -136,10 +146,20 @@ fn help(
}); });
cols.push("usage".into()); cols.push("usage".into());
vals.push(Value::String { val: c, span: head }); vals.push(Value::String {
val: usage,
span: head,
});
cols.push("extra_usage".into()); cols.push("search_terms".into());
vals.push(Value::String { val: e, span: head }); vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: search_terms.join(", "),
span: head,
}
});
found_cmds_vec.push(Value::Record { found_cmds_vec.push(Value::Record {
cols, cols,
@ -163,11 +183,11 @@ fn help(
let mut vals = vec![]; let mut vals = vec![];
let decl = engine_state.get_decl(decl_id); let decl = engine_state.get_decl(decl_id);
let sig = decl.signature(); let sig = decl.signature().update_from_command(decl.borrow());
let key = sig.name.clone(); let key = sig.name;
let c = sig.usage.clone(); let usage = sig.usage;
let e = sig.extra_usage.clone(); let search_terms = sig.search_terms;
cols.push("name".into()); cols.push("name".into());
vals.push(Value::String { vals.push(Value::String {
@ -200,10 +220,20 @@ fn help(
}); });
cols.push("usage".into()); cols.push("usage".into());
vals.push(Value::String { val: c, span: head }); vals.push(Value::String {
val: usage,
span: head,
});
cols.push("extra_usage".into()); cols.push("search_terms".into());
vals.push(Value::String { val: e, span: head }); vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: search_terms.join(", "),
span: head,
}
});
found_cmds_vec.push(Value::Record { found_cmds_vec.push(Value::Record {
cols, cols,

View File

@ -20,7 +20,6 @@ impl Command for ViewSource {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("view-source") Signature::build("view-source")
.desc(self.usage())
.required("item", SyntaxShape::Any, "name or block to view") .required("item", SyntaxShape::Any, "name or block to view")
.category(Category::Core) .category(Category::Core)
} }

View File

@ -36,6 +36,10 @@ impl Command for Reduce {
"Aggregate a list table to a single value using an accumulator block." "Aggregate a list table to a single value using an accumulator block."
} }
fn search_terms(&self) -> Vec<&str> {
vec!["map", "fold", "foldl"]
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -27,7 +27,6 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("fetch") Signature::build("fetch")
.desc("Load from a URL into a cell, convert to table if possible (avoid by appending '--raw').")
.required( .required(
"URL", "URL",
SyntaxShape::String, SyntaxShape::String,
@ -45,15 +44,33 @@ impl Command for SubCommand {
"the password when authenticating", "the password when authenticating",
Some('p'), Some('p'),
) )
.named("timeout", SyntaxShape::Int, "timeout period in seconds", Some('t')) .named(
.named("headers",SyntaxShape::Any, "custom headers you want to add ", Some('H')) "timeout",
.switch("raw", "fetch contents as text rather than a table", Some('r')) SyntaxShape::Int,
"timeout period in seconds",
Some('t'),
)
.named(
"headers",
SyntaxShape::Any,
"custom headers you want to add ",
Some('H'),
)
.switch(
"raw",
"fetch contents as text rather than a table",
Some('r'),
)
.filter() .filter()
.category(Category::Network) .category(Category::Network)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Fetch the contents from a URL (HTTP GET operation)." "Fetch the contents from a URL."
}
fn extra_usage(&self) -> &str {
"Performs HTTP GET operation."
} }
fn run( fn run(

View File

@ -24,7 +24,6 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("post") Signature::build("post")
.desc("Post content to a URL and retrieve data as a table if possible.")
.required("path", SyntaxShape::String, "the URL to post to") .required("path", SyntaxShape::String, "the URL to post to")
.required("body", SyntaxShape::Any, "the contents of the post body") .required("body", SyntaxShape::Any, "the contents of the post body")
.named( .named(
@ -70,9 +69,15 @@ impl Command for SubCommand {
.filter() .filter()
.category(Category::Network) .category(Category::Network)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Post a body to a URL (HTTP POST operation)." "Post a body to a URL."
} }
fn extra_usage(&self) -> &str {
"Performs HTTP POST operation."
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,

View File

@ -16,7 +16,6 @@ impl Command for Ps {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("ps") Signature::build("ps")
.desc("View information about system processes.")
.switch( .switch(
"long", "long",
"list all available columns for each entry", "list all available columns for each entry",

View File

@ -14,10 +14,7 @@ impl Command for Sys {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("sys") Signature::build("sys").filter().category(Category::System)
.desc("View information about the current system.")
.filter()
.category(Category::System)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View File

@ -4,6 +4,8 @@ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Example, IntoPipelineData, Signature, Span, Value, Example, IntoPipelineData, Signature, Span, Value,
}; };
use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
const COMMANDS_DOCS_DIR: &str = "docs/commands"; const COMMANDS_DOCS_DIR: &str = "docs/commands";
@ -51,10 +53,12 @@ fn generate_doc(
}); });
} }
let signature = command.signature().update_from_command(command.borrow());
cols.push("documentation".to_owned()); cols.push("documentation".to_owned());
vals.push(Value::String { vals.push(Value::String {
val: get_documentation( val: get_documentation(
&command.signature(), &signature,
&command.examples(), &command.examples(),
engine_state, engine_state,
stack, stack,
@ -177,6 +181,13 @@ pub fn get_documentation(
} }
} }
if !sig.search_terms.is_empty() {
long_desc.push_str(&format!(
"Search terms: {}\n\n",
sig.search_terms.join(", ")
));
}
long_desc.push_str(&format!("Usage:\n > {}\n", sig.call_signature())); long_desc.push_str(&format!("Usage:\n > {}\n", sig.call_signature()));
if !subcommands.is_empty() { if !subcommands.is_empty() {

View File

@ -817,6 +817,7 @@ pub fn create_scope(
let decl = engine_state.get_decl(*decl_id); let decl = engine_state.get_decl(*decl_id);
let signature = decl.signature(); let signature = decl.signature();
cols.push("category".to_string()); cols.push("category".to_string());
vals.push(Value::String { vals.push(Value::String {
val: signature.category.to_string(), val: signature.category.to_string(),
@ -1026,6 +1027,17 @@ pub fn create_scope(
span, span,
}); });
let search_terms = decl.search_terms();
cols.push("search_terms".to_string());
vals.push(if search_terms.is_empty() {
Value::nothing(span)
} else {
Value::String {
val: search_terms.join(", "),
span,
}
});
commands.push(Value::Record { cols, vals, span }) commands.push(Value::Record { cols, vals, span })
} }
} }

View File

@ -1884,20 +1884,17 @@ pub mod signature {
!self.reader.get_pointer_field(2).is_null() !self.reader.get_pointer_field(2).is_null()
} }
#[inline] #[inline]
pub fn get_required_positional( pub fn get_search_terms(self) -> ::capnp::Result<::capnp::text_list::Reader<'a>> {
self,
) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>>
{
::capnp::traits::FromPointerReader::get_from_pointer( ::capnp::traits::FromPointerReader::get_from_pointer(
&self.reader.get_pointer_field(3), &self.reader.get_pointer_field(3),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
pub fn has_required_positional(&self) -> bool { pub fn has_search_terms(&self) -> bool {
!self.reader.get_pointer_field(3).is_null() !self.reader.get_pointer_field(3).is_null()
} }
#[inline] #[inline]
pub fn get_optional_positional( pub fn get_required_positional(
self, self,
) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>> ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>>
{ {
@ -1906,31 +1903,44 @@ pub mod signature {
::core::option::Option::None, ::core::option::Option::None,
) )
} }
pub fn has_optional_positional(&self) -> bool { pub fn has_required_positional(&self) -> bool {
!self.reader.get_pointer_field(4).is_null() !self.reader.get_pointer_field(4).is_null()
} }
#[inline] #[inline]
pub fn get_rest(self) -> ::capnp::Result<crate::plugin_capnp::argument::Reader<'a>> { pub fn get_optional_positional(
self,
) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>>
{
::capnp::traits::FromPointerReader::get_from_pointer( ::capnp::traits::FromPointerReader::get_from_pointer(
&self.reader.get_pointer_field(5), &self.reader.get_pointer_field(5),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
pub fn has_rest(&self) -> bool { pub fn has_optional_positional(&self) -> bool {
!self.reader.get_pointer_field(5).is_null() !self.reader.get_pointer_field(5).is_null()
} }
#[inline] #[inline]
pub fn get_rest(self) -> ::capnp::Result<crate::plugin_capnp::argument::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(
&self.reader.get_pointer_field(6),
::core::option::Option::None,
)
}
pub fn has_rest(&self) -> bool {
!self.reader.get_pointer_field(6).is_null()
}
#[inline]
pub fn get_named( pub fn get_named(
self, self,
) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::flag::Owned>> ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::flag::Owned>>
{ {
::capnp::traits::FromPointerReader::get_from_pointer( ::capnp::traits::FromPointerReader::get_from_pointer(
&self.reader.get_pointer_field(6), &self.reader.get_pointer_field(7),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
pub fn has_named(&self) -> bool { pub fn has_named(&self) -> bool {
!self.reader.get_pointer_field(6).is_null() !self.reader.get_pointer_field(7).is_null()
} }
#[inline] #[inline]
pub fn get_is_filter(self) -> bool { pub fn get_is_filter(self) -> bool {
@ -2068,12 +2078,40 @@ pub mod signature {
!self.builder.get_pointer_field(2).is_null() !self.builder.get_pointer_field(2).is_null()
} }
#[inline] #[inline]
pub fn get_search_terms(self) -> ::capnp::Result<::capnp::text_list::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(
self.builder.get_pointer_field(3),
::core::option::Option::None,
)
}
#[inline]
pub fn set_search_terms(
&mut self,
value: ::capnp::text_list::Reader<'a>,
) -> ::capnp::Result<()> {
::capnp::traits::SetPointerBuilder::set_pointer_builder(
self.builder.get_pointer_field(3),
value,
false,
)
}
#[inline]
pub fn init_search_terms(self, size: u32) -> ::capnp::text_list::Builder<'a> {
::capnp::traits::FromPointerBuilder::init_pointer(
self.builder.get_pointer_field(3),
size,
)
}
pub fn has_search_terms(&self) -> bool {
!self.builder.get_pointer_field(3).is_null()
}
#[inline]
pub fn get_required_positional( pub fn get_required_positional(
self, self,
) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned>> ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned>>
{ {
::capnp::traits::FromPointerBuilder::get_from_pointer( ::capnp::traits::FromPointerBuilder::get_from_pointer(
self.builder.get_pointer_field(3), self.builder.get_pointer_field(4),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
@ -2083,7 +2121,7 @@ pub mod signature {
value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>, value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>,
) -> ::capnp::Result<()> { ) -> ::capnp::Result<()> {
::capnp::traits::SetPointerBuilder::set_pointer_builder( ::capnp::traits::SetPointerBuilder::set_pointer_builder(
self.builder.get_pointer_field(3), self.builder.get_pointer_field(4),
value, value,
false, false,
) )
@ -2094,12 +2132,12 @@ pub mod signature {
size: u32, size: u32,
) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned> { ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned> {
::capnp::traits::FromPointerBuilder::init_pointer( ::capnp::traits::FromPointerBuilder::init_pointer(
self.builder.get_pointer_field(3), self.builder.get_pointer_field(4),
size, size,
) )
} }
pub fn has_required_positional(&self) -> bool { pub fn has_required_positional(&self) -> bool {
!self.builder.get_pointer_field(3).is_null() !self.builder.get_pointer_field(4).is_null()
} }
#[inline] #[inline]
pub fn get_optional_positional( pub fn get_optional_positional(
@ -2107,7 +2145,7 @@ pub mod signature {
) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned>> ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned>>
{ {
::capnp::traits::FromPointerBuilder::get_from_pointer( ::capnp::traits::FromPointerBuilder::get_from_pointer(
self.builder.get_pointer_field(4), self.builder.get_pointer_field(5),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
@ -2117,7 +2155,7 @@ pub mod signature {
value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>, value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::argument::Owned>,
) -> ::capnp::Result<()> { ) -> ::capnp::Result<()> {
::capnp::traits::SetPointerBuilder::set_pointer_builder( ::capnp::traits::SetPointerBuilder::set_pointer_builder(
self.builder.get_pointer_field(4), self.builder.get_pointer_field(5),
value, value,
false, false,
) )
@ -2128,17 +2166,17 @@ pub mod signature {
size: u32, size: u32,
) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned> { ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::argument::Owned> {
::capnp::traits::FromPointerBuilder::init_pointer( ::capnp::traits::FromPointerBuilder::init_pointer(
self.builder.get_pointer_field(4), self.builder.get_pointer_field(5),
size, size,
) )
} }
pub fn has_optional_positional(&self) -> bool { pub fn has_optional_positional(&self) -> bool {
!self.builder.get_pointer_field(4).is_null() !self.builder.get_pointer_field(5).is_null()
} }
#[inline] #[inline]
pub fn get_rest(self) -> ::capnp::Result<crate::plugin_capnp::argument::Builder<'a>> { pub fn get_rest(self) -> ::capnp::Result<crate::plugin_capnp::argument::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer( ::capnp::traits::FromPointerBuilder::get_from_pointer(
self.builder.get_pointer_field(5), self.builder.get_pointer_field(6),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
@ -2148,17 +2186,17 @@ pub mod signature {
value: crate::plugin_capnp::argument::Reader<'_>, value: crate::plugin_capnp::argument::Reader<'_>,
) -> ::capnp::Result<()> { ) -> ::capnp::Result<()> {
::capnp::traits::SetPointerBuilder::set_pointer_builder( ::capnp::traits::SetPointerBuilder::set_pointer_builder(
self.builder.get_pointer_field(5), self.builder.get_pointer_field(6),
value, value,
false, false,
) )
} }
#[inline] #[inline]
pub fn init_rest(self) -> crate::plugin_capnp::argument::Builder<'a> { pub fn init_rest(self) -> crate::plugin_capnp::argument::Builder<'a> {
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(5), 0) ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(6), 0)
} }
pub fn has_rest(&self) -> bool { pub fn has_rest(&self) -> bool {
!self.builder.get_pointer_field(5).is_null() !self.builder.get_pointer_field(6).is_null()
} }
#[inline] #[inline]
pub fn get_named( pub fn get_named(
@ -2166,7 +2204,7 @@ pub mod signature {
) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::flag::Owned>> ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::flag::Owned>>
{ {
::capnp::traits::FromPointerBuilder::get_from_pointer( ::capnp::traits::FromPointerBuilder::get_from_pointer(
self.builder.get_pointer_field(6), self.builder.get_pointer_field(7),
::core::option::Option::None, ::core::option::Option::None,
) )
} }
@ -2176,7 +2214,7 @@ pub mod signature {
value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::flag::Owned>, value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::flag::Owned>,
) -> ::capnp::Result<()> { ) -> ::capnp::Result<()> {
::capnp::traits::SetPointerBuilder::set_pointer_builder( ::capnp::traits::SetPointerBuilder::set_pointer_builder(
self.builder.get_pointer_field(6), self.builder.get_pointer_field(7),
value, value,
false, false,
) )
@ -2187,12 +2225,12 @@ pub mod signature {
size: u32, size: u32,
) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::flag::Owned> { ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::flag::Owned> {
::capnp::traits::FromPointerBuilder::init_pointer( ::capnp::traits::FromPointerBuilder::init_pointer(
self.builder.get_pointer_field(6), self.builder.get_pointer_field(7),
size, size,
) )
} }
pub fn has_named(&self) -> bool { pub fn has_named(&self) -> bool {
!self.builder.get_pointer_field(6).is_null() !self.builder.get_pointer_field(7).is_null()
} }
#[inline] #[inline]
pub fn get_is_filter(self) -> bool { pub fn get_is_filter(self) -> bool {
@ -2226,14 +2264,14 @@ pub mod signature {
} }
impl Pipeline { impl Pipeline {
pub fn get_rest(&self) -> crate::plugin_capnp::argument::Pipeline { pub fn get_rest(&self) -> crate::plugin_capnp::argument::Pipeline {
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(5)) ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(6))
} }
} }
mod _private { mod _private {
use capnp::private::layout; use capnp::private::layout;
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { pub const STRUCT_SIZE: layout::StructSize = layout::StructSize {
data: 1, data: 1,
pointers: 7, pointers: 8,
}; };
pub const TYPE_ID: u64 = 0xec96_eeb4_8cb7_90fa; pub const TYPE_ID: u64 = 0xec96_eeb4_8cb7_90fa;
} }

View File

@ -55,13 +55,14 @@ struct Signature {
name @0 :Text; name @0 :Text;
usage @1 :Text; usage @1 :Text;
extraUsage @2 :Text; extraUsage @2 :Text;
requiredPositional @3 :List(Argument); searchTerms @3 :List(Text);
optionalPositional @4 :List(Argument); requiredPositional @4 :List(Argument);
optionalPositional @5 :List(Argument);
# Optional value. Check for existence when deserializing # Optional value. Check for existence when deserializing
rest @5 :Argument; rest @6 :Argument;
named @6 :List(Flag); named @7 :List(Flag);
isFilter @7 :Bool; isFilter @8 :Bool;
category @8 :Category; category @9 :Category;
} }
enum Category { enum Category {

View File

@ -30,6 +30,17 @@ pub(crate) fn serialize_signature(signature: &Signature, mut builder: signature:
_ => builder.set_category(PluginCategory::Default), _ => builder.set_category(PluginCategory::Default),
} }
// Serializing list of search terms
let mut search_terms_builder = builder
.reborrow()
.init_search_terms(signature.search_terms.len() as u32);
signature
.search_terms
.iter()
.enumerate()
.for_each(|(index, term)| search_terms_builder.set(index as u32, term.as_str()));
// Serializing list of required arguments // Serializing list of required arguments
let mut required_list = builder let mut required_list = builder
.reborrow() .reborrow()
@ -136,6 +147,17 @@ pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result<Signatu
PluginCategory::Generators => Category::Generators, PluginCategory::Generators => Category::Generators,
}; };
// Deserializing list of search terms
let search_terms = reader
.get_search_terms()
.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?
.iter()
.map(|term| {
term.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))
.map(|term| term.to_string())
})
.collect::<Result<Vec<String>, ShellError>>()?;
// Deserializing required arguments // Deserializing required arguments
let required_list = reader let required_list = reader
.get_required_positional() .get_required_positional()
@ -181,6 +203,7 @@ pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result<Signatu
name: name.to_string(), name: name.to_string(),
usage: usage.to_string(), usage: usage.to_string(),
extra_usage: extra_usage.to_string(), extra_usage: extra_usage.to_string(),
search_terms,
required_positional, required_positional,
optional_positional, optional_positional,
rest_positional, rest_positional,
@ -349,7 +372,8 @@ mod tests {
#[test] #[test]
fn value_round_trip_2() { fn value_round_trip_2() {
let signature = Signature::build("test-1") let signature = Signature::build("test-1")
.desc("Signature test 1 for plugin. Returns Value::Nothing") .usage("Signature test 1 for plugin. Returns Value::Nothing")
.search_terms(vec!["a".into(), "b".into()])
.required("a", SyntaxShape::Int, "required integer value") .required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value") .required("b", SyntaxShape::String, "required string value")
.optional("opt", SyntaxShape::Boolean, "Optional boolean") .optional("opt", SyntaxShape::Boolean, "Optional boolean")
@ -368,6 +392,12 @@ mod tests {
assert_eq!(signature.is_filter, returned_signature.is_filter); assert_eq!(signature.is_filter, returned_signature.is_filter);
assert_eq!(signature.category, returned_signature.category); assert_eq!(signature.category, returned_signature.category);
signature
.search_terms
.iter()
.zip(returned_signature.search_terms.iter())
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
signature signature
.required_positional .required_positional
.iter() .iter()

View File

@ -61,6 +61,11 @@ pub trait Command: Send + Sync + CommandClone {
fn get_block_id(&self) -> Option<BlockId> { fn get_block_id(&self) -> Option<BlockId> {
None None
} }
// Related terms to help with command search
fn search_terms(&self) -> Vec<&str> {
vec![]
}
} }
pub trait CommandClone { pub trait CommandClone {

View File

@ -11,6 +11,7 @@ use std::{
use crate::Value; use crate::Value;
use std::borrow::Borrow;
use std::path::Path; use std::path::Path;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -527,11 +528,7 @@ impl EngineState {
.map(|id| { .map(|id| {
let decl = self.get_decl(id); let decl = self.get_decl(id);
let mut signature = (*decl).signature(); (*decl).signature().update_from_command(decl.borrow())
signature.usage = decl.usage().to_string();
signature.extra_usage = decl.extra_usage().to_string();
signature
}) })
.collect() .collect()
} }
@ -549,9 +546,7 @@ impl EngineState {
.map(|id| { .map(|id| {
let decl = self.get_decl(id); let decl = self.get_decl(id);
let mut signature = (*decl).signature(); let signature = (*decl).signature().update_from_command(decl.borrow());
signature.usage = decl.usage().to_string();
signature.extra_usage = decl.extra_usage().to_string();
( (
signature, signature,

View File

@ -95,6 +95,7 @@ pub struct Signature {
pub name: String, pub name: String,
pub usage: String, pub usage: String,
pub extra_usage: String, pub extra_usage: String,
pub search_terms: Vec<String>,
pub required_positional: Vec<PositionalArg>, pub required_positional: Vec<PositionalArg>,
pub optional_positional: Vec<PositionalArg>, pub optional_positional: Vec<PositionalArg>,
pub rest_positional: Option<PositionalArg>, pub rest_positional: Option<PositionalArg>,
@ -135,6 +136,7 @@ impl Signature {
name: name.into(), name: name.into(),
usage: String::new(), usage: String::new(),
extra_usage: String::new(), extra_usage: String::new(),
search_terms: vec![],
required_positional: vec![], required_positional: vec![],
optional_positional: vec![], optional_positional: vec![],
rest_positional: None, rest_positional: None,
@ -144,13 +146,38 @@ impl Signature {
category: Category::Default, category: Category::Default,
} }
} }
pub fn build(name: impl Into<String>) -> Signature { pub fn build(name: impl Into<String>) -> Signature {
Signature::new(name.into()) Signature::new(name.into())
} }
/// Add a description to the signature /// Add a description to the signature
pub fn desc(mut self, usage: impl Into<String>) -> Signature { pub fn usage(mut self, msg: impl Into<String>) -> Signature {
self.usage = usage.into(); self.usage = msg.into();
self
}
/// Add an extra description to the signature
pub fn extra_usage(mut self, msg: impl Into<String>) -> Signature {
self.extra_usage = msg.into();
self
}
/// Add search terms to the signature
pub fn search_terms(mut self, terms: Vec<String>) -> Signature {
self.search_terms = terms;
self
}
/// Update signature's fields from a Command trait implementation
pub fn update_from_command(mut self, command: &dyn Command) -> Signature {
self.search_terms = command
.search_terms()
.into_iter()
.map(|term| term.to_string())
.collect();
self.extra_usage = command.extra_usage().to_string();
self.usage = command.usage().to_string();
self self
} }

View File

@ -9,14 +9,14 @@ fn test_signature() {
assert_eq!(signature, from_build); assert_eq!(signature, from_build);
// constructing signature with description // constructing signature with description
let signature = Signature::new("signature").desc("example usage"); let signature = Signature::new("signature").usage("example usage");
assert_eq!(signature.usage, "example usage".to_string()) assert_eq!(signature.usage, "example usage".to_string())
} }
#[test] #[test]
fn test_signature_chained() { fn test_signature_chained() {
let signature = Signature::new("new_signature") let signature = Signature::new("new_signature")
.desc("description") .usage("description")
.required("required", SyntaxShape::String, "required description") .required("required", SyntaxShape::String, "required description")
.optional("optional", SyntaxShape::String, "optional description") .optional("optional", SyntaxShape::String, "optional description")
.required_named( .required_named(
@ -129,7 +129,7 @@ fn test_signature_same_name() {
#[test] #[test]
fn test_signature_round_trip() { fn test_signature_round_trip() {
let signature = Signature::new("new_signature") let signature = Signature::new("new_signature")
.desc("description") .usage("description")
.required("first", SyntaxShape::String, "first required") .required("first", SyntaxShape::String, "first required")
.required("second", SyntaxShape::Int, "second required") .required("second", SyntaxShape::Int, "second required")
.optional("optional", SyntaxShape::String, "optional description") .optional("optional", SyntaxShape::String, "optional description")

View File

@ -9,7 +9,7 @@ impl Plugin for Example {
// plugin is registered to nushell // plugin is registered to nushell
vec![ vec![
Signature::build("nu-example-1") Signature::build("nu-example-1")
.desc("Signature test 1 for plugin. Returns Value::Nothing") .usage("Signature test 1 for plugin. Returns Value::Nothing")
.required("a", SyntaxShape::Int, "required integer value") .required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value") .required("b", SyntaxShape::String, "required string value")
.switch("flag", "a flag for the signature", Some('f')) .switch("flag", "a flag for the signature", Some('f'))
@ -18,7 +18,7 @@ impl Plugin for Example {
.rest("rest", SyntaxShape::String, "rest value string") .rest("rest", SyntaxShape::String, "rest value string")
.category(Category::Experimental), .category(Category::Experimental),
Signature::build("nu-example-2") Signature::build("nu-example-2")
.desc("Signature test 2 for plugin. Returns list of records") .usage("Signature test 2 for plugin. Returns list of records")
.required("a", SyntaxShape::Int, "required integer value") .required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value") .required("b", SyntaxShape::String, "required string value")
.switch("flag", "a flag for the signature", Some('f')) .switch("flag", "a flag for the signature", Some('f'))
@ -27,7 +27,7 @@ impl Plugin for Example {
.rest("rest", SyntaxShape::String, "rest value string") .rest("rest", SyntaxShape::String, "rest value string")
.category(Category::Experimental), .category(Category::Experimental),
Signature::build("nu-example-3") Signature::build("nu-example-3")
.desc("Signature test 3 for plugin. Returns labeled error") .usage("Signature test 3 for plugin. Returns labeled error")
.required("a", SyntaxShape::Int, "required integer value") .required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value") .required("b", SyntaxShape::String, "required string value")
.switch("flag", "a flag for the signature", Some('f')) .switch("flag", "a flag for the signature", Some('f'))

View File

@ -5,7 +5,7 @@ use nu_protocol::{Category, Signature, Spanned, SyntaxShape, Value};
impl Plugin for GStat { impl Plugin for GStat {
fn signature(&self) -> Vec<Signature> { fn signature(&self) -> Vec<Signature> {
vec![Signature::build("gstat") vec![Signature::build("gstat")
.desc("Get the git status of a repo") .usage("Get the git status of a repo")
.optional("path", SyntaxShape::Filepath, "path to repo") .optional("path", SyntaxShape::Filepath, "path to repo")
.category(Category::Custom("Prompt".to_string()))] .category(Category::Custom("Prompt".to_string()))]
} }

View File

@ -6,7 +6,7 @@ use nu_protocol::{ast::CellPath, Signature, SyntaxShape, Value};
impl Plugin for Inc { impl Plugin for Inc {
fn signature(&self) -> Vec<Signature> { fn signature(&self) -> Vec<Signature> {
vec![Signature::build("inc") vec![Signature::build("inc")
.desc("Increment a value or version. Optionally use the column of a table.") .usage("Increment a value or version. Optionally use the column of a table.")
.optional("cell_path", SyntaxShape::CellPath, "cell path to update") .optional("cell_path", SyntaxShape::CellPath, "cell path to update")
.switch( .switch(
"major", "major",

View File

@ -6,21 +6,21 @@ impl Plugin for Query {
fn signature(&self) -> Vec<Signature> { fn signature(&self) -> Vec<Signature> {
vec![ vec![
Signature::build("query") Signature::build("query")
.desc("Show all the query commands") .usage("Show all the query commands")
.category(Category::Filters), .category(Category::Filters),
Signature::build("query json") Signature::build("query json")
.desc("execute json query on json file (open --raw <file> | query json 'query string')") .usage("execute json query on json file (open --raw <file> | query json 'query string')")
.required("query", SyntaxShape::String, "json query") .required("query", SyntaxShape::String, "json query")
.category(Category::Filters), .category(Category::Filters),
Signature::build("query xml") Signature::build("query xml")
.desc("execute xpath query on xml") .usage("execute xpath query on xml")
.required("query", SyntaxShape::String, "xpath query") .required("query", SyntaxShape::String, "xpath query")
.category(Category::Filters), .category(Category::Filters),
Signature::build("query web") Signature::build("query web")
.desc("execute selector query on html/web") .usage("execute selector query on html/web")
.named("query", SyntaxShape::String, "selector query", Some('q')) .named("query", SyntaxShape::String, "selector query", Some('q'))
.switch("as-html", "return the query output as html", Some('m')) .switch("as-html", "return the query output as html", Some('m'))
.named( .named(

View File

@ -129,7 +129,7 @@ fn display(model: &Model) -> Result<(), Box<dyn Error>> {
impl Plugin for SubCommand { impl Plugin for SubCommand {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("chart bar") Ok(Signature::build("chart bar")
.desc("Bar charts") .usage("Bar charts")
.switch("acc", "accumulate values", Some('a')) .switch("acc", "accumulate values", Some('a'))
.optional( .optional(
"columns", "columns",

View File

@ -127,7 +127,7 @@ fn display(model: &Model) -> Result<(), Box<dyn Error>> {
impl Plugin for SubCommand { impl Plugin for SubCommand {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("chart line") Ok(Signature::build("chart line")
.desc("Line charts") .usage("Line charts")
.switch("acc", "accumulate values", Some('a')) .switch("acc", "accumulate values", Some('a'))
.optional( .optional(
"columns", "columns",

View File

@ -10,7 +10,7 @@ use nu_source::Tag;
impl Plugin for FromBson { impl Plugin for FromBson {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("from bson") Ok(Signature::build("from bson")
.desc("Convert from .bson binary into table") .usage("Convert from .bson binary into table")
.filter()) .filter())
} }

View File

@ -10,7 +10,7 @@ use nu_source::Tag;
impl Plugin for FromMp4 { impl Plugin for FromMp4 {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("from mp4") Ok(Signature::build("from mp4")
.desc("Get meta-data of mp4 file") .usage("Get meta-data of mp4 file")
.filter()) .filter())
} }

View File

@ -33,7 +33,7 @@ impl Plugin for FromSqlite {
"Only convert specified tables", "Only convert specified tables",
Some('t'), Some('t'),
) )
.desc("Convert from sqlite binary into table") .usage("Convert from sqlite binary into table")
.filter()) .filter())
} }

View File

@ -9,7 +9,7 @@ use crate::handler::s3_helper;
impl Plugin for handler::Handler { impl Plugin for handler::Handler {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("s3") Ok(Signature::build("s3")
.desc("Load S3 resource into a cell, convert to table if possible (avoid by appending '--raw' or '-R')") .usage("Load S3 resource into a cell, convert to table if possible (avoid by appending '--raw' or '-R')")
.required( .required(
"RESOURCE", "RESOURCE",
SyntaxShape::String, SyntaxShape::String,

View File

@ -7,7 +7,7 @@ use crate::start::Start;
impl Plugin for Start { impl Plugin for Start {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("start") Ok(Signature::build("start")
.desc("Opens each file/directory/URL using the default application") .usage("Opens each file/directory/URL using the default application")
.rest( .rest(
"rest", "rest",
SyntaxShape::String, SyntaxShape::String,

View File

@ -10,7 +10,7 @@ use nu_source::Tag;
impl Plugin for ToBson { impl Plugin for ToBson {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("to bson") Ok(Signature::build("to bson")
.desc("Convert table into .bson binary") .usage("Convert table into .bson binary")
.filter()) .filter())
} }

View File

@ -10,7 +10,7 @@ use nu_source::Tag;
impl Plugin for ToSqlite { impl Plugin for ToSqlite {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("to sqlite") Ok(Signature::build("to sqlite")
.desc("Convert table into sqlite binary") .usage("Convert table into sqlite binary")
.filter()) .filter())
} }

View File

@ -7,7 +7,7 @@ use crate::TreeViewer;
impl Plugin for TreeViewer { impl Plugin for TreeViewer {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("tree").desc("View the contents of the pipeline as a tree.")) Ok(Signature::build("tree").usage("View the contents of the pipeline as a tree."))
} }
fn sink(&mut self, _call_info: CallInfo, input: Vec<Value>) { fn sink(&mut self, _call_info: CallInfo, input: Vec<Value>) {

View File

@ -415,7 +415,7 @@ impl Command for Nu {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("nu") Signature::build("nu")
.desc("The nushell language and shell.") .usage("The nushell language and shell.")
.switch("stdin", "redirect the stdin", None) .switch("stdin", "redirect the stdin", None)
.switch("login", "start as a login shell", Some('l')) .switch("login", "start as a login shell", Some('l'))
.switch("interactive", "start as an interactive shell", Some('i')) .switch("interactive", "start as an interactive shell", Some('i'))