From ca0c6eaf58c3bb910bcbf8993ffee4d4eafdc6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 29 Aug 2019 17:52:32 -0500 Subject: [PATCH] This commit introduces a basic help feature. We can go to it with the `help` command to explore and list all commands available. Enter will also try to see if the location to be entered is an existing Nu command, if it is it will let you inspect the command under `help`. This provides baseline needed so we can iterate on it. --- README.md | 10 +- src/cli.rs | 1 + src/commands.rs | 2 + src/commands/autoview.rs | 12 +- src/commands/cd.rs | 21 ++- src/commands/classified.rs | 23 +++ src/commands/clip.rs | 13 +- src/commands/command.rs | 53 ++++-- src/commands/config.rs | 4 + src/commands/cp.rs | 24 ++- src/commands/date.rs | 21 ++- src/commands/debug.rs | 20 +- src/commands/enter.rs | 12 +- src/commands/exit.rs | 21 ++- src/commands/first.rs | 21 ++- src/commands/from_array.rs | 20 +- src/commands/from_bson.rs | 20 +- src/commands/from_csv.rs | 7 +- src/commands/from_ini.rs | 20 +- src/commands/from_json.rs | 7 +- src/commands/from_sqlite.rs | 30 +-- src/commands/from_toml.rs | 20 +- src/commands/from_tsv.rs | 4 + src/commands/from_xml.rs | 20 +- src/commands/from_yaml.rs | 30 +-- src/commands/get.rs | 12 +- src/commands/help.rs | 55 ++++++ src/commands/last.rs | 21 ++- src/commands/lines.rs | 20 +- src/commands/ls.rs | 20 +- src/commands/mkdir.rs | 20 +- src/commands/mv.rs | 4 + src/commands/next.rs | 20 +- src/commands/nth.rs | 20 +- src/commands/open.rs | 4 + src/commands/pick.rs | 4 + src/commands/plugin.rs | 8 + src/commands/post.rs | 4 + src/commands/prev.rs | 20 +- src/commands/ps.rs | 20 +- src/commands/reject.rs | 20 +- src/commands/reverse.rs | 20 +- src/commands/rm.rs | 4 + src/commands/save.rs | 4 + src/commands/shells.rs | 20 +- src/commands/size.rs | 20 +- src/commands/skip_while.rs | 4 + src/commands/sort_by.rs | 20 +- src/commands/split_column.rs | 20 +- src/commands/split_row.rs | 21 ++- src/commands/table.rs | 12 +- src/commands/tags.rs | 20 +- src/commands/to_array.rs | 20 +- src/commands/to_bson.rs | 20 +- src/commands/to_csv.rs | 7 +- src/commands/to_json.rs | 20 +- src/commands/to_sqlite.rs | 30 +-- src/commands/to_toml.rs | 20 +- src/commands/to_tsv.rs | 7 +- src/commands/to_yaml.rs | 20 +- src/commands/trim.rs | 20 +- src/commands/version.rs | 20 +- src/commands/vtable.rs | 12 +- src/commands/where_.rs | 7 +- src/commands/which_.rs | 20 +- src/context.rs | 2 +- src/object.rs | 4 +- src/object/command.rs | 67 +++++++ src/object/dict.rs | 1 - src/parser/hir/baseline_parse_tokens.rs | 18 ++ src/parser/registry.rs | 7 + src/plugins/add.rs | 1 + src/plugins/binaryview.rs | 4 +- src/plugins/edit.rs | 1 + src/plugins/inc.rs | 5 +- src/plugins/skip.rs | 12 +- src/plugins/str.rs | 7 +- src/plugins/sum.rs | 4 +- src/plugins/sys.rs | 4 +- src/plugins/textview.rs | 3 +- src/plugins/tree.rs | 3 +- src/prelude.rs | 1 + src/shell.rs | 1 + src/shell/help_shell.rs | 234 ++++++++++++++++++++++++ src/shell/shell.rs | 1 + tests/filters_test.rs | 157 ++++++++-------- 86 files changed, 1180 insertions(+), 453 deletions(-) create mode 100644 src/commands/help.rs create mode 100644 src/object/command.rs create mode 100644 src/shell/help_shell.rs diff --git a/README.md b/README.md index 1e5e120372..54117e08e3 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | date (--utc) | Get the current datetime | | ps | View current processes | | sys | View information about the current system | +| which filename | Finds a program file. | | open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | | post url body (--user ) (--password ) | Post content to a url and retrieve data as a table if possible | | rm {file or directory} | Remove a file, (for removing directory append '--recursive') | @@ -217,6 +218,8 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | p | Go to previous shell | | n | Go to next shell | | shells | Display the list of current shells | +| help | Display help information about commands | +| version | Display Nu version | ## Filters on tables (structured data) | command | description | @@ -230,8 +233,11 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | add field value | Add a new field to the table | | sum | Sum a column of values | | edit field value | Edit an existing field to have a new value | +| reverse | Reverses the table. | | skip amount | Skip a number of rows | +| skip-while condition | Skips rows while the condition matches. | | first amount | Show only the first number of rows | +| last amount | Show only the last number of rows | | nth row-number | Return only the selected row | | str (field) | Apply string function. Optional use the field of a table | | tags | Read the tags (metadata) for values | @@ -240,13 +246,13 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | to-json | Convert table into .json text | | to-toml | Convert table into .toml text | | to-yaml | Convert table into .yaml text | +| to-bson | Convert table into .bson text | | to-csv | Convert table into .csv text | | to-bson | Convert table into .bson binary data | | to-tsv | Convert table into .tsv text | | to-sqlite | Convert table to sqlite .db binary data | | reverse | Reverse the rows of a table | - ## Filters on text (unstructured data) | command | description | | ------------- | ------------- | @@ -256,7 +262,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | | from-toml | Parse text as .toml and create table | -| from-tsv | Parse text as .tsv and create table | +| from-tsv | Parse text as .tsv and create table | | from-xml | Parse text as .xml and create a table | | from-yaml | Parse text as a .yaml/.yml and create a table | | lines | Split single string into rows, one per line | diff --git a/src/cli.rs b/src/cli.rs index e6592bd540..949c7a359f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -212,6 +212,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Config), whole_stream_command(SkipWhile), per_item_command(Enter), + per_item_command(Help), whole_stream_command(Exit), whole_stream_command(Autoview), per_item_command(Cpy), diff --git a/src/commands.rs b/src/commands.rs index d547422f31..0da8cadbd4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -25,6 +25,7 @@ pub(crate) mod from_tsv; pub(crate) mod from_xml; pub(crate) mod from_yaml; pub(crate) mod get; +pub(crate) mod help; pub(crate) mod last; pub(crate) mod lines; pub(crate) mod ls; @@ -91,6 +92,7 @@ pub(crate) use from_xml::FromXML; pub(crate) use from_yaml::FromYAML; pub(crate) use from_yaml::FromYML; pub(crate) use get::Get; +pub(crate) use help::Help; pub(crate) use last::Last; pub(crate) use lines::Lines; pub(crate) use ls::LS; diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 7d754d9adc..844a093c4a 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -12,6 +12,14 @@ impl WholeStreamCommand for Autoview { "autoview" } + fn signature(&self) -> Signature { + Signature::build("autoview") + } + + fn usage(&self) -> &str { + "View the contents of the pipeline as a table or list." + } + fn run( &self, args: CommandArgs, @@ -19,10 +27,6 @@ impl WholeStreamCommand for Autoview { ) -> Result { Ok(args.process_raw(registry, autoview)?.run()) } - - fn signature(&self) -> Signature { - Signature::build("autoview") - } } pub fn autoview( diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 718f2846f4..a84e66fce9 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -5,6 +5,19 @@ use crate::prelude::*; pub struct CD; impl WholeStreamCommand for CD { + fn name(&self) -> &str { + "cd" + } + + fn signature(&self) -> Signature { + Signature::build("cd") + .optional("directory", SyntaxType::Path) + } + + fn usage(&self) -> &str { + "Change to a new path." + } + fn run( &self, args: CommandArgs, @@ -12,14 +25,6 @@ impl WholeStreamCommand for CD { ) -> Result { cd(args, registry) } - - fn name(&self) -> &str { - "cd" - } - - fn signature(&self) -> Signature { - Signature::build("cd").optional("directory", SyntaxType::Path) - } } fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 1430707637..6babb6c9ce 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -140,6 +140,29 @@ impl InternalCommand { context.add_span_source(uuid, span_source); } CommandAction::Exit => std::process::exit(0), + CommandAction::EnterHelpShell(value) => { + match value { + Tagged { + item: Value::Primitive(Primitive::String(cmd)), + .. + } => { + context.shell_manager.insert_at_current(Box::new( + HelpShell::for_command( + Tagged::from_simple_spanned_item( + Value::string(cmd), + Span::unknown(), + ), + &context.registry().clone(), + )?, + )); + } + _ => { + context.shell_manager.insert_at_current(Box::new( + HelpShell::index(&context.registry().clone())?, + )); + } + } + } CommandAction::EnterValueShell(value) => { context .shell_manager diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 535c8f7450..9bf7fa9f3a 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -18,6 +18,15 @@ pub mod clipboard { fn name(&self) -> &str { "clip" } + + fn signature(&self) -> Signature { + Signature::build("clip") + } + + fn usage(&self) -> &str { + "Copy the contents of the pipeline to the copy/paste buffer" + } + fn run( &self, args: CommandArgs, @@ -25,10 +34,6 @@ pub mod clipboard { ) -> Result { args.process(registry, clip)?.run() } - - fn signature(&self) -> Signature { - Signature::build("clip") - } } pub fn clip( diff --git a/src/commands/command.rs b/src/commands/command.rs index 404d1d6b89..10611b41b0 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -417,6 +417,7 @@ pub enum CommandAction { Exit, EnterShell(String), EnterValueShell(Tagged), + EnterHelpShell(Tagged), PreviousShell, NextShell, LeaveShell, @@ -434,6 +435,9 @@ impl ToDebug for CommandAction { CommandAction::EnterValueShell(t) => { write!(f, "action:enter-value-shell={:?}", t.debug()) } + CommandAction::EnterHelpShell(t) => { + write!(f, "action:enter-help-shell={:?}", t.debug()) + } CommandAction::PreviousShell => write!(f, "action:previous-shell"), CommandAction::NextShell => write!(f, "action:next-shell"), CommandAction::LeaveShell => write!(f, "action:leave-shell"), @@ -488,26 +492,42 @@ impl ReturnSuccess { pub trait WholeStreamCommand: Send + Sync { fn name(&self) -> &str; - fn run( - &self, - args: CommandArgs, - registry: ®istry::CommandRegistry, - ) -> Result; - fn signature(&self) -> Signature { Signature { name: self.name().to_string(), + usage: self.usage().to_string(), positional: vec![], rest_positional: None, named: indexmap::IndexMap::new(), is_filter: true, } } + + fn usage(&self) -> &str; + + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result; } pub trait PerItemCommand: Send + Sync { fn name(&self) -> &str; + fn signature(&self) -> Signature { + Signature { + name: self.name().to_string(), + usage: self.usage().to_string(), + positional: vec![], + rest_positional: None, + named: indexmap::IndexMap::new(), + is_filter: true, + } + } + + fn usage(&self) -> &str; + fn run( &self, call_info: &CallInfo, @@ -515,16 +535,6 @@ pub trait PerItemCommand: Send + Sync { raw_args: &RawCommandArgs, input: Tagged, ) -> Result; - - fn signature(&self) -> Signature { - Signature { - name: self.name().to_string(), - positional: vec![], - rest_positional: None, - named: indexmap::IndexMap::new(), - is_filter: true, - } - } } pub enum Command { @@ -547,6 +557,13 @@ impl Command { } } + pub fn usage(&self) -> &str { + match self { + Command::WholeStream(command) => command.usage(), + Command::PerItem(command) => command.usage(), + } + } + pub fn run(&self, args: CommandArgs, registry: ®istry::CommandRegistry) -> OutputStream { match self { Command::WholeStream(command) => match command.run(args, registry) { @@ -618,6 +635,10 @@ impl WholeStreamCommand for FnFilterCommand { &self.name } + fn usage(&self) -> &str { + "usage" + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/config.rs b/src/commands/config.rs index c41c1d374a..56cce62270 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -32,6 +32,10 @@ impl WholeStreamCommand for Config { .switch("path") } + fn usage(&self) -> &str { + "Configuration management." + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/cp.rs b/src/commands/cp.rs index ee1a289cd8..8160fc9d2d 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -15,16 +15,6 @@ pub struct CopyArgs { } impl PerItemCommand for Cpy { - fn run( - &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Tagged, - ) -> Result { - call_info.process(&raw_args.shell_manager, cp)?.run() - } - fn name(&self) -> &str { "cp" } @@ -36,6 +26,20 @@ impl PerItemCommand for Cpy { .named("file", SyntaxType::Any) .switch("recursive") } + + fn usage(&self) -> &str { + "Copy files." + } + + fn run( + &self, + call_info: &CallInfo, + _registry: &CommandRegistry, + raw_args: &RawCommandArgs, + _input: Tagged, + ) -> Result { + call_info.process(&raw_args.shell_manager, cp)?.run() + } } fn cp(args: CopyArgs, context: &RunnablePerItemContext) -> Result { diff --git a/src/commands/date.rs b/src/commands/date.rs index b979dfe3e7..0e2fee563a 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -12,6 +12,20 @@ use indexmap::IndexMap; pub struct Date; impl WholeStreamCommand for Date { + fn name(&self) -> &str { + "date" + } + + fn signature(&self) -> Signature { + Signature::build("date") + .switch("utc") + .switch("local") + } + + fn usage(&self) -> &str { + "Get the current datetime." + } + fn run( &self, args: CommandArgs, @@ -19,13 +33,6 @@ impl WholeStreamCommand for Date { ) -> Result { date(args, registry) } - fn name(&self) -> &str { - "date" - } - - fn signature(&self) -> Signature { - Signature::build("date").switch("utc").switch("local") - } } pub fn date_to_value(dt: DateTime, span: Span) -> Tagged diff --git a/src/commands/debug.rs b/src/commands/debug.rs index 9562aee38e..80da5fe1c6 100644 --- a/src/commands/debug.rs +++ b/src/commands/debug.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct Debug; impl WholeStreamCommand for Debug { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - debug(args, registry) - } - fn name(&self) -> &str { "debug" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for Debug { fn signature(&self) -> Signature { Signature::build("debug") } + + fn usage(&self) -> &str { + "Debug input fed." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + debug(args, registry) + } } pub fn debug(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 63316b7082..d2170b00d6 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -17,6 +17,10 @@ impl PerItemCommand for Enter { Signature::build("enter").required("location", SyntaxType::Block) } + fn usage(&self) -> &str { + "Create a new shell and begin at this path." + } + fn run( &self, call_info: &CallInfo, @@ -33,7 +37,13 @@ impl PerItemCommand for Enter { } => { let location = location.to_string(); let location_clone = location.to_string(); - if PathBuf::from(location).is_dir() { + + if registry.has(&location) { + Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Value::string(location_clone).tagged(Tag::unknown()), + )))] + .into()) + } else if PathBuf::from(location).is_dir() { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( location_clone, )))] diff --git a/src/commands/exit.rs b/src/commands/exit.rs index ce5f1d19e9..feed8f7c4f 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -6,6 +6,19 @@ use crate::prelude::*; pub struct Exit; impl WholeStreamCommand for Exit { + fn name(&self) -> &str { + "exit" + } + + fn signature(&self) -> Signature { + Signature::build("exit") + .switch("now") + } + + fn usage(&self) -> &str { + "Exit the current shell (or all shells)" + } + fn run( &self, args: CommandArgs, @@ -13,14 +26,6 @@ impl WholeStreamCommand for Exit { ) -> Result { exit(args, registry) } - - fn name(&self) -> &str { - "exit" - } - - fn signature(&self) -> Signature { - Signature::build("exit").switch("now") - } } pub fn exit(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/first.rs b/src/commands/first.rs index 48f1871fd9..af61dc8be5 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -6,6 +6,19 @@ use crate::prelude::*; pub struct First; impl WholeStreamCommand for First { + fn name(&self) -> &str { + "first" + } + + fn signature(&self) -> Signature { + Signature::build("first") + .required("amount", SyntaxType::Literal) + } + + fn usage(&self) -> &str { + "Show only the first number of rows." + } + fn run( &self, args: CommandArgs, @@ -13,14 +26,6 @@ impl WholeStreamCommand for First { ) -> Result { first(args, registry) } - - fn name(&self) -> &str { - "first" - } - - fn signature(&self) -> Signature { - Signature::build("first").required("amount", SyntaxType::Literal) - } } fn first(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/from_array.rs b/src/commands/from_array.rs index 3fb04c9bfd..93ba87ecea 100644 --- a/src/commands/from_array.rs +++ b/src/commands/from_array.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct FromArray; impl WholeStreamCommand for FromArray { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_array(args, registry) - } - fn name(&self) -> &str { "from-array" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for FromArray { fn signature(&self) -> Signature { Signature::build("from-array") } + + fn usage(&self) -> &str { + "Expand an array/list into rows" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_array(args, registry) + } } fn from_array(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 6b5ed571ce..0d5f05024f 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -7,14 +7,6 @@ use std::str::FromStr; pub struct FromBSON; impl WholeStreamCommand for FromBSON { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_bson(args, registry) - } - fn name(&self) -> &str { "from-bson" } @@ -22,6 +14,18 @@ impl WholeStreamCommand for FromBSON { fn signature(&self) -> Signature { Signature::build("from-bson") } + + fn usage(&self) -> &str { + "Parse text as .bson and create table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_bson(args, registry) + } } fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged { diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 97aa981249..9f4a96da3e 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -16,7 +16,12 @@ impl WholeStreamCommand for FromCSV { } fn signature(&self) -> Signature { - Signature::build("from-csv").switch("headerless") + Signature::build("from-csv") + .switch("headerless") + } + + fn usage(&self) -> &str { + "Parse text as .csv and create table" } fn run( diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index d1f7bb4f8a..8495c4b221 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -6,14 +6,6 @@ use std::collections::HashMap; pub struct FromINI; impl WholeStreamCommand for FromINI { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_ini(args, registry) - } - fn name(&self) -> &str { "from-ini" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for FromINI { fn signature(&self) -> Signature { Signature::build("from-ini") } + + fn usage(&self) -> &str { + "Parse text as .ini and create table" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_ini(args, registry) + } } fn convert_ini_second_to_nu_value( diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index b4b1f7980e..e902d0a3de 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -15,7 +15,12 @@ impl WholeStreamCommand for FromJSON { } fn signature(&self) -> Signature { - Signature::build("from-json").switch("objects") + Signature::build("from-json") + .switch("objects") + } + + fn usage(&self) -> &str { + "Parse text as .json and create table." } fn run( diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index bc288ff396..88bd30375e 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -9,14 +9,6 @@ use std::path::Path; pub struct FromSQLite; impl WholeStreamCommand for FromSQLite { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_sqlite(args, registry) - } - fn name(&self) -> &str { "from-sqlite" } @@ -24,11 +16,11 @@ impl WholeStreamCommand for FromSQLite { fn signature(&self) -> Signature { Signature::build("from-sqlite") } -} -pub struct FromDB; + fn usage(&self) -> &str { + "Parse binary data as sqlite .db and create table." + } -impl WholeStreamCommand for FromDB { fn run( &self, args: CommandArgs, @@ -36,7 +28,11 @@ impl WholeStreamCommand for FromDB { ) -> Result { from_sqlite(args, registry) } +} +pub struct FromDB; + +impl WholeStreamCommand for FromDB { fn name(&self) -> &str { "from-db" } @@ -44,6 +40,18 @@ impl WholeStreamCommand for FromDB { fn signature(&self) -> Signature { Signature::build("from-db") } + + fn usage(&self) -> &str { + "Parse binary data as db and create table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_sqlite(args, registry) + } } pub fn convert_sqlite_file_to_nu_value( diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index 6df13d6860..b4b341ecaa 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct FromTOML; impl WholeStreamCommand for FromTOML { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_toml(args, registry) - } - fn name(&self) -> &str { "from-toml" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for FromTOML { fn signature(&self) -> Signature { Signature::build("from-toml") } + + fn usage(&self) -> &str { + "Parse text as .toml and create table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_toml(args, registry) + } } pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> Tagged { diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index ed37bf4531..366621d435 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -19,6 +19,10 @@ impl WholeStreamCommand for FromTSV { Signature::build("from-tsv").switch("headerless") } + fn usage(&self) -> &str { + "Parse text as .tsv and create table." + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index adf39240de..4e4be72bc2 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct FromXML; impl WholeStreamCommand for FromXML { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_xml(args, registry) - } - fn name(&self) -> &str { "from-xml" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for FromXML { fn signature(&self) -> Signature { Signature::build("from-xml") } + + fn usage(&self) -> &str { + "Parse text as .xml and create table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_xml(args, registry) + } } fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>, tag: impl Into) -> Tagged { diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index a5f229a033..e67c045038 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct FromYAML; impl WholeStreamCommand for FromYAML { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_yaml(args, registry) - } - fn name(&self) -> &str { "from-yaml" } @@ -20,11 +12,11 @@ impl WholeStreamCommand for FromYAML { fn signature(&self) -> Signature { Signature::build("from-yaml") } -} -pub struct FromYML; + fn usage(&self) -> &str { + "Parse text as .yaml/.yml and create table." + } -impl WholeStreamCommand for FromYML { fn run( &self, args: CommandArgs, @@ -32,7 +24,11 @@ impl WholeStreamCommand for FromYML { ) -> Result { from_yaml(args, registry) } +} +pub struct FromYML; + +impl WholeStreamCommand for FromYML { fn name(&self) -> &str { "from-yml" } @@ -40,6 +36,18 @@ impl WholeStreamCommand for FromYML { fn signature(&self) -> Signature { Signature::build("from-yml") } + + fn usage(&self) -> &str { + "Parse text as .yaml/.yml and create table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_yaml(args, registry) + } } fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into) -> Tagged { diff --git a/src/commands/get.rs b/src/commands/get.rs index cd8db615a7..7f0512a7bc 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -15,6 +15,14 @@ impl WholeStreamCommand for Get { "get" } + fn signature(&self) -> Signature { + Signature::build("get").rest(SyntaxType::Member) + } + + fn usage(&self) -> &str { + "Open given cells as text." + } + fn run( &self, args: CommandArgs, @@ -22,10 +30,6 @@ impl WholeStreamCommand for Get { ) -> Result { args.process(registry, get)?.run() } - - fn signature(&self) -> Signature { - Signature::build("get").rest(SyntaxType::Member) - } } fn get_member(path: &Tagged, obj: &Tagged) -> Result, ShellError> { diff --git a/src/commands/help.rs b/src/commands/help.rs new file mode 100644 index 0000000000..def2f33385 --- /dev/null +++ b/src/commands/help.rs @@ -0,0 +1,55 @@ +use crate::commands::command::CommandAction; +use crate::commands::PerItemCommand; +use crate::errors::ShellError; +use crate::parser::registry; +use crate::prelude::*; + +pub struct Help; + +impl PerItemCommand for Help { + fn name(&self) -> &str { + "help" + } + + fn signature(&self) -> registry::Signature { + Signature::build("help").rest(SyntaxType::Any) + } + + fn usage(&self) -> &str { + "Display help information about commands." + } + + fn run( + &self, + call_info: &CallInfo, + _registry: &CommandRegistry, + _raw_args: &RawCommandArgs, + _input: Tagged, + ) -> Result { + let span = call_info.name_span; + + if call_info.args.len() == 0 { + return Ok( + vec![ + Ok(ReturnSuccess::Action( + CommandAction::EnterHelpShell( + Tagged::from_simple_spanned_item(Value::nothing(), span) + )))].into() + ) + } + + match call_info.args.expect_nth(0)? { + Tagged { + item: Value::Primitive(Primitive::String(document)), + .. + } => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Tagged::from_simple_spanned_item(Value::string(document), span) + )))] + .into()), + x => Ok( + vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(x.clone())))] + .into(), + ), + } + } +} diff --git a/src/commands/last.rs b/src/commands/last.rs index f776b7d654..29fe773775 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -6,6 +6,19 @@ use crate::prelude::*; pub struct Last; impl WholeStreamCommand for Last { + fn name(&self) -> &str { + "last" + } + + fn signature(&self) -> Signature { + Signature::build("last") + .required("amount", SyntaxType::Number) + } + + fn usage(&self) -> &str { + "Show only the last number of rows." + } + fn run( &self, args: CommandArgs, @@ -13,14 +26,6 @@ impl WholeStreamCommand for Last { ) -> Result { last(args, registry) } - - fn name(&self) -> &str { - "last" - } - - fn signature(&self) -> Signature { - Signature::build("last").required("amount", SyntaxType::Literal) - } } fn last(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/lines.rs b/src/commands/lines.rs index e6ce32b3f2..0195177542 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -7,14 +7,6 @@ use log::trace; pub struct Lines; impl WholeStreamCommand for Lines { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - lines(args, registry) - } - fn name(&self) -> &str { "lines" } @@ -22,6 +14,18 @@ impl WholeStreamCommand for Lines { fn signature(&self) -> Signature { Signature::build("lines") } + + fn usage(&self) -> &str { + "Split single string into rows, one per line." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + lines(args, registry) + } } // TODO: "Amount remaining" wrapper diff --git a/src/commands/ls.rs b/src/commands/ls.rs index bc020c17d0..7096141507 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct LS; impl WholeStreamCommand for LS { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - ls(args, registry) - } - fn name(&self) -> &str { "ls" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for LS { fn signature(&self) -> Signature { Signature::build("ls").optional("path", SyntaxType::Path) } + + fn usage(&self) -> &str { + "View the contents of the current or given path." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + ls(args, registry) + } } fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 96fd16afaf..9dec9a3142 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -12,6 +12,18 @@ pub struct MkdirArgs { } impl PerItemCommand for Mkdir { + fn name(&self) -> &str { + "mkdir" + } + + fn signature(&self) -> Signature { + Signature::build("mkdir").rest(SyntaxType::Path) + } + + fn usage(&self) -> &str { + "Make directories, creates intermediary directories as required." + } + fn run( &self, call_info: &CallInfo, @@ -21,14 +33,6 @@ impl PerItemCommand for Mkdir { ) -> Result { call_info.process(&raw_args.shell_manager, mkdir)?.run() } - - fn name(&self) -> &str { - "mkdir" - } - - fn signature(&self) -> Signature { - Signature::build("mkdir").rest(SyntaxType::Path) - } } fn mkdir(args: MkdirArgs, context: &RunnablePerItemContext) -> Result { diff --git a/src/commands/mv.rs b/src/commands/mv.rs index af93dde382..130e5996e8 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -25,6 +25,10 @@ impl PerItemCommand for Move { .named("file", SyntaxType::Any) } + fn usage(&self) -> &str { + "Move files or directories." + } + fn run( &self, call_info: &CallInfo, diff --git a/src/commands/next.rs b/src/commands/next.rs index de0d3dbe24..359f262312 100644 --- a/src/commands/next.rs +++ b/src/commands/next.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Next; impl WholeStreamCommand for Next { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - next(args, registry) - } - fn name(&self) -> &str { "n" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Next { fn signature(&self) -> Signature { Signature::build("n") } + + fn usage(&self) -> &str { + "Go to next shell." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + next(args, registry) + } } fn next(_args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/nth.rs b/src/commands/nth.rs index 5651d84123..98ab6a10a9 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -11,14 +11,6 @@ struct NthArgs { pub struct Nth; impl WholeStreamCommand for Nth { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, nth)?.run() - } - fn name(&self) -> &str { "nth" } @@ -26,6 +18,18 @@ impl WholeStreamCommand for Nth { fn signature(&self) -> Signature { Signature::build("nth").required("amount", SyntaxType::Any) } + + fn usage(&self) -> &str { + "Return only the selected row" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, nth)?.run() + } } fn nth( diff --git a/src/commands/open.rs b/src/commands/open.rs index 205ad62ff7..202f8eae18 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -23,6 +23,10 @@ impl PerItemCommand for Open { .switch("raw") } + fn usage(&self) -> &str { + "Load a file into a cell, convert to table if possible (avoid by appending '--raw')" + } + fn run( &self, call_info: &CallInfo, diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 6c7fca8f25..927edf8b3d 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -20,6 +20,10 @@ impl WholeStreamCommand for Pick { Signature::build("pick").rest(SyntaxType::Any) } + fn usage(&self) -> &str { + "Down-select table to only these columns." + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index bd25db852b..a14cb1a609 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -51,6 +51,10 @@ impl WholeStreamCommand for PluginCommand { self.config.clone() } + fn usage(&self) -> &str { + &self.config.usage + } + fn run( &self, args: CommandArgs, @@ -270,6 +274,10 @@ impl WholeStreamCommand for PluginSink { self.config.clone() } + fn usage(&self) -> &str { + &self.config.usage + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/post.rs b/src/commands/post.rs index adab330248..993605eb1f 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -27,6 +27,10 @@ impl PerItemCommand for Post { .switch("raw") } + fn usage(&self) -> &str { + "Post content to a url and retrieve data as a table if possible." + } + fn run( &self, call_info: &CallInfo, diff --git a/src/commands/prev.rs b/src/commands/prev.rs index 4291e93802..970cce3436 100644 --- a/src/commands/prev.rs +++ b/src/commands/prev.rs @@ -7,14 +7,6 @@ use crate::commands::WholeStreamCommand; pub struct Previous; impl WholeStreamCommand for Previous { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - previous(args, registry) - } - fn name(&self) -> &str { "p" } @@ -22,6 +14,18 @@ impl WholeStreamCommand for Previous { fn signature(&self) -> Signature { Signature::build("p") } + + fn usage(&self) -> &str { + "Go to previous shell." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + previous(args, registry) + } } fn previous(_args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 15a19a8578..b11e797961 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -12,14 +12,6 @@ use heim::units::{ratio, Ratio}; pub struct PS; impl WholeStreamCommand for PS { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - ps(args, registry) - } - fn name(&self) -> &str { "ps" } @@ -27,6 +19,18 @@ impl WholeStreamCommand for PS { fn signature(&self) -> Signature { Signature::build("ps") } + + fn usage(&self) -> &str { + "View current processes." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + ps(args, registry) + } } async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio)> { diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 92aff1cb2e..b519f8ea64 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -11,14 +11,6 @@ pub struct RejectArgs { pub struct Reject; impl WholeStreamCommand for Reject { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, reject)?.run() - } - fn name(&self) -> &str { "reject" } @@ -26,6 +18,18 @@ impl WholeStreamCommand for Reject { fn signature(&self) -> Signature { Signature::build("reject").rest(SyntaxType::Member) } + + fn usage(&self) -> &str { + "Remove the given columns from the table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, reject)?.run() + } } fn reject( diff --git a/src/commands/reverse.rs b/src/commands/reverse.rs index 2b43f01470..8b81297249 100644 --- a/src/commands/reverse.rs +++ b/src/commands/reverse.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Reverse; impl WholeStreamCommand for Reverse { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - reverse(args, registry) - } - fn name(&self) -> &str { "reverse" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Reverse { fn signature(&self) -> Signature { Signature::build("reverse") } + + fn usage(&self) -> &str { + "Reverses the table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + reverse(args, registry) + } } fn reverse(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 36f7aa6d8a..60bb5c6e4a 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -24,6 +24,10 @@ impl PerItemCommand for Remove { .switch("recursive") } + fn usage(&self) -> &str { + "Remove a file, (for removing directory append '--recursive')" + } + fn run( &self, call_info: &CallInfo, diff --git a/src/commands/save.rs b/src/commands/save.rs index 953d90f966..4723c4bda6 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -23,6 +23,10 @@ impl WholeStreamCommand for Save { .switch("raw") } + fn usage(&self) -> &str { + "Save the contents of the pipeline to a file." + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/shells.rs b/src/commands/shells.rs index f31a16fd56..5e0159e147 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Shells; impl WholeStreamCommand for Shells { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - shells(args, registry) - } - fn name(&self) -> &str { "shells" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Shells { fn signature(&self) -> Signature { Signature::build("shells") } + + fn usage(&self) -> &str { + "Display the list of current shells." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + shells(args, registry) + } } fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/size.rs b/src/commands/size.rs index 2af7d47e89..02da0460cb 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Size; impl WholeStreamCommand for Size { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - size(args, registry) - } - fn name(&self) -> &str { "size" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Size { fn signature(&self) -> Signature { Signature::build("size") } + + fn usage(&self) -> &str { + "Gather word count statistics on the text." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + size(args, registry) + } } fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index d0237b16ad..90ba24b996 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -20,6 +20,10 @@ impl WholeStreamCommand for SkipWhile { .filter() } + fn usage(&self) -> &str { + "Skips rows while the condition matches." + } + fn run( &self, args: CommandArgs, diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index c538fdf97f..edce33b963 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -10,14 +10,6 @@ pub struct SortByArgs { } impl WholeStreamCommand for SortBy { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, sort_by)?.run() - } - fn name(&self) -> &str { "sort-by" } @@ -25,6 +17,18 @@ impl WholeStreamCommand for SortBy { fn signature(&self) -> Signature { Signature::build("sort-by").rest(SyntaxType::String) } + + fn usage(&self) -> &str { + "Sort by the given columns." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, sort_by)?.run() + } } fn sort_by( diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index ab5e7f5783..4bde5e25b0 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -15,14 +15,6 @@ struct SplitColumnArgs { pub struct SplitColumn; impl WholeStreamCommand for SplitColumn { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, split_column)?.run() - } - fn name(&self) -> &str { "split-column" } @@ -33,6 +25,18 @@ impl WholeStreamCommand for SplitColumn { .switch("collapse-empty") .rest(SyntaxType::Member) } + + fn usage(&self) -> &str { + "Split row contents across multiple columns via the separator." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, split_column)?.run() + } } fn split_column( diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index 592d301bea..fa95225f4f 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -12,6 +12,19 @@ struct SplitRowArgs { pub struct SplitRow; impl WholeStreamCommand for SplitRow { + fn name(&self) -> &str { + "split-row" + } + + fn signature(&self) -> Signature { + Signature::build("split-row") + .required("separator", SyntaxType::Any) + } + + fn usage(&self) -> &str { + "Split row contents over multiple rows via the separator." + } + fn run( &self, args: CommandArgs, @@ -19,14 +32,6 @@ impl WholeStreamCommand for SplitRow { ) -> Result { args.process(registry, split_row)?.run() } - - fn name(&self) -> &str { - "split-row" - } - - fn signature(&self) -> Signature { - Signature::build("split-row").required("separator", SyntaxType::Any) - } } fn split_row( diff --git a/src/commands/table.rs b/src/commands/table.rs index 0f58e34bf4..4efd6f821f 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -13,6 +13,15 @@ impl WholeStreamCommand for Table { fn name(&self) -> &str { "table" } + + fn signature(&self) -> Signature { + Signature::build("table") + } + + fn usage(&self) -> &str { + "View the contents of the pipeline as a table." + } + fn run( &self, args: CommandArgs, @@ -20,9 +29,6 @@ impl WholeStreamCommand for Table { ) -> Result { args.process(registry, table)?.run() } - fn signature(&self) -> Signature { - Signature::build("table") - } } pub fn table(_args: TableArgs, context: RunnableContext) -> Result { diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 01c7565d55..973105709c 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Tags; impl WholeStreamCommand for Tags { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - tags(args, registry) - } - fn name(&self) -> &str { "tags" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Tags { fn signature(&self) -> Signature { Signature::build("tags") } + + fn usage(&self) -> &str { + "Read the tags (metadata) for values." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + tags(args, registry) + } } fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index 43984ce58b..04c429e1b4 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct ToArray; impl WholeStreamCommand for ToArray { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_array(args, registry) - } - fn name(&self) -> &str { "to-array" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for ToArray { fn signature(&self) -> Signature { Signature::build("to-array") } + + fn usage(&self) -> &str { + "Collapse rows into a single list." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_array(args, registry) + } } fn to_array(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 025484d8d3..64ba9aa4af 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -7,14 +7,6 @@ use std::convert::TryInto; pub struct ToBSON; impl WholeStreamCommand for ToBSON { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_bson(args, registry) - } - fn name(&self) -> &str { "to-bson" } @@ -22,6 +14,18 @@ impl WholeStreamCommand for ToBSON { fn signature(&self) -> Signature { Signature::build("to-bson") } + + fn usage(&self) -> &str { + "Convert table into .bson text." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_bson(args, registry) + } } pub fn value_to_bson_value(v: &Value) -> Bson { diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 65651396ab..58ad208192 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -16,7 +16,12 @@ impl WholeStreamCommand for ToCSV { } fn signature(&self) -> Signature { - Signature::build("to-csv").switch("headerless") + Signature::build("to-csv") + .switch("headerless") + } + + fn usage(&self) -> &str { + "Convert table into .csv text " } fn run( diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 3559459cdc..a1c0e22987 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct ToJSON; impl WholeStreamCommand for ToJSON { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_json(args, registry) - } - fn name(&self) -> &str { "to-json" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for ToJSON { fn signature(&self) -> Signature { Signature::build("to-json") } + + fn usage(&self) -> &str { + "Convert table into .json text" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_json(args, registry) + } } pub fn value_to_json_value(v: &Value) -> serde_json::Value { diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index fcbf96e3b3..dca953c235 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -8,14 +8,6 @@ use std::io::Read; pub struct ToSQLite; impl WholeStreamCommand for ToSQLite { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_sqlite(args, registry) - } - fn name(&self) -> &str { "to-sqlite" } @@ -23,11 +15,11 @@ impl WholeStreamCommand for ToSQLite { fn signature(&self) -> Signature { Signature::build("to-sqlite") } -} -pub struct ToDB; + fn usage(&self) -> &str { + "Convert table to sqlite .db binary data" + } -impl WholeStreamCommand for ToDB { fn run( &self, args: CommandArgs, @@ -35,7 +27,11 @@ impl WholeStreamCommand for ToDB { ) -> Result { to_sqlite(args, registry) } +} +pub struct ToDB; + +impl WholeStreamCommand for ToDB { fn name(&self) -> &str { "to-db" } @@ -43,6 +39,18 @@ impl WholeStreamCommand for ToDB { fn signature(&self) -> Signature { Signature::build("to-db") } + + fn usage(&self) -> &str { + "Convert table to db data" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_sqlite(args, registry) + } } fn comma_concat(acc: String, current: String) -> String { diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 5d60807390..d8216f9004 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct ToTOML; impl WholeStreamCommand for ToTOML { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_toml(args, registry) - } - fn name(&self) -> &str { "to-toml" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for ToTOML { fn signature(&self) -> Signature { Signature::build("to-toml") } + + fn usage(&self) -> &str { + "Convert table into .toml text" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_toml(args, registry) + } } pub fn value_to_toml_value(v: &Value) -> Result { diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index b999ce12d6..1a229d768e 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -16,7 +16,12 @@ impl WholeStreamCommand for ToTSV { } fn signature(&self) -> Signature { - Signature::build("to-tsv").switch("headerless") + Signature::build("to-tsv") + .switch("headerless") + } + + fn usage(&self) -> &str { + "Convert table into .tsv text" } fn run( diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index f0aa0219e0..9b91cbc0cc 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -5,14 +5,6 @@ use crate::prelude::*; pub struct ToYAML; impl WholeStreamCommand for ToYAML { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_yaml(args, registry) - } - fn name(&self) -> &str { "to-yaml" } @@ -20,6 +12,18 @@ impl WholeStreamCommand for ToYAML { fn signature(&self) -> Signature { Signature::build("to-yaml") } + + fn usage(&self) -> &str { + "Convert table into .yaml/.yml text" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_yaml(args, registry) + } } pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 22a29a36b6..66152843f0 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -6,14 +6,6 @@ use crate::prelude::*; pub struct Trim; impl WholeStreamCommand for Trim { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - trim(args, registry) - } - fn name(&self) -> &str { "trim" } @@ -21,6 +13,18 @@ impl WholeStreamCommand for Trim { fn signature(&self) -> Signature { Signature::build("trim") } + + fn usage(&self) -> &str { + "Trim leading and following whitespace from text data." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + trim(args, registry) + } } fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result { diff --git a/src/commands/version.rs b/src/commands/version.rs index e13017ab51..def335420a 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -8,14 +8,6 @@ use indexmap::IndexMap; pub struct Version; impl WholeStreamCommand for Version { - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - date(args, registry) - } - fn name(&self) -> &str { "version" } @@ -23,6 +15,18 @@ impl WholeStreamCommand for Version { fn signature(&self) -> Signature { Signature::build("version") } + + fn usage(&self) -> &str { + "Display Nu version" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + date(args, registry) + } } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/vtable.rs b/src/commands/vtable.rs index 4e26c05efd..5abd4c6d1f 100644 --- a/src/commands/vtable.rs +++ b/src/commands/vtable.rs @@ -12,6 +12,15 @@ impl WholeStreamCommand for VTable { fn name(&self) -> &str { "vtable" } + + fn signature(&self) -> Signature { + Signature::build("vtable") + } + + fn usage(&self) -> &str { + "View the contents of the pipeline as a vertical (rotated) table." + } + fn run( &self, args: CommandArgs, @@ -19,9 +28,6 @@ impl WholeStreamCommand for VTable { ) -> Result { args.process(registry, vtable)?.run() } - fn signature(&self) -> Signature { - Signature::build("vtable") - } } pub fn vtable(_args: VTableArgs, context: RunnableContext) -> Result { diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 5f037ded43..b09c341b4a 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -12,7 +12,12 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where").required("condition", SyntaxType::Block) + Signature::build("where") + .required("condition", SyntaxType::Block) + } + + fn usage(&self) -> &str { + "Filter table to match the condition." } fn run( diff --git a/src/commands/which_.rs b/src/commands/which_.rs index 0ffbc19dba..fdd722d892 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -8,6 +8,19 @@ use crate::parser::registry::Signature; pub struct Which; impl WholeStreamCommand for Which { + fn name(&self) -> &str { + "which" + } + + fn signature(&self) -> Signature { + Signature::build("which") + .required("name", SyntaxType::Any) + } + + fn usage(&self) -> &str { + "Finds a program file." + } + fn run( &self, args: CommandArgs, @@ -15,13 +28,6 @@ impl WholeStreamCommand for Which { ) -> Result { which(args, registry) } - fn name(&self) -> &str { - "which" - } - - fn signature(&self) -> Signature { - Signature::build("which").required("name", SyntaxType::Any) - } } pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/context.rs b/src/context.rs index 5bade25f21..577a55c20d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -53,7 +53,7 @@ impl CommandRegistry { registry.get(name).map(|c| c.clone()) } - fn has(&self, name: &str) -> bool { + pub(crate) fn has(&self, name: &str) -> bool { let registry = self.registry.lock().unwrap(); registry.contains_key(name) diff --git a/src/object.rs b/src/object.rs index dc5ecfc085..e2a8df1020 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,4 +1,5 @@ pub(crate) mod base; +pub(crate) mod command; pub(crate) mod config; pub(crate) mod dict; pub(crate) mod files; @@ -8,5 +9,6 @@ pub(crate) mod types; #[allow(unused)] pub(crate) use base::{Block, Primitive, Switch, Value}; -pub(crate) use dict::{Dictionary, TaggedDictBuilder}; +pub(crate) use dict::{Dictionary, TaggedListBuilder, TaggedDictBuilder}; pub(crate) use files::dir_entry_dict; +pub(crate) use command::command_dict; diff --git a/src/object/command.rs b/src/object/command.rs new file mode 100644 index 0000000000..317fe5fa34 --- /dev/null +++ b/src/object/command.rs @@ -0,0 +1,67 @@ +use crate::commands::command::Command; +use crate::object::{TaggedDictBuilder, TaggedListBuilder, Value}; +use crate::parser::registry::{NamedType, PositionalType, Signature}; +use crate::prelude::*; +use std::ops::Deref; + +pub(crate) fn command_dict(command: Arc, tag: impl Into) -> Tagged { + let tag = tag.into(); + + let mut cmd_dict = TaggedDictBuilder::new(tag); + + cmd_dict.insert("name", Value::string(command.name())); + + cmd_dict.insert( + "type", + Value::string(match command.deref() { + Command::WholeStream(_) => "Command", + Command::PerItem(_) => "Filter", + }), + ); + + cmd_dict.insert_tagged("signature", signature_dict(command.signature(), tag)); + cmd_dict.insert("usage", Value::string(command.usage())); + + cmd_dict.into_tagged_value() +} + +fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into) -> Tagged { + let tag = tag.into(); + + let mut spec = TaggedDictBuilder::new(tag); + + spec.insert("name", Value::string(name)); + spec.insert("type", Value::string(ty)); + spec.insert("required", Value::string(if required { "yes" } else { "no" })); + + spec.into_tagged_value() +} + +fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { + let tag = tag.into(); + let mut sig = TaggedListBuilder::new(tag); + + for arg in signature.positional.iter() { + let is_required = match arg { + PositionalType::Mandatory(_,_) => true, + PositionalType::Optional(_,_) => false, + }; + + sig.insert_tagged(for_spec(arg.name(), "argument", is_required, tag)); + } + + if let Some(_) = signature.rest_positional { + let is_required = false; + sig.insert_tagged(for_spec("rest", "argument", is_required, tag)); + } + + for (name, ty) in signature.named.iter() { + match ty { + NamedType::Mandatory(_) => sig.insert_tagged(for_spec(name, "flag", true, tag)), + NamedType::Optional(_) => sig.insert_tagged(for_spec(name, "flag", false, tag)), + NamedType::Switch => sig.insert_tagged(for_spec(name, "switch", false, tag)), + } + } + + sig.into_tagged_value() +} diff --git a/src/object/dict.rs b/src/object/dict.rs index b090e4ef71..2f85fcfa65 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -1,5 +1,4 @@ use crate::prelude::*; - use crate::object::{Primitive, Value}; use derive_new::new; use indexmap::IndexMap; diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index a1119bc765..89bffc3f5f 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -49,6 +49,24 @@ pub enum SyntaxType { Boolean, } +impl std::fmt::Display for SyntaxType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SyntaxType::Any => write!(f, "Any"), + SyntaxType::List => write!(f, "List"), + SyntaxType::Literal => write!(f, "Literal"), + SyntaxType::String => write!(f, "String"), + SyntaxType::Member => write!(f, "Member"), + SyntaxType::Variable => write!(f, "Variable"), + SyntaxType::Number => write!(f, "Number"), + SyntaxType::Path => write!(f, "Path"), + SyntaxType::Binary => write!(f, "Binary"), + SyntaxType::Block => write!(f, "Block"), + SyntaxType::Boolean => write!(f, "Boolean") + } + } +} + pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, context: &Context, diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 52d49ea5c0..7d22e4f85d 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -73,6 +73,8 @@ impl PositionalType { pub struct Signature { pub name: String, #[new(default)] + pub usage: String, + #[new(default)] pub positional: Vec, #[new(value = "None")] pub rest_positional: Option, @@ -87,6 +89,11 @@ impl Signature { Signature::new(name.into()) } + pub fn desc(mut self, usage: impl Into) -> Signature { + self.usage = usage.into(); + self + } + pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Mandatory(name.into(), ty.into())); diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 93c519212e..27a12e677e 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -43,6 +43,7 @@ impl Add { impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") + .desc("Add a new field to the table.") .required("Field", SyntaxType::String) .required("Value", SyntaxType::String) .rest(SyntaxType::String).filter()) diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index fb9c2213a2..7601621cd1 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -12,7 +12,9 @@ impl BinaryView { impl Plugin for BinaryView { fn config(&mut self) -> Result { - Ok(Signature::build("binaryview").switch("lores")) + Ok(Signature::build("binaryview") + .desc("Autoview of binary data.") + .switch("lores")) } fn sink(&mut self, call_info: CallInfo, input: Vec>) { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index 902c038bc1..ab8a25aa68 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -42,6 +42,7 @@ impl Edit { impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") + .desc("Edit an existing field to have a new value.") .required("Field", SyntaxType::String) .required("Value", SyntaxType::String) .filter()) diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index ece83b7f21..5e6a2a293f 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -70,7 +70,7 @@ impl Inc { self.error = Some(message.to_string()); } - fn usage(&self) -> &'static str { + pub fn usage() -> &'static str { "Usage: inc field [--major|--minor|--patch]" } @@ -116,6 +116,7 @@ impl Inc { impl Plugin for Inc { fn config(&mut self) -> Result { Ok(Signature::build("inc") + .desc("Increment a value or version. Optional use the field of a table.") .switch("major") .switch("minor") .switch("patch") @@ -159,7 +160,7 @@ impl Plugin for Inc { match &self.error { Some(reason) => { - return Err(ShellError::string(format!("{}: {}", reason, self.usage()))) + return Err(ShellError::string(format!("{}: {}", reason, Inc::usage()))) } None => Ok(vec![]), } diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index 6f78351fee..bb80534218 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,4 +1,3 @@ -use indexmap::IndexMap; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxType, Tagged, Value, @@ -15,13 +14,10 @@ impl Skip { impl Plugin for Skip { fn config(&mut self) -> Result { - Ok(Signature { - name: "skip".to_string(), - positional: vec![], - is_filter: true, - named: IndexMap::new(), - rest_positional: Some(SyntaxType::Number), - }) + Ok(Signature::build("skip") + .desc("Skip a number of rows") + .rest(SyntaxType::Number) + .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if let Some(args) = call_info.args.positional { diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 0656d5f780..8aa24b4907 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -124,7 +124,7 @@ impl Str { } } - fn usage(&self) -> &'static str { + pub fn usage() -> &'static str { "Usage: str field [--downcase|--upcase|--to-int|--replace|--find-replace]" } } @@ -154,7 +154,7 @@ impl Str { None => Err(ShellError::string(format!( "{}: {}", "str needs a field when applying it to a value in an object", - self.usage() + Str::usage() ))), }, x => Err(ShellError::string(format!( @@ -168,6 +168,7 @@ impl Str { impl Plugin for Str { fn config(&mut self) -> Result { Ok(Signature::build("str") + .desc("Apply string function. Optional use the field of a table") .switch("downcase") .switch("upcase") .switch("to-int") @@ -240,7 +241,7 @@ impl Plugin for Str { match &self.error { Some(reason) => { - return Err(ShellError::string(format!("{}: {}", reason, self.usage()))) + return Err(ShellError::string(format!("{}: {}", reason, Str::usage()))) } None => Ok(vec![]), } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index 1c983b1ad8..bd30cef98e 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -64,7 +64,9 @@ impl Sum { impl Plugin for Sum { fn config(&mut self) -> Result { - Ok(Signature::build("sum").filter()) + Ok(Signature::build("sum") + .desc("Sum a column of values.") + .filter()) } fn begin_filter(&mut self, _: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index 74d87befe0..e7ff919d8f 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -309,7 +309,9 @@ async fn sysinfo(tag: Tag) -> Vec> { impl Plugin for Sys { fn config(&mut self) -> Result { - Ok(Signature::build("sys").filter()) + Ok(Signature::build("sys") + .desc("View information about the current system.") + .filter()) } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 81097179a5..81db9dd194 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -26,7 +26,8 @@ impl TextView { impl Plugin for TextView { fn config(&mut self) -> Result { - Ok(Signature::build("textview")) + Ok(Signature::build("textview") + .desc("Autoview of text data.")) } fn sink(&mut self, call_info: CallInfo, input: Vec>) { diff --git a/src/plugins/tree.rs b/src/plugins/tree.rs index 9784e854c3..f57a705eca 100644 --- a/src/plugins/tree.rs +++ b/src/plugins/tree.rs @@ -80,7 +80,8 @@ struct TreeViewer; impl Plugin for TreeViewer { fn config(&mut self) -> Result { - Ok(Signature::build("tree")) + Ok(Signature::build("tree") + .desc("View the contents of the pipeline as a tree.")) } fn sink(&mut self, _call_info: CallInfo, input: Vec>) { diff --git a/src/prelude.rs b/src/prelude.rs index 8e511f82cd..d781ff22ce 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -68,6 +68,7 @@ pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::value_shell::ValueShell; +pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::stream::{InputStream, OutputStream}; pub(crate) use crate::traits::{HasSpan, ToDebug}; pub(crate) use crate::Span; diff --git a/src/shell.rs b/src/shell.rs index 77670c5ec7..caa1443ac7 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -4,5 +4,6 @@ pub(crate) mod helper; pub(crate) mod shell; pub(crate) mod shell_manager; pub(crate) mod value_shell; +pub(crate) mod help_shell; pub(crate) use helper::Helper; diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs new file mode 100644 index 0000000000..59f141b8c0 --- /dev/null +++ b/src/shell/help_shell.rs @@ -0,0 +1,234 @@ +use crate::commands::command::EvaluatedWholeStreamCommandArgs; +use crate::commands::cp::CopyArgs; +use crate::commands::mkdir::MkdirArgs; +use crate::commands::mv::MoveArgs; +use crate::commands::rm::RemoveArgs; +use crate::context::SourceMap; +use crate::object::{TaggedDictBuilder, command_dict}; +use crate::prelude::*; +use crate::shell::shell::Shell; +use std::ffi::OsStr; +use std::path::PathBuf; + +#[derive(Clone, Debug)] +pub struct HelpShell { + pub(crate) path: String, + pub(crate) value: Tagged, +} + +impl HelpShell { + pub fn index(registry: &CommandRegistry) -> Result { + let mut cmds = TaggedDictBuilder::new(Tag::unknown()); + let mut specs = Vec::new(); + + for cmd in registry.names() { + let mut spec = TaggedDictBuilder::new(Tag::unknown()); + let value = command_dict(registry.get_command(&cmd).unwrap(), Tag::unknown()); + + spec.insert("name", cmd); + spec.insert("description", value.get_data_by_key("usage").unwrap().as_string().unwrap()); + spec.insert_tagged("details", value); + + specs.push(spec.into_tagged_value()); + } + + cmds.insert("help", Value::List(specs)); + + Ok(HelpShell { + path: "/help".to_string(), + value: cmds.into_tagged_value(), + }) + } + + pub fn for_command( + cmd: Tagged, + registry: &CommandRegistry, + ) -> Result { + let mut sh = HelpShell::index(®istry)?; + + if let Tagged { + item: Value::Primitive(Primitive::String(name)), .. + } = cmd { + sh.set_path(format!("/help/{:}/details", name)); + } + + Ok(sh) + } + + fn commands(&self) -> VecDeque> { + let mut cmds = VecDeque::new(); + let full_path = PathBuf::from(&self.path); + + let mut viewed = self.value.clone(); + let sep_string = std::path::MAIN_SEPARATOR.to_string(); + let sep = OsStr::new(&sep_string); + + for p in full_path.iter() { + match p { + x if x == sep => {} + step => match viewed.get_data_by_key(step.to_str().unwrap()) { + Some(v) => { + viewed = v.clone(); + } + _ => {} + }, + } + } + match viewed { + Tagged { + item: Value::List(l), + .. + } => { + for item in l { + cmds.push_back(item.clone()); + } + } + x => { + cmds.push_back(x.clone()); + } + } + + cmds + } +} + +impl Shell for HelpShell { + fn name(&self, source_map: &SourceMap) -> String { + let origin_name = self.value.origin_name(source_map); + format!( + "{}", + match origin_name { + Some(x) => format!("{{{}}}", x), + None => format!("<{}>", self.value.item.type_name(),), + } + ) + } + + fn homedir(&self) -> Option { + dirs::home_dir() + } + + fn path(&self) -> String { + self.path.clone() + } + + fn set_path(&mut self, path: String) { + let _ = std::env::set_current_dir(&path); + self.path = path.clone(); + } + + fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { + Ok(self + .commands() + .map(|x| ReturnSuccess::value(x)) + .to_output_stream()) + } + + fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + let path = match args.nth(0) { + None => "/".to_string(), + Some(v) => { + let target = v.as_path()?; + + let mut cwd = PathBuf::from(&self.path); + + if target == PathBuf::from("..") { + cwd.pop(); + } else { + match target.to_str() { + Some(target) => match target.chars().nth(0) { + Some(x) if x == '/' => cwd = PathBuf::from(target), + _ => cwd.push(target), + }, + None => cwd.push(target), + } + } + cwd.to_string_lossy().to_string() + } + }; + + let mut stream = VecDeque::new(); + stream.push_back(ReturnSuccess::change_cwd(path)); + Ok(stream.into()) + } + + fn cp(&self, _args: CopyArgs, _name: Span, _path: &str) -> Result { + Ok(OutputStream::empty()) + } + + fn mv(&self, _args: MoveArgs, _name: Span, _path: &str) -> Result { + Ok(OutputStream::empty()) + } + + fn mkdir( + &self, + _args: MkdirArgs, + _name: Span, + _path: &str, + ) -> Result { + Ok(OutputStream::empty()) + } + + fn rm(&self, _args: RemoveArgs, _name: Span, _path: &str) -> Result { + Ok(OutputStream::empty()) + } + + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &rustyline::Context<'_>, + ) -> Result<(usize, Vec), rustyline::error::ReadlineError> { + let mut completions = vec![]; + + let mut possible_completion = vec![]; + let commands = self.commands(); + for cmd in commands { + match cmd { + Tagged { item, .. } => { + for desc in item.data_descriptors() { + possible_completion.push(desc); + } + } + } + } + + let line_chars: Vec<_> = line.chars().collect(); + let mut replace_pos = pos; + while replace_pos > 0 { + if line_chars[replace_pos - 1] == ' ' { + break; + } + replace_pos -= 1; + } + + for command in possible_completion.iter() { + let mut pos = replace_pos; + let mut matched = true; + if pos < line_chars.len() { + for chr in command.chars() { + if line_chars[pos] != chr { + matched = false; + break; + } + pos += 1; + if pos == line_chars.len() { + break; + } + } + } + + if matched { + completions.push(rustyline::completion::Pair { + display: command.to_string(), + replacement: command.to_string(), + }); + } + } + Ok((replace_pos, completions)) + } + + fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option { + None + } +} diff --git a/src/shell/shell.rs b/src/shell/shell.rs index 4157cacb68..dc1f104b6f 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -12,6 +12,7 @@ use std::path::PathBuf; pub trait Shell: std::fmt::Debug { fn name(&self, source_map: &SourceMap) -> String; fn homedir(&self) -> Option; + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result; diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 33baf67fe3..d7bfd10644 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -19,10 +19,10 @@ fn converts_structured_table_to_csv_text() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "csv_text_sample.txt", r#" - importer,shipper,tariff_item,name,origin - Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain - Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia - "#, + importer,shipper,tariff_item,name,origin + Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain + Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia + "#, )]); let actual = nu!( @@ -49,10 +49,10 @@ fn converts_structured_table_to_csv_text_skipping_headers_after_conversion() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "csv_text_sample.txt", r#" - importer,shipper,tariff_item,name,origin - Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain - Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia - "#, + importer,shipper,tariff_item,name,origin + Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain + Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia + "#, )]); let actual = nu!( @@ -75,19 +75,19 @@ fn converts_structured_table_to_csv_text_skipping_headers_after_conversion() { fn converts_from_csv_text_to_structured_table() { Playground::setup("filter_from_csv_test_1", |dirs, sandbox| { sandbox.with_files(vec![FileWithContentToBeTrimmed( - "los_tres_amigos.txt", + "los_tres_caballeros.txt", r#" - first_name,last_name,rusty_luck - Andrés,Robalino,1 - Jonathan,Turner,1 - Yehuda,Katz,1 - "#, + first_name,last_name,rusty_luck + Andrés,Robalino,1 + Jonathan,Turner,1 + Yehuda,Katz,1 + "#, )]); let actual = nu!( cwd: dirs.test(), h::pipeline( r#" - open los_tres_amigos.txt + open los_tres_caballeros.txt | from-csv | get rusty_luck | str --to-int @@ -106,11 +106,11 @@ fn converts_from_csv_text_skipping_headers_to_structured_table() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "los_tres_amigos.txt", r#" - first_name,last_name,rusty_luck - Andrés,Robalino,1 - Jonathan,Turner,1 - Yehuda,Katz,1 - "#, + first_name,last_name,rusty_luck + Andrés,Robalino,1 + Jonathan,Turner,1 + Yehuda,Katz,1 + "#, )]); let actual = nu!( @@ -151,15 +151,15 @@ fn converts_from_json_text_to_structured_table() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "katz.txt", r#" - { - "katz": [ - {"name": "Yehuda", "rusty_luck": 1}, - {"name": "Jonathan", "rusty_luck": 1}, - {"name": "Andres", "rusty_luck": 1}, - {"name":"GorbyPuff", "rusty_luck": 1} - ] - } - "#, + { + "katz": [ + {"name": "Yehuda", "rusty_luck": 1}, + {"name": "Jonathan", "rusty_luck": 1}, + {"name": "Andres", "rusty_luck": 1}, + {"name":"GorbyPuff", "rusty_luck": 1} + ] + } + "#, )]); let actual = nu!( @@ -177,11 +177,11 @@ fn converts_from_json_text_recognizing_objects_independendtly_to_structured_tabl sandbox.with_files(vec![FileWithContentToBeTrimmed( "katz.txt", r#" - {"name": "Yehuda", "rusty_luck": 1} - {"name": "Jonathan", "rusty_luck": 1} - {"name": "Andres", "rusty_luck": 1} - {"name":"GorbyPuff", "rusty_luck": 3} - "#, + {"name": "Yehuda", "rusty_luck": 1} + {"name": "Jonathan", "rusty_luck": 1} + {"name": "Andres", "rusty_luck": 1} + {"name":"GorbyPuff", "rusty_luck": 3} + "#, )]); let actual = nu!( @@ -205,9 +205,9 @@ fn converts_structured_table_to_json_text() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "sample.txt", r#" - JonAndrehudaTZ,3 - GorbyPuff,100 - "#, + JonAndrehudaTZ,3 + GorbyPuff,100 + "#, )]); let actual = nu!( @@ -245,10 +245,10 @@ fn converts_structured_table_to_tsv_text() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "tsv_text_sample.txt", r#" - importer shipper tariff_item name origin - Plasticos Rival Reverte 2509000000 Calcium carbonate Spain - Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia - "#, + importer shipper tariff_item name origin + Plasticos Rival Reverte 2509000000 Calcium carbonate Spain + Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia + "#, )]); let actual = nu!( @@ -275,10 +275,10 @@ fn converts_structured_table_to_tsv_text_skipping_headers_after_conversion() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "tsv_text_sample.txt", r#" - importer shipper tariff_item name origin - Plasticos Rival Reverte 2509000000 Calcium carbonate Spain - Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia - "#, + importer shipper tariff_item name origin + Plasticos Rival Reverte 2509000000 Calcium carbonate Spain + Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia + "#, )]); let actual = nu!( @@ -303,11 +303,11 @@ fn converts_from_tsv_text_to_structured_table() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "los_tres_amigos.txt", r#" - first Name Last Name rusty_luck - Andrés Robalino 1 - Jonathan Turner 1 - Yehuda Katz 1 - "#, + first Name Last Name rusty_luck + Andrés Robalino 1 + Jonathan Turner 1 + Yehuda Katz 1 + "#, )]); let actual = nu!( @@ -332,11 +332,11 @@ fn converts_from_tsv_text_skipping_headers_to_structured_table() { sandbox.with_files(vec![FileWithContentToBeTrimmed( "los_tres_amigos.txt", r#" - first Name Last Name rusty_luck - Andrés Robalino 1 - Jonathan Turner 1 - Yehuda Katz 1 - "#, + first Name Last Name rusty_luck + Andrés Robalino 1 + Jonathan Turner 1 + Yehuda Katz 1 + "#, )]); let actual = nu!( @@ -359,14 +359,15 @@ fn converts_from_tsv_text_skipping_headers_to_structured_table() { fn can_convert_table_to_bson_and_back_into_table() { let actual = nu!( cwd: "tests/fixtures/formats", h::pipeline( - r#" - open sample.bson - | to-bson - | from-bson - | get root - | nth 1 - | get b - | echo $it"# + r#" + open sample.bson + | to-bson + | from-bson + | get root + | nth 1 + | get b + | echo $it + "# )); assert_eq!(actual, "whel"); @@ -376,14 +377,15 @@ fn can_convert_table_to_bson_and_back_into_table() { fn can_convert_table_to_sqlite_and_back_into_table() { let actual = nu!( cwd: "tests/fixtures/formats", h::pipeline( - r#" - open sample.db - | to-sqlite - | from-sqlite - | get table_values - | nth 2 - | get x - | echo $it"# + r#" + open sample.db + | to-sqlite + | from-sqlite + | get table_values + | nth 2 + | get x + | echo $it + "# )); assert_eq!(actual, "hello"); @@ -393,12 +395,13 @@ fn can_convert_table_to_sqlite_and_back_into_table() { fn can_convert_table_to_toml_text_and_from_toml_text_back_into_table() { let actual = nu!( cwd: "tests/fixtures/formats", h::pipeline( - r#" - open cargo_sample.toml - | to-toml - | from-toml - | get package.name - | echo $it"# + r#" + open cargo_sample.toml + | to-toml + | from-toml + | get package.name + | echo $it + "# )); assert_eq!(actual, "nu");