Highlight help tutor (#838)

* WIP

* Syntax highlight help, add tutor
This commit is contained in:
JT 2022-01-24 10:05:19 -05:00 committed by GitHub
parent 525ed7653f
commit 3d0b1ef1ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 640 additions and 34 deletions

View file

@ -1,11 +1,13 @@
mod completions; mod completions;
mod errors; mod errors;
mod nu_highlight;
mod prompt; mod prompt;
mod syntax_highlight; mod syntax_highlight;
mod validation; mod validation;
pub use completions::NuCompleter; pub use completions::NuCompleter;
pub use errors::CliError; pub use errors::CliError;
pub use nu_highlight::NuHighlight;
pub use prompt::NushellPrompt; pub use prompt::NushellPrompt;
pub use syntax_highlight::NuHighlighter; pub use syntax_highlight::NuHighlighter;
pub use validation::NuValidator; pub use validation::NuValidator;

View file

@ -0,0 +1,63 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Value};
use reedline::Highlighter;
#[derive(Clone)]
pub struct NuHighlight;
impl Command for NuHighlight {
fn name(&self) -> &str {
"nu-highlight"
}
fn signature(&self) -> Signature {
Signature::build("nu-highlight").category(Category::Strings)
}
fn usage(&self) -> &str {
"Syntax highlight the input string."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let config = stack.get_config()?;
let highlighter = crate::NuHighlighter {
engine_state,
config,
};
input.map(
move |x| match x.as_string() {
Ok(line) => {
let highlights = highlighter.highlight(&line);
Value::String {
val: highlights.render_simple(),
span: head,
}
}
Err(err) => Value::Error { error: err },
},
ctrlc,
)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Describe the type of a string",
example: "'let x = 3' | nu-highlight",
result: None,
}]
}
}

View file

@ -24,12 +24,12 @@ impl Command for Into {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Into.signature(), &[], engine_state), val: get_full_help(&Into.signature(), &[], engine_state, stack),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -24,7 +24,7 @@ impl Command for ExportCommand {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
@ -33,6 +33,7 @@ impl Command for ExportCommand {
&ExportCommand.signature(), &ExportCommand.signature(),
&ExportCommand.examples(), &ExportCommand.examples(),
engine_state, engine_state,
stack,
), ),
span: call.head, span: call.head,
} }

View file

