mirror of
https://github.com/nushell/nushell.git
synced 2024-12-23 07:30:13 +01:00
Merge pull request #108 from nushell/help_and_start_split
Port help and start porting split
This commit is contained in:
commit
44fbf0fce3
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -551,6 +551,7 @@ dependencies = [
|
|||||||
name = "nu-engine"
|
name = "nu-engine"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
"nu-parser",
|
"nu-parser",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
|
@ -15,7 +15,7 @@ impl Command for Do {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("do").required(
|
Signature::build("do").desc(self.usage()).required(
|
||||||
"block",
|
"block",
|
||||||
SyntaxShape::Block(Some(vec![])),
|
SyntaxShape::Block(Some(vec![])),
|
||||||
"the block to run",
|
"the block to run",
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
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;
|
pub struct Help;
|
||||||
|
|
||||||
@ -73,11 +73,11 @@ impl Command for Help {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
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 find: Option<Spanned<String>> = call.get_flag(context, "find")?;
|
||||||
let rest: Vec<Spanned<String>> = call.rest(context, 0)?;
|
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 {
|
if let Some(f) = find {
|
||||||
let search_string = f.item;
|
let search_string = f.item;
|
||||||
@ -87,29 +87,36 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
|||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
let key = cmd.name.clone();
|
let key = cmd.0.name.clone();
|
||||||
let c = cmd.usage.clone();
|
let c = cmd.0.usage.clone();
|
||||||
let e = cmd.extra_usage.clone();
|
let e = cmd.0.extra_usage.clone();
|
||||||
if key.to_lowercase().contains(&search_string)
|
if key.to_lowercase().contains(&search_string)
|
||||||
|| c.to_lowercase().contains(&search_string)
|
|| c.to_lowercase().contains(&search_string)
|
||||||
|| e.to_lowercase().contains(&search_string)
|
|| e.to_lowercase().contains(&search_string)
|
||||||
{
|
{
|
||||||
cols.push("name".into());
|
cols.push("name".into());
|
||||||
vals.push(Value::String { val: key, span });
|
vals.push(Value::String {
|
||||||
|
val: key,
|
||||||
|
span: head,
|
||||||
|
});
|
||||||
|
|
||||||
cols.push("usage".into());
|
cols.push("usage".into());
|
||||||
vals.push(Value::String { val: c, span });
|
vals.push(Value::String { val: c, span: head });
|
||||||
|
|
||||||
cols.push("extra_usage".into());
|
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 {
|
return Ok(Value::List {
|
||||||
vals: found_cmds_vec,
|
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 cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
let key = cmd.name.clone();
|
let key = cmd.0.name.clone();
|
||||||
let c = cmd.usage.clone();
|
let c = cmd.0.usage.clone();
|
||||||
let e = cmd.extra_usage.clone();
|
let e = cmd.0.extra_usage.clone();
|
||||||
|
|
||||||
cols.push("name".into());
|
cols.push("name".into());
|
||||||
vals.push(Value::String { val: key, span });
|
vals.push(Value::String {
|
||||||
|
val: key,
|
||||||
|
span: head,
|
||||||
|
});
|
||||||
|
|
||||||
cols.push("usage".into());
|
cols.push("usage".into());
|
||||||
vals.push(Value::String { val: c, span });
|
vals.push(Value::String { val: c, span: head });
|
||||||
|
|
||||||
cols.push("extra_usage".into());
|
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 {
|
} else {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
for r in rest {
|
for r in &rest {
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
name.push(' ');
|
name.push(' ');
|
||||||
}
|
}
|
||||||
@ -147,31 +167,24 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for cmd in full_commands {
|
for cmd in full_commands {
|
||||||
let mut cols = vec![];
|
if cmd.0.name == name {
|
||||||
let mut vals = vec![];
|
let help = get_full_help(&cmd.0, &cmd.1, context);
|
||||||
|
output.push_str(&help);
|
||||||
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 !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
|
// 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 {
|
Ok(Value::String {
|
||||||
val: msg.into(),
|
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(Mv));
|
||||||
working_set.add_decl(Box::new(Ps));
|
working_set.add_decl(Box::new(Ps));
|
||||||
working_set.add_decl(Box::new(Select));
|
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(Sys));
|
||||||
working_set.add_decl(Box::new(Table));
|
working_set.add_decl(Box::new(Table));
|
||||||
working_set.add_decl(Box::new(Touch));
|
working_set.add_decl(Box::new(Touch));
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
mod build_string;
|
mod build_string;
|
||||||
|
mod split;
|
||||||
|
|
||||||
pub use build_string::BuildString;
|
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]
|
[dependencies]
|
||||||
nu-parser = { path = "../nu-parser" }
|
nu-parser = { path = "../nu-parser" }
|
||||||
nu-protocol = { path = "../nu-protocol" }
|
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 call_ext;
|
||||||
|
mod documentation;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod from_value;
|
mod from_value;
|
||||||
|
|
||||||
pub use call_ext::CallExt;
|
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 eval::{eval_block, eval_expression, eval_operator};
|
||||||
pub use from_value::FromValue;
|
pub use from_value::FromValue;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::Command;
|
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 core::panic;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
@ -178,7 +178,7 @@ impl EngineState {
|
|||||||
.expect("internal error: missing declaration")
|
.expect("internal error: missing declaration")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_decls(&self) -> Vec<Signature> {
|
pub fn get_signatures(&self) -> Vec<Signature> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for decl in self.decls.iter() {
|
for decl in self.decls.iter() {
|
||||||
if decl.get_block_id().is_none() {
|
if decl.get_block_id().is_none() {
|
||||||
@ -193,6 +193,21 @@ impl EngineState {
|
|||||||
output
|
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 {
|
pub fn get_block(&self, block_id: BlockId) -> &Block {
|
||||||
self.blocks
|
self.blocks
|
||||||
.get(block_id)
|
.get(block_id)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::EngineState;
|
use super::EngineState;
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use crate::{ShellError, Signature, Value, VarId};
|
use crate::{Example, ShellError, Signature, Value, VarId};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EvaluationContext {
|
pub struct EvaluationContext {
|
||||||
@ -47,8 +47,12 @@ impl EvaluationContext {
|
|||||||
self.stack.print_stack();
|
self.stack.print_stack();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_commands_info(&self) -> Vec<Signature> {
|
pub fn get_signatures(&self) -> Vec<Signature> {
|
||||||
self.engine_state.borrow().get_decls()
|
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,
|
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}.")]
|
#[error("Unsupported operator: {0}.")]
|
||||||
#[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))]
|
#[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))]
|
||||||
UnsupportedOperator(Operator, #[label = "unsupported operator"] Span),
|
UnsupportedOperator(Operator, #[label = "unsupported operator"] Span),
|
||||||
@ -79,6 +89,10 @@ pub enum ShellError {
|
|||||||
#[diagnostic(code(nu::shell::unsupported_input), url(docsrs))]
|
#[diagnostic(code(nu::shell::unsupported_input), url(docsrs))]
|
||||||
UnsupportedInput(String, #[label("{0}")] Span),
|
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")]
|
#[error("Flag not found")]
|
||||||
#[diagnostic(code(nu::shell::flag_not_found), url(docsrs))]
|
#[diagnostic(code(nu::shell::flag_not_found), url(docsrs))]
|
||||||
FlagNotFound(String, #[label("{0} not found")] Span),
|
FlagNotFound(String, #[label("{0} not found")] Span),
|
||||||
|
Loading…
Reference in New Issue
Block a user