mirror of
https://github.com/nushell/nushell
synced 2024-11-10 07:04:13 +00:00
Port help and start porting split
This commit is contained in:
parent
b654415494
commit
4ddc953e38
14 changed files with 633 additions and 50 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -551,6 +551,7 @@ dependencies = [
|
|||
name = "nu-engine"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nu-parser",
|
||||
"nu-path",
|
||||
"nu-protocol",
|
||||
|
|
|
@ -15,7 +15,7 @@ impl Command for Do {
|
|||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("do").required(
|
||||
Signature::build("do").desc(self.usage()).required(
|
||||
"block",
|
||||
SyntaxShape::Block(Some(vec![])),
|
||||
"the block to run",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EvaluationContext},
|
||||
Example, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
span, Example, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
use nu_engine::CallExt;
|
||||
use nu_engine::{get_full_help, CallExt};
|
||||
|
||||
pub struct Help;
|
||||
|
||||
|
@ -73,11 +73,11 @@ impl Command for Help {
|
|||
}
|
||||
|
||||
fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||
let span = call.head;
|
||||
let head = call.head;
|
||||
let find: Option<Spanned<String>> = call.get_flag(context, "find")?;
|
||||
let rest: Vec<Spanned<String>> = call.rest(context, 0)?;
|
||||
|
||||
let full_commands = context.get_commands_info();
|
||||
let full_commands = context.get_signatures_with_examples();
|
||||
|
||||
if let Some(f) = find {
|
||||
let search_string = f.item;
|
||||
|
@ -87,29 +87,36 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
|||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let key = cmd.name.clone();
|
||||
let c = cmd.usage.clone();
|
||||
let e = cmd.extra_usage.clone();
|
||||
let key = cmd.0.name.clone();
|
||||
let c = cmd.0.usage.clone();
|
||||
let e = cmd.0.extra_usage.clone();
|
||||
if key.to_lowercase().contains(&search_string)
|
||||
|| c.to_lowercase().contains(&search_string)
|
||||
|| e.to_lowercase().contains(&search_string)
|
||||
{
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String { val: key, span });
|
||||
vals.push(Value::String {
|
||||
val: key,
|
||||
span: head,
|
||||
});
|
||||
|
||||
cols.push("usage".into());
|
||||
vals.push(Value::String { val: c, span });
|
||||
vals.push(Value::String { val: c, span: head });
|
||||
|
||||
cols.push("extra_usage".into());
|
||||
vals.push(Value::String { val: e, span });
|
||||
vals.push(Value::String { val: e, span: head });
|
||||
|
||||
found_cmds_vec.push(Value::Record { cols, vals, span });
|
||||
found_cmds_vec.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Value::List {
|
||||
vals: found_cmds_vec,
|
||||
span,
|
||||
span: head,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,25 +128,38 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
|||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let key = cmd.name.clone();
|
||||
let c = cmd.usage.clone();
|
||||
let e = cmd.extra_usage.clone();
|
||||
let key = cmd.0.name.clone();
|
||||
let c = cmd.0.usage.clone();
|
||||
let e = cmd.0.extra_usage.clone();
|
||||
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String { val: key, span });
|
||||
vals.push(Value::String {
|
||||
val: key,
|
||||
span: head,
|
||||
});
|
||||
|
||||
cols.push("usage".into());
|
||||
vals.push(Value::String { val: c, span });
|
||||
vals.push(Value::String { val: c, span: head });
|
||||
|
||||
cols.push("extra_usage".into());
|
||||
vals.push(Value::String { val: e, span });
|
||||
vals.push(Value::String { val: e, span: head });
|
||||
|
||||
found_cmds_vec.push(Value::Record { cols, vals, span });
|
||||
found_cmds_vec.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Value::List {
|
||||
vals: found_cmds_vec,
|
||||
span: head,
|
||||
})
|
||||
} else {
|
||||
let mut name = String::new();
|
||||
let mut output = String::new();
|
||||
|
||||
for r in rest {
|
||||
for r in &rest {
|
||||
if !name.is_empty() {
|
||||
name.push(' ');
|
||||
}
|
||||
|
@ -147,31 +167,24 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
|||
}
|
||||
|
||||
for cmd in full_commands {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let key = cmd.name.clone();
|
||||
let c = cmd.usage.clone();
|
||||
let e = cmd.extra_usage.clone();
|
||||
|
||||
if key.starts_with(&name) {
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String { val: key, span });
|
||||
|
||||
cols.push("usage".into());
|
||||
vals.push(Value::String { val: c, span });
|
||||
|
||||
cols.push("extra_usage".into());
|
||||
vals.push(Value::String { val: e, span });
|
||||
|
||||
found_cmds_vec.push(Value::Record { cols, vals, span });
|
||||
if cmd.0.name == name {
|
||||
let help = get_full_help(&cmd.0, &cmd.1, context);
|
||||
output.push_str(&help);
|
||||
}
|
||||
}
|
||||
|
||||
if !output.is_empty() {
|
||||
Ok(Value::String {
|
||||
val: output,
|
||||
span: call.head,
|
||||
})
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound(span(&[
|
||||
rest[0].span,
|
||||
rest[rest.len() - 1].span,
|
||||
])))
|
||||
}
|
||||
}
|
||||
Ok(Value::List {
|
||||
vals: found_cmds_vec,
|
||||
span,
|
||||
})
|
||||
|
||||
// FIXME: the fancy help stuff needs to be reimplemented
|
||||
/*
|
||||
|
@ -341,7 +354,7 @@ You can also learn more at https://www.nushell.sh/book/"#;
|
|||
|
||||
Ok(Value::String {
|
||||
val: msg.into(),
|
||||
span,
|
||||
span: head,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
|||
working_set.add_decl(Box::new(Mv));
|
||||
working_set.add_decl(Box::new(Ps));
|
||||
working_set.add_decl(Box::new(Select));
|
||||
working_set.add_decl(Box::new(Split));
|
||||
working_set.add_decl(Box::new(SplitChars));
|
||||
working_set.add_decl(Box::new(Sys));
|
||||
working_set.add_decl(Box::new(Table));
|
||||
working_set.add_decl(Box::new(Touch));
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
mod build_string;
|
||||
mod split;
|
||||
|
||||
pub use build_string::BuildString;
|
||||
pub use split::*;
|
||||
|
|
157
crates/nu-command/src/strings/split/chars.rs
Normal file
157
crates/nu-command/src/strings/split/chars.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EvaluationContext},
|
||||
Example, IntoValueStream, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"split chars"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("split chars")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"splits a string's characters into separate rows"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_context: &EvaluationContext,
|
||||
call: &Call,
|
||||
input: Value,
|
||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||
split_chars(call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Split the string's characters into separate rows",
|
||||
example: "echo 'hello' | split chars",
|
||||
result: Some(vec![
|
||||
Value::String {
|
||||
val: "h".into(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "e".into(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "l".into(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "l".into(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "o".into(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn split_chars(call: &Call, input: Value) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||
let name = call.head;
|
||||
|
||||
Ok(match input {
|
||||
Value::List { vals, span } => Value::List {
|
||||
vals: vals
|
||||
.iter()
|
||||
.flat_map(move |v| {
|
||||
if let Ok(s) = v.as_string() {
|
||||
let v_span = v.span();
|
||||
s.chars()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(move |x| Value::String {
|
||||
val: x.to_string(),
|
||||
span: v_span,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![Value::Error {
|
||||
error: ShellError::PipelineMismatch {
|
||||
expected: Type::String,
|
||||
expected_span: name,
|
||||
origin: v.span(),
|
||||
},
|
||||
}]
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
Value::Stream { stream, span } => Value::Stream {
|
||||
stream: stream
|
||||
.flat_map(move |v| {
|
||||
if let Ok(s) = v.as_string() {
|
||||
let v_span = v.span();
|
||||
s.chars()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(move |x| Value::String {
|
||||
val: x.to_string(),
|
||||
span: v_span,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![Value::Error {
|
||||
error: ShellError::PipelineMismatch {
|
||||
expected: Type::String,
|
||||
expected_span: name,
|
||||
origin: v.span(),
|
||||
},
|
||||
}]
|
||||
}
|
||||
})
|
||||
.into_value_stream(),
|
||||
span,
|
||||
},
|
||||
v => {
|
||||
let v_span = v.span();
|
||||
if let Ok(s) = v.as_string() {
|
||||
Value::List {
|
||||
vals: s
|
||||
.chars()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(move |x| Value::String {
|
||||
val: x.to_string(),
|
||||
span: v_span,
|
||||
})
|
||||
.collect(),
|
||||
span: v_span,
|
||||
}
|
||||
} else {
|
||||
Value::Error {
|
||||
error: ShellError::PipelineMismatch {
|
||||
expected: Type::String,
|
||||
expected_span: name,
|
||||
origin: v.span(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::ShellError;
|
||||
// use super::SubCommand;
|
||||
|
||||
// #[test]
|
||||
// fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
// use crate::examples::test as test_examples;
|
||||
|
||||
// test_examples(SubCommand {})
|
||||
// }
|
||||
// }
|
48
crates/nu-command/src/strings/split/command.rs
Normal file
48
crates/nu-command/src/strings/split/command.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use nu_engine::get_full_help;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EvaluationContext},
|
||||
Signature, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SplitCommand;
|
||||
|
||||
impl Command for SplitCommand {
|
||||
fn name(&self) -> &str {
|
||||
"split"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("split")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Split contents across desired subcommand (like row, column) via the separator."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
context: &EvaluationContext,
|
||||
call: &Call,
|
||||
_input: Value,
|
||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
||||
Ok(Value::String {
|
||||
val: get_full_help(&SplitCommand.signature(), &SplitCommand.examples(), context),
|
||||
span: call.head,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::Command;
|
||||
// use super::ShellError;
|
||||
|
||||
// #[test]
|
||||
// fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
// use crate::examples::test as test_examples;
|
||||
|
||||
// test_examples(Command {})
|
||||
// }
|
||||
// }
|
9
crates/nu-command/src/strings/split/mod.rs
Normal file
9
crates/nu-command/src/strings/split/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub mod chars;
|
||||
// pub mod column;
|
||||
pub mod command;
|
||||
// pub mod row;
|
||||
|
||||
pub use chars::SubCommand as SplitChars;
|
||||
// pub use column::SubCommand as SplitColumn;
|
||||
pub use command::SplitCommand as Split;
|
||||
// pub use row::SubCommand as SplitRow;
|
|
@ -6,4 +6,5 @@ edition = "2018"
|
|||
[dependencies]
|
||||
nu-parser = { path = "../nu-parser" }
|
||||
nu-protocol = { path = "../nu-protocol" }
|
||||
nu-path = { path = "../nu-path" }
|
||||
nu-path = { path = "../nu-path" }
|
||||
itertools = "0.10.1"
|
315
crates/nu-engine/src/documentation.rs
Normal file
315
crates/nu-engine/src/documentation.rs
Normal file
|
@ -0,0 +1,315 @@
|
|||
use itertools::Itertools;
|
||||
use nu_protocol::{engine::EvaluationContext, Example, Signature, Span, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const COMMANDS_DOCS_DIR: &str = "docs/commands";
|
||||
|
||||
pub struct DocumentationConfig {
|
||||
no_subcommands: bool,
|
||||
//FIXME:
|
||||
#[allow(dead_code)]
|
||||
no_color: bool,
|
||||
brief: bool,
|
||||
}
|
||||
|
||||
impl Default for DocumentationConfig {
|
||||
fn default() -> Self {
|
||||
DocumentationConfig {
|
||||
no_subcommands: false,
|
||||
no_color: false,
|
||||
brief: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_doc(name: &str, context: &EvaluationContext) -> (Vec<String>, Vec<Value>) {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let engine_state = context.engine_state.borrow();
|
||||
|
||||
let command = engine_state
|
||||
.find_decl(name.as_bytes())
|
||||
.map(|decl_id| engine_state.get_decl(decl_id))
|
||||
.unwrap_or_else(|| panic!("Expected command '{}' from names to be in registry", name));
|
||||
|
||||
cols.push("name".to_string());
|
||||
vals.push(Value::String {
|
||||
val: name.into(),
|
||||
span: Span::unknown(),
|
||||
});
|
||||
|
||||
cols.push("usage".to_string());
|
||||
vals.push(Value::String {
|
||||
val: command.usage().to_owned(),
|
||||
span: Span::unknown(),
|
||||
});
|
||||
|
||||
if let Some(link) = retrieve_doc_link(name) {
|
||||
cols.push("doc_link".into());
|
||||
vals.push(Value::String {
|
||||
val: link,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
}
|
||||
|
||||
cols.push("documentation".to_owned());
|
||||
vals.push(Value::String {
|
||||
val: get_documentation(
|
||||
&command.signature(),
|
||||
&command.examples(),
|
||||
context,
|
||||
&DocumentationConfig {
|
||||
no_subcommands: true,
|
||||
no_color: true,
|
||||
brief: false,
|
||||
},
|
||||
),
|
||||
span: Span::unknown(),
|
||||
});
|
||||
|
||||
(cols, vals)
|
||||
}
|
||||
|
||||
// generate_docs gets the documentation from each command and returns a Table as output
|
||||
pub fn generate_docs(context: &EvaluationContext) -> Value {
|
||||
let signatures = context.get_signatures();
|
||||
|
||||
// cmap will map parent commands to it's subcommands e.g. to -> [to csv, to yaml, to bson]
|
||||
let mut cmap: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for sig in &signatures {
|
||||
if sig.name.contains(' ') {
|
||||
let mut split_name = sig.name.split_whitespace();
|
||||
let parent_name = split_name.next().expect("Expected a parent command name");
|
||||
if cmap.contains_key(parent_name) {
|
||||
let sub_names = cmap
|
||||
.get_mut(parent_name)
|
||||
.expect("Expected an entry for parent");
|
||||
sub_names.push(sig.name.to_owned());
|
||||
}
|
||||
} else {
|
||||
cmap.insert(sig.name.to_owned(), Vec::new());
|
||||
};
|
||||
}
|
||||
// Return documentation for each command
|
||||
// Sub-commands are nested under their respective parent commands
|
||||
let mut table = Vec::new();
|
||||
for sig in &signatures {
|
||||
// Must be a sub-command, skip since it's being handled underneath when we hit the parent command
|
||||
if !cmap.contains_key(&sig.name) {
|
||||
continue;
|
||||
}
|
||||
let mut row_entries = generate_doc(&sig.name, context);
|
||||
// Iterate over all the subcommands of the parent command
|
||||
let mut sub_table = Vec::new();
|
||||
for sub_name in cmap.get(&sig.name).unwrap_or(&Vec::new()) {
|
||||
let (cols, vals) = generate_doc(sub_name, context);
|
||||
sub_table.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
}
|
||||
|
||||
if !sub_table.is_empty() {
|
||||
row_entries.0.push("subcommands".into());
|
||||
row_entries.1.push(Value::List {
|
||||
vals: sub_table,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
}
|
||||
table.push(Value::Record {
|
||||
cols: row_entries.0,
|
||||
vals: row_entries.1,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
}
|
||||
Value::List {
|
||||
vals: table,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
fn retrieve_doc_link(name: &str) -> Option<String> {
|
||||
let doc_name = name.split_whitespace().join("_"); // Because .replace(" ", "_") didn't work
|
||||
let mut entries =
|
||||
std::fs::read_dir(COMMANDS_DOCS_DIR).expect("Directory for command docs are missing!");
|
||||
entries.find_map(|r| {
|
||||
r.map_or(None, |de| {
|
||||
if de.file_name().to_string_lossy() == format!("{}.{}", &doc_name, "md") {
|
||||
Some(format!("/commands/{}.{}", &doc_name, "html"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn get_documentation(
|
||||
sig: &Signature,
|
||||
examples: &[Example],
|
||||
context: &EvaluationContext,
|
||||
config: &DocumentationConfig,
|
||||
) -> String {
|
||||
let cmd_name = &sig.name;
|
||||
let mut long_desc = String::new();
|
||||
|
||||
let usage = &sig.usage;
|
||||
if !usage.is_empty() {
|
||||
long_desc.push_str(usage);
|
||||
long_desc.push_str("\n\n");
|
||||
}
|
||||
|
||||
let extra_usage = if config.brief { "" } else { &sig.extra_usage };
|
||||
if !extra_usage.is_empty() {
|
||||
long_desc.push_str(extra_usage);
|
||||
long_desc.push_str("\n\n");
|
||||
}
|
||||
|
||||
let mut subcommands = vec![];
|
||||
if !config.no_subcommands {
|
||||
let signatures = context.get_signatures();
|
||||
for sig in signatures {
|
||||
if sig.name.starts_with(&format!("{} ", cmd_name)) {
|
||||
subcommands.push(format!(" {} - {}", sig.name, sig.usage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut one_liner = String::new();
|
||||
one_liner.push_str(&sig.name);
|
||||
one_liner.push(' ');
|
||||
|
||||
for positional in &sig.required_positional {
|
||||
one_liner.push_str(&format!("<{}> ", positional.name));
|
||||
}
|
||||
for positional in &sig.optional_positional {
|
||||
one_liner.push_str(&format!("({}) ", positional.name));
|
||||
}
|
||||
|
||||
if sig.rest_positional.is_some() {
|
||||
one_liner.push_str("...args ");
|
||||
}
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
one_liner.push_str("<subcommand> ");
|
||||
}
|
||||
|
||||
if !sig.named.is_empty() {
|
||||
one_liner.push_str("{flags} ");
|
||||
}
|
||||
|
||||
long_desc.push_str(&format!("Usage:\n > {}\n", one_liner));
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
long_desc.push_str("\nSubcommands:\n");
|
||||
subcommands.sort();
|
||||
long_desc.push_str(&subcommands.join("\n"));
|
||||
long_desc.push('\n');
|
||||
}
|
||||
|
||||
if !sig.required_positional.is_empty()
|
||||
|| !sig.optional_positional.is_empty()
|
||||
|| sig.rest_positional.is_some()
|
||||
{
|
||||
long_desc.push_str("\nParameters:\n");
|
||||
for positional in &sig.required_positional {
|
||||
long_desc.push_str(&format!(" <{}> {}\n", positional.name, positional.desc));
|
||||
}
|
||||
for positional in &sig.optional_positional {
|
||||
long_desc.push_str(&format!(" ({}) {}\n", positional.name, positional.desc));
|
||||
}
|
||||
|
||||
if let Some(rest_positional) = &sig.rest_positional {
|
||||
long_desc.push_str(&format!(" ...args: {}\n", rest_positional.desc));
|
||||
}
|
||||
}
|
||||
if !sig.named.is_empty() {
|
||||
long_desc.push_str(&get_flags_section(sig))
|
||||
}
|
||||
|
||||
if !examples.is_empty() {
|
||||
long_desc.push_str("\nExamples:");
|
||||
}
|
||||
for example in examples {
|
||||
long_desc.push('\n');
|
||||
long_desc.push_str(" ");
|
||||
long_desc.push_str(example.description);
|
||||
|
||||
// if config.no_color {
|
||||
long_desc.push_str(&format!("\n > {}\n", example.example));
|
||||
// } else {
|
||||
// let colored_example =
|
||||
|
||||
// crate::shell::painter::Painter::paint_string(example.example, scope, &palette);
|
||||
// long_desc.push_str(&format!("\n > {}\n", colored_example));
|
||||
// }
|
||||
}
|
||||
|
||||
long_desc.push('\n');
|
||||
|
||||
long_desc
|
||||
}
|
||||
|
||||
fn get_flags_section(signature: &Signature) -> String {
|
||||
let mut long_desc = String::new();
|
||||
long_desc.push_str("\nFlags:\n");
|
||||
for flag in &signature.named {
|
||||
let msg = if let Some(arg) = &flag.arg {
|
||||
if let Some(short) = flag.short {
|
||||
if flag.required {
|
||||
format!(
|
||||
" -{}, --{} (required parameter){:?} {}\n",
|
||||
short, flag.long, arg, flag.desc
|
||||
)
|
||||
} else {
|
||||
format!(" -{}, --{} {:?} {}\n", short, flag.long, arg, flag.desc)
|
||||
}
|
||||
} else if flag.required {
|
||||
format!(
|
||||
" --{} (required parameter){:?} {}\n",
|
||||
flag.long, arg, flag.desc
|
||||
)
|
||||
} else {
|
||||
format!(" --{} {:?} {}\n", flag.long, arg, flag.desc)
|
||||
}
|
||||
} else if let Some(short) = flag.short {
|
||||
if flag.required {
|
||||
format!(
|
||||
" -{}, --{} (required parameter) {}\n",
|
||||
short, flag.long, flag.desc
|
||||
)
|
||||
} else {
|
||||
format!(" -{}, --{} {}\n", short, flag.long, flag.desc)
|
||||
}
|
||||
} else if flag.required {
|
||||
format!(" --{} (required parameter) {}\n", flag.long, flag.desc)
|
||||
} else {
|
||||
format!(" --{} {}\n", flag.long, flag.desc)
|
||||
};
|
||||
long_desc.push_str(&msg);
|
||||
}
|
||||
long_desc
|
||||
}
|
||||
|
||||
pub fn get_brief_help(
|
||||
sig: &Signature,
|
||||
examples: &[Example],
|
||||
context: &EvaluationContext,
|
||||
) -> String {
|
||||
get_documentation(
|
||||
sig,
|
||||
examples,
|
||||
context,
|
||||
&DocumentationConfig {
|
||||
no_subcommands: false,
|
||||
no_color: false,
|
||||
brief: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_full_help(sig: &Signature, examples: &[Example], context: &EvaluationContext) -> String {
|
||||
get_documentation(sig, examples, context, &DocumentationConfig::default())
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
mod call_ext;
|
||||
mod documentation;
|
||||
mod eval;
|
||||
mod from_value;
|
||||
|
||||
pub use call_ext::CallExt;
|
||||
pub use documentation::{generate_docs, get_brief_help, get_documentation, get_full_help};
|
||||
pub use eval::{eval_block, eval_expression, eval_operator};
|
||||
pub use from_value::FromValue;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::Command;
|
||||
use crate::{ast::Block, BlockId, DeclId, Signature, Span, Type, VarId};
|
||||
use crate::{ast::Block, BlockId, DeclId, Example, Signature, Span, Type, VarId};
|
||||
use core::panic;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
|
@ -178,7 +178,7 @@ impl EngineState {
|
|||
.expect("internal error: missing declaration")
|
||||
}
|
||||
|
||||
pub fn get_decls(&self) -> Vec<Signature> {
|
||||
pub fn get_signatures(&self) -> Vec<Signature> {
|
||||
let mut output = vec![];
|
||||
for decl in self.decls.iter() {
|
||||
if decl.get_block_id().is_none() {
|
||||
|
@ -193,6 +193,21 @@ impl EngineState {
|
|||
output
|
||||
}
|
||||
|
||||
pub fn get_signatures_with_examples(&self) -> Vec<(Signature, Vec<Example>)> {
|
||||
let mut output = vec![];
|
||||
for decl in self.decls.iter() {
|
||||
if decl.get_block_id().is_none() {
|
||||
let mut signature = (*decl).signature();
|
||||
signature.usage = decl.usage().to_string();
|
||||
signature.extra_usage = decl.extra_usage().to_string();
|
||||
|
||||
output.push((signature, decl.examples()));
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn get_block(&self, block_id: BlockId) -> &Block {
|
||||
self.blocks
|
||||
.get(block_id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::EngineState;
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{ShellError, Signature, Value, VarId};
|
||||
use crate::{Example, ShellError, Signature, Value, VarId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EvaluationContext {
|
||||
|
@ -47,8 +47,12 @@ impl EvaluationContext {
|
|||
self.stack.print_stack();
|
||||
}
|
||||
|
||||
pub fn get_commands_info(&self) -> Vec<Signature> {
|
||||
self.engine_state.borrow().get_decls()
|
||||
pub fn get_signatures(&self) -> Vec<Signature> {
|
||||
self.engine_state.borrow().get_signatures()
|
||||
}
|
||||
|
||||
pub fn get_signatures_with_examples(&self) -> Vec<(Signature, Vec<Example>)> {
|
||||
self.engine_state.borrow().get_signatures_with_examples()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,16 @@ pub enum ShellError {
|
|||
rhs_span: Span,
|
||||
},
|
||||
|
||||
#[error("Pipeline mismatch.")]
|
||||
#[diagnostic(code(nu::shell::pipeline_mismatch), url(docsrs))]
|
||||
PipelineMismatch {
|
||||
expected: Type,
|
||||
#[label("expected: {expected}")]
|
||||
expected_span: Span,
|
||||
#[label("value originates from here")]
|
||||
origin: Span,
|
||||
},
|
||||
|
||||
#[error("Unsupported operator: {0}.")]
|
||||
#[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))]
|
||||
UnsupportedOperator(Operator, #[label = "unsupported operator"] Span),
|
||||
|
@ -79,6 +89,10 @@ pub enum ShellError {
|
|||
#[diagnostic(code(nu::shell::unsupported_input), url(docsrs))]
|
||||
UnsupportedInput(String, #[label("{0}")] Span),
|
||||
|
||||
#[error("Command not found")]
|
||||
#[diagnostic(code(nu::shell::command_not_found), url(docsrs))]
|
||||
CommandNotFound(#[label("command not found")] Span),
|
||||
|
||||
#[error("Flag not found")]
|
||||
#[diagnostic(code(nu::shell::flag_not_found), url(docsrs))]
|
||||
FlagNotFound(String, #[label("{0} not found")] Span),
|
||||
|
|
Loading…
Reference in a new issue