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 1e5e12037..54117e08e 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 e6592bd54..949c7a359 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 d547422f3..0da8cadbd 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 7d754d9ad..844a093c4 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 718f2846f..a84e66fce 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 143070763..6babb6c9c 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 535c8f745..9bf7fa9f3 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 404d1d6b8..10611b41b 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 c41c1d374..56cce6227 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 ee1a289cd..8160fc9d2 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 b979dfe3e..0e2fee563 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 9562aee38..80da5fe1c 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 63316b708..d2170b00d 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 ce5f1d19e..feed8f7c4 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 48f1871fd..af61dc8be 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 3fb04c9bf..93ba87ece 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 6b5ed571c..0d5f05024 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 97aa98124..9f4a96da3 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 d1f7bb4f8..8495c4b22 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 b4b1f7980..e902d0a3d 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 bc288ff39..88bd30375 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 6df13d686..b4b341eca 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 ed37bf453..366621d43 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 adf39240d..4e4be72bc 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 a5f229a03..e67c04503 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 cd8db615a..7f0512a7b 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 000000000..def2f3338 --- /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 f776b7d65..29fe77377 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 e6ce32b3f..019517754 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 bc020c17d..709614150 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 96fd16afa..9dec9a314 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 af93dde38..130e5996e 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 de0d3dbe2..359f26231 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 5651d8412..98ab6a10a 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 205ad62ff..202f8eae1 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 6c7fca8f2..927edf8b3 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 bd25db852..a14cb1a60 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 adab33024..993605eb1 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 4291e9380..970cce343 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 15a19a857..b11e79796 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 92aff1cb2..b519f8ea6 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 2b43f0147..8b8129724 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 36f7aa6d8..60bb5c6e4 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 953d90f96..4723c4bda 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 f31a16fd5..5e0159e14 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 2af7d47e8..02da0460c 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 d0237b16a..90ba24b99 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 c538fdf97..edce33b96 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 ab5e7f578..4bde5e25b 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 592d301be..fa95225f4 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 0f58e34bf..4efd6f821 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 01c7565d5..973105709 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 43984ce58..04c429e1b 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 025484d8d..64ba9aa4a 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 65651396a..58ad20819 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 3559459cd..a1c0e2298 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 fcbf96e3b..dca953c23 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 5d6080739..d8216f900 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 b999ce12d..1a229d768 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 f0aa0219e..9b91cbc0c 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 22a29a36b..66152843f 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 e13017ab5..def335420 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 4e26c05ef..5abd4c6d1 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 5f037ded4..b09c341b4 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 0ffbc19db..fdd722d89 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 5bade25f2..577a55c20 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 dc5ecfc08..e2a8df102 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 000000000..317fe5fa3 --- /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 b090e4ef7..2f85fcfa6 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 a1119bc76..89bffc3f5 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 52d49ea5c..7d22e4f85 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 93c519212..27a12e677 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 fb9c2213a..7601621cd 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 902c038bc..ab8a25aa6 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 ece83b7f2..5e6a2a293 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 6f78351fe..bb8053421 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 0656d5f78..8aa24b490 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 1c983b1ad..bd30cef98 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 74d87befe..e7ff919d8 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 81097179a..81db9dd19 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 9784e854c..f57a705ec 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 8e511f82c..d781ff22c 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 77670c5ec..caa1443ac 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 000000000..59f141b8c --- /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 4157cacb6..dc1f104b6 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 33baf67fe..d7bfd1064 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");