@ -210,7 +210,9 @@ fn help(
let output = full_commands let output = full_commands
.iter() .iter()
.filter(|(signature, _, _, _)| signature.name == name) .filter(|(signature, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _)| get_full_help(signature, examples, engine_state)) .map(|(signature, examples, _, _)| {
get_full_help(signature, examples, engine_state, stack)
})
.collect::<Vec<String>>(); .collect::<Vec<String>>();
if !output.is_empty() { if !output.is_empty() {

View file

@ -17,6 +17,7 @@ mod let_;
mod metadata; mod metadata;
mod module; mod module;
mod source; mod source;
mod tutor;
mod use_; mod use_;
mod version; mod version;
@ -39,6 +40,7 @@ pub use let_::Let;
pub use metadata::Metadata; pub use metadata::Metadata;
pub use module::Module; pub use module::Module;
pub use source::Source; pub use source::Source;
pub use tutor::Tutor;
pub use use_::Use; pub use use_::Use;
pub use version::Version; pub use version::Version;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]

View file

@ -0,0 +1,466 @@
use itertools::Itertools;
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value,
};
#[derive(Clone)]
pub struct Tutor;
impl Command for Tutor {
fn name(&self) -> &str {
"tutor"
}
fn signature(&self) -> Signature {
Signature::build("tutor")
.optional(
"search",
SyntaxShape::String,
"item to search for, or 'list' to list available tutorials",
)
.named(
"find",
SyntaxShape::String,
"Search tutorial for a phrase",
Some('f'),
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Run the tutorial. To begin, run: tutor"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
tutor(engine_state, stack, call)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Begin the tutorial",
example: "tutor begin",
result: None,
},
Example {
description: "Search a tutorial by phrase",
example: "tutor -f \"$in\"",
result: None,
},
]
}
}
fn tutor(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let search: Option<String> = call.opt(engine_state, stack, 0).unwrap_or(None);
let find: Option<String> = call.get_flag(engine_state, stack, "find")?;
let search_space = [
(vec!["begin"], begin_tutor()),
(
vec!["table", "tables", "row", "rows", "column", "columns"],
table_tutor(),
),
(vec!["cell", "cells"], cell_tutor()),
(
vec![
"expr",
"exprs",
"expressions",
"subexpression",
"subexpressions",
"sub-expression",
"sub-expressions",
],
expression_tutor(),
),
(vec!["echo"], echo_tutor()),
(vec!["each", "iteration", "iter"], each_tutor()),
(
vec!["var", "vars", "variable", "variables"],
variable_tutor(),
),
(vec!["engine-q", "e-q"], engineq_tutor()),
(vec!["block", "blocks"], block_tutor()),
(vec!["shorthand", "shorthands"], shorthand_tutor()),
];
if let Some(find) = find {
let mut results = vec![];
for search_group in search_space {
if search_group.1.contains(&find) {
results.push(search_group.0[0].to_string())
}
}
let message = format!("You can find '{}' in the following topics:\n{}\n\nYou can learn about a topic using `tutor` followed by the name of the topic.\nFor example: `tutor table` to open the table topic.\n\n",
find,
results.into_iter().map(|x| format!("- {}", x)).join("\n")
);
return Ok(display(&message, engine_state, stack, span));
} else if let Some(search) = search {
for search_group in search_space {
if search_group.0.contains(&search.as_str()) {
return Ok(display(search_group.1, engine_state, stack, span));
}
}
}
Ok(display(default_tutor(), engine_state, stack, span))
}
fn default_tutor() -> &'static str {
r#"
Welcome to the Nushell tutorial!
With the `tutor` command, you'll be able to learn a lot about how Nushell
works along with many fun tips and tricks to speed up everyday tasks.
To get started, you can use `tutor begin`.
"#
}
fn begin_tutor() -> &'static str {
r#"
Nushell is a structured shell and programming language. One way to begin
using it is to try a few of the commands.
The first command to try is `ls`. The `ls` command will show you a list
of the files in the current directory. Notice that these files are shown
as a table. Each column of this table not only tells us what is being
shown, but also gives us a way to work with the data.
You can combine the `ls` command with other commands using the pipeline
symbol '|'. This allows data to flow from one command to the next.
For example, if we only wanted the name column, we could do:
```
ls | select name
```
Notice that we still get a table, but this time it only has one column:
the name column.
You can continue to learn more about tables by running:
```
tutor tables
```
If at any point, you'd like to restart this tutorial, you can run:
```
tutor begin
```
"#
}
fn table_tutor() -> &'static str {
r#"
The most common form of data in Nushell is the table. Tables contain rows and
columns of data. In each cell of the table, there is data that you can access
using Nushell commands.
To get the 3rd row in the table, you can use the `nth` command:
```
ls | nth 2
```
This will get the 3rd (note that `nth` is zero-based) row in the table created
by the `ls` command. You can use `nth` on any table created by other commands
as well.
You can also access the column of data in one of two ways. If you want
to keep the column as part of a new table, you can use `select`.
```
ls | select name
```
This runs `ls` and returns only the "name" column of the table.
If, instead, you'd like to get access to the values inside of the column, you
can use the `get` command.
```
ls | get name
```
This allows us to get to the list of strings that are the filenames rather
than having a full table. In some cases, this can make the names easier to
work with.
You can continue to learn more about working with cells of the table by
running:
```
tutor cells
```
"#
}
fn cell_tutor() -> &'static str {
r#"
Working with cells of data in the table is a key part of working with data in
Nushell. Because of this, there is a rich list of commands to work with cells
as well as handy shorthands for accessing cells.
Cells can hold simple values like strings and numbers, or more complex values
like lists and tables.
To reach a cell of data from a table, you can combine a row operation and a
column operation.
```
ls | nth 4 | get name
```
You can combine these operations into one step using a shortcut.
```
(ls).4.name
```
Names/strings represent columns names and numbers represent row numbers.
The `(ls)` is a form of expression. You can continue to learn more about
expressions by running:
```
tutor expressions
```
You can also learn about these cell shorthands by running:
```
tutor shorthands
```
"#
}
fn expression_tutor() -> &'static str {
r#"
Expressions give you the power to mix calls to commands with math. The
simplest expression is a single value like a string or number.
```
3
```
Expressions can also include math operations like addition or division.
```
10 / 2
```
Normally, an expression is one type of operation: math or commands. You can
mix these types by using subexpressions. Subexpressions are just like
expressions, but they're wrapped in parentheses `()`.
```
10 * (3 + 4)
```
Here we use parentheses to create a higher math precedence in the math
expression.
```
echo (2 + 3)
```
You can continue to learn more about the `echo` command by running:
```
tutor echo
```
"#
}
fn echo_tutor() -> &'static str {
r#"
The `echo` command in Nushell is a powerful tool for not only seeing values,
but also for creating new ones.
```
echo "Hello"
```
You can echo output. This output, if it's not redirected using a "|" pipeline
will be displayed to the screen.
```
echo 1..10
```
You can also use echo to work with individual values of a range. In this
example, `echo` will create the values from 1 to 10 as a list.
```
echo 1 2 3 4 5
```
You can also create lists of values by passing `echo` multiple arguments.
This can be helpful if you want to later processes these values.
The `echo` command can pair well with the `each` command which can run
code on each row, or item, of input.
You can continue to learn more about the `each` command by running:
```
tutor each
```
"#
}
fn each_tutor() -> &'static str {
r#"
The `each` command gives us a way of working with each individual row or
element of a list one at a time. It reads these in from the pipeline and
runs a block on each element. A block is a group of pipelines.
```
echo 1 2 3 | each { $it + 10}
```
This example iterates over each element sent by `echo`, giving us three new
values that are the original value + 10. Here, the `$it` is a variable that
is the name given to the block's parameter by default.
You can learn more about blocks by running:
```
tutor blocks
```
You can also learn more about variables by running:
```
tutor variables
```
"#
}
fn variable_tutor() -> &'static str {
r#"
Variables are an important way to store values to be used later. To create a
variable, you can use the `let` keyword. The `let` command will create a
variable and then assign it a value in one step.
```
let $x = 3
```
Once created, we can refer to this variable by name.
```
$x
```
Nushell also comes with built-in variables. The `$nu` variable is a reserved
variable that contains a lot of information about the currently running
instance of Nushell. The `$it` variable is the name given to block parameters
if you don't specify one. And `$in` is the variable that allows you to work
with all of the data coming in from the pipeline in one place.
"#
}
fn block_tutor() -> &'static str {
r#"
Blocks are a special form of expression that hold code to be run at a later
time. Often, you'll see blocks as one of the arguments given to commands
like `each` and `if`.
```
ls | each {|x| $x.name}
```
The above will create a list of the filenames in the directory.
```
if $true { echo "it's true" } { echo "it's not true" }
```
This `if` call will run the first block if the expression is true, or the
second block if the expression is false.
"#
}
fn shorthand_tutor() -> &'static str {
r#"
You can access cells in a table using a shorthand notation sometimes called a
"column path" or "cell path". These paths allow you to go from a table to
rows, columns, or cells inside of the table.
Shorthand paths are made from rows numbers, column names, or both. You can use
them on any variable or subexpression.
```
$nu.cwd
```
The above accesses the built-in `$nu` variable, gets its table, and then uses
the shorthand path to retrieve only the cell data inside the "cwd" column.
```
(ls).name.4
```
This will retrieve the cell data in the "name" column on the 5th row (note:
row numbers are zero-based).
Rows and columns don't need to come in any specific order. You can get the
same value using:
```
(ls).4.name
```
"#
}
fn engineq_tutor() -> &'static str {
r#"
Engine-q is the upcoming engine for Nushell. Build for speed and correctness,
it also comes with a set of changes from Nushell versions prior to 0.60. To
get ready for engine-q look for some of these changes that might impact your
current scripts:
* Engine-q now uses a few new data structures, including a record syntax
that allows you to model key-value pairs similar to JSON objects.
* Environment variables can now contain more than just strings. Structured
values are converted to strings for external commands using converters.
* `if` will now use an `else` keyword before the else block.
* We're moving from "config.toml" to "config.nu". This means startup will
now be a script file.
* `config` and its subcommands are being replaced by a record that you can
update in the shell which contains all the settings under the variable
`$config`.
* bigint/bigdecimal values are now machine i64 and f64 values
* And more, you can read more about upcoming changes in the up-to-date list
at: https://github.com/nushell/engine-q/issues/522
"#
}
fn display(help: &str, engine_state: &EngineState, stack: &mut Stack, span: Span) -> PipelineData {
let help = help.split('`');
let mut build = String::new();
let mut code_mode = false;
for item in help {
if code_mode {
code_mode = false;
//TODO: support no-color mode
if let Some(highlighter) = engine_state.find_decl(b"nu-highlight") {
let decl = engine_state.get_decl(highlighter);
if let Ok(output) = decl.run(
engine_state,
stack,
&Call::new(),
Value::String {
val: item.to_string(),
span: Span { start: 0, end: 0 },
}
.into_pipeline_data(),
) {
let result = output.into_value(Span { start: 0, end: 0 });
match result.as_string() {
Ok(s) => {
build.push_str(&s);
}
_ => {
build.push_str(item);
}
}
}
}
} else {
code_mode = true;
build.push_str(item);
}
}
Value::string(build, span).into_pipeline_data()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Tutor)
}
}

View file

@ -24,12 +24,17 @@ impl Command for Dataframe {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Dataframe.signature(), &Dataframe.examples(), engine_state), val: get_full_help(
&Dataframe.signature(),
&Dataframe.examples(),
engine_state,
stack,
),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -34,13 +34,13 @@ impl Command for Date {
fn date( fn date(
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Date.signature(), &Date.examples(), engine_state), val: get_full_help(&Date.signature(), &Date.examples(), engine_state, stack),
span: head, span: head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -44,6 +44,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
Metadata, Metadata,
Module, Module,
Source, Source,
Tutor,
Use, Use,
Version, Version,
}; };

View file

@ -22,12 +22,12 @@ impl Command for Hash {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Self.signature(), &Self.examples(), engine_state), val: get_full_help(&Self.signature(), &Self.examples(), engine_state, stack),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -24,7 +24,7 @@ impl Command for MathCommand {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
@ -33,6 +33,7 @@ impl Command for MathCommand {
&MathCommand.signature(), &MathCommand.signature(),
&MathCommand.examples(), &MathCommand.examples(),
engine_state, engine_state,
stack,
), ),
span: call.head, span: call.head,
} }

View file

@ -24,12 +24,12 @@ impl Command for Url {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Url.signature(), &Url.examples(), engine_state), val: get_full_help(&Url.signature(), &Url.examples(), engine_state, stack),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -39,7 +39,7 @@ the path literal."#
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, nu_protocol::ShellError> { ) -> Result<PipelineData, nu_protocol::ShellError> {
@ -48,6 +48,7 @@ the path literal."#
&PathCommand.signature(), &PathCommand.signature(),
&PathCommand.examples(), &PathCommand.examples(),
engine_state, engine_state,
stack,
), ),
span: call.head, span: call.head,
} }

View file

@ -24,7 +24,7 @@ impl Command for RandomCommand {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
@ -33,6 +33,7 @@ impl Command for RandomCommand {
&RandomCommand.signature(), &RandomCommand.signature(),
&RandomCommand.examples(), &RandomCommand.examples(),
engine_state, engine_state,
stack,
), ),
span: call.head, span: call.head,
} }

View file

@ -24,7 +24,7 @@ impl Command for SplitCommand {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
@ -33,6 +33,7 @@ impl Command for SplitCommand {
&SplitCommand.signature(), &SplitCommand.signature(),
&SplitCommand.examples(), &SplitCommand.examples(),
engine_state, engine_state,
stack,
), ),
span: call.head, span: call.head,
} }

View file

@ -24,12 +24,12 @@ impl Command for Str {
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Str.signature(), &Str.examples(), engine_state), val: get_full_help(&Str.signature(), &Str.examples(), engine_state, stack),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View file

@ -1,5 +1,9 @@
use itertools::Itertools; use itertools::Itertools;
use nu_protocol::{engine::EngineState, Example, Signature, Span, Value}; use nu_protocol::{
ast::Call,
engine::{EngineState, Stack},
Example, IntoPipelineData, Signature, Span, Value,
};
use std::collections::HashMap; use std::collections::HashMap;
const COMMANDS_DOCS_DIR: &str = "docs/commands"; const COMMANDS_DOCS_DIR: &str = "docs/commands";
@ -13,7 +17,12 @@ pub struct DocumentationConfig {
brief: bool, brief: bool,
} }
fn generate_doc(name: &str, engine_state: &EngineState, head: Span) -> (Vec<String>, Vec<Value>) { fn generate_doc(
name: &str,
engine_state: &EngineState,
stack: &mut Stack,
head: Span,
) -> (Vec<String>, Vec<Value>) {
let mut cols = vec![]; let mut cols = vec![];
let mut vals = vec![]; let mut vals = vec![];
@ -48,6 +57,7 @@ fn generate_doc(name: &str, engine_state: &EngineState, head: Span) -> (Vec<Stri
&command.signature(), &command.signature(),
&command.examples(), &command.examples(),
engine_state, engine_state,
stack,
&DocumentationConfig { &DocumentationConfig {
no_subcommands: true, no_subcommands: true,
no_color: true, no_color: true,
@ -61,7 +71,7 @@ fn generate_doc(name: &str, engine_state: &EngineState, head: Span) -> (Vec<Stri
} }
// generate_docs gets the documentation from each command and returns a Table as output // generate_docs gets the documentation from each command and returns a Table as output
pub fn generate_docs(engine_state: &EngineState, head: Span) -> Value { pub fn generate_docs(engine_state: &EngineState, stack: &mut Stack, head: Span) -> Value {
let signatures = engine_state.get_signatures(true); let signatures = engine_state.get_signatures(true);
// cmap will map parent commands to it's subcommands e.g. to -> [to csv, to yaml, to bson] // cmap will map parent commands to it's subcommands e.g. to -> [to csv, to yaml, to bson]
@ -88,11 +98,11 @@ pub fn generate_docs(engine_state: &EngineState, head: Span) -> Value {
if !cmap.contains_key(&sig.name) { if !cmap.contains_key(&sig.name) {
continue; continue;
} }
let mut row_entries = generate_doc(&sig.name, engine_state, head); let mut row_entries = generate_doc(&sig.name, engine_state, stack, head);
// Iterate over all the subcommands of the parent command // Iterate over all the subcommands of the parent command
let mut sub_table = Vec::new(); let mut sub_table = Vec::new();
for sub_name in cmap.get(&sig.name).unwrap_or(&Vec::new()) { for sub_name in cmap.get(&sig.name).unwrap_or(&Vec::new()) {
let (cols, vals) = generate_doc(sub_name, engine_state, head); let (cols, vals) = generate_doc(sub_name, engine_state, stack, head);
sub_table.push(Value::Record { sub_table.push(Value::Record {
cols, cols,
vals, vals,
@ -139,6 +149,7 @@ pub fn get_documentation(
sig: &Signature, sig: &Signature,
examples: &[Example], examples: &[Example],
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
config: &DocumentationConfig, config: &DocumentationConfig,
) -> String { ) -> String {
let cmd_name = &sig.name; let cmd_name = &sig.name;
@ -206,14 +217,32 @@ pub fn get_documentation(
long_desc.push_str(" "); long_desc.push_str(" ");
long_desc.push_str(example.description); long_desc.push_str(example.description);
// if config.no_color { if config.no_color {
long_desc.push_str(&format!("\n > {}\n", example.example)); long_desc.push_str(&format!("\n > {}\n", example.example));
// } else { } else if let Some(highlighter) = engine_state.find_decl(b"nu-highlight") {
// let colored_example = let decl = engine_state.get_decl(highlighter);
// crate::shell::painter::Painter::paint_string(example.example, scope, &palette); if let Ok(output) = decl.run(
// long_desc.push_str(&format!("\n > {}\n", colored_example)); engine_state,
// } stack,
&Call::new(),
Value::String {
val: example.example.to_string(),
span: Span { start: 0, end: 0 },
}
.into_pipeline_data(),
) {
let result = output.into_value(Span { start: 0, end: 0 });
match result.as_string() {
Ok(s) => {
long_desc.push_str(&format!("\n > {}\n", s));
}
_ => {
long_desc.push_str(&format!("\n > {}\n", example.example));
}
}
}
}
} }
long_desc.push('\n'); long_desc.push('\n');
@ -294,11 +323,17 @@ fn get_flags_section(signature: &Signature) -> String {
long_desc long_desc
} }
pub fn get_brief_help(sig: &Signature, examples: &[Example], engine_state: &EngineState) -> String { pub fn get_brief_help(
sig: &Signature,
examples: &[Example],
engine_state: &EngineState,
stack: &mut Stack,
) -> String {
get_documentation( get_documentation(
sig, sig,
examples, examples,
engine_state, engine_state,
stack,
&DocumentationConfig { &DocumentationConfig {
no_subcommands: false, no_subcommands: false,
no_color: false, no_color: false,
@ -307,6 +342,17 @@ pub fn get_brief_help(sig: &Signature, examples: &[Example], engine_state: &Engi
) )
} }
pub fn get_full_help(sig: &Signature, examples: &[Example], engine_state: &EngineState) -> String { pub fn get_full_help(
get_documentation(sig, examples, engine_state, &DocumentationConfig::default()) sig: &Signature,
examples: &[Example],
engine_state: &EngineState,
stack: &mut Stack,
) -> String {
get_documentation(
sig,
examples,
engine_state,
stack,
&DocumentationConfig::default(),
)
} }

View file

@ -33,7 +33,12 @@ fn eval_call(
let decl = engine_state.get_decl(call.decl_id); let decl = engine_state.get_decl(call.decl_id);
if call.named.iter().any(|(flag, _)| flag.item == "help") { if call.named.iter().any(|(flag, _)| flag.item == "help") {
let full_help = get_full_help(&decl.signature(), &decl.examples(), engine_state); let full_help = get_full_help(
&decl.signature(),
&decl.examples(),
engine_state,
caller_stack,
);
Ok(Value::String { Ok(Value::String {
val: full_help, val: full_help,
span: call.head, span: call.head,

View file

@ -28,6 +28,15 @@ fn main() -> Result<()> {
let init_cwd = utils::get_init_cwd(); let init_cwd = utils::get_init_cwd();
let mut engine_state = create_default_context(&init_cwd); let mut engine_state = create_default_context(&init_cwd);
// Custom additions
let delta = {
let mut working_set = nu_protocol::engine::StateWorkingSet::new(&engine_state);
working_set.add_decl(Box::new(nu_cli::NuHighlight));
working_set.render()
};
let _ = engine_state.merge_delta(delta, None, &init_cwd);
// TODO: make this conditional in the future // TODO: make this conditional in the future
// Ctrl-c protection section // Ctrl-c protection section
let ctrlc = Arc::new(AtomicBool::new(false)); let ctrlc = Arc::new(AtomicBool::new(false));