Cratification: Break out nu_cmd_lang into a separate crate (#8181)

# Description

This breaks out the core_commands into a separate crate called
nu_cmd_lang

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

_(List of all changes that impact the user experience here. This helps
us keep track of breaking changes.)_

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
Michael Angerman
2023-02-24 07:54:42 -08:00
committed by GitHub
parent d0aefa99eb
commit 585e104608
59 changed files with 531 additions and 60 deletions

View File

@ -15,6 +15,7 @@ bench = false
[dependencies]
nu-ansi-term = "0.46.0"
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.76.1" }
nu-color-config = { path = "../nu-color-config", version = "0.76.1" }
nu-engine = { path = "../nu-engine", version = "0.76.1" }
nu-explore = { path = "../nu-explore", version = "0.76.1" }

View File

@ -1,68 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Alias;
impl Command for Alias {
fn name(&self) -> &str {
"alias"
}
fn usage(&self) -> &str {
"Alias a command (with optional flags) to a new name"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["abbr", "aka", "fn", "func", "function"]
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Alias ll to ls -l",
example: "alias ll = ls -l",
result: Some(Value::nothing(Span::test_data())),
},
Example {
description: "Make an alias that makes a list of all custom commands",
example: "alias customs = ($nu.scope.commands | where is_custom | get command)",
result: Some(Value::nothing(Span::test_data())),
},
]
}
}

View File

@ -1,49 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
#[derive(Clone)]
pub struct Break;
impl Command for Break {
fn name(&self) -> &str {
"break"
}
fn usage(&self) -> &str {
"Break a loop"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("break")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Err(ShellError::Break(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Break out of a loop",
example: r#"loop { break }"#,
result: None,
}]
}
}

View File

@ -1,85 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::ReplOperation;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::IntoPipelineData;
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct Commandline;
impl Command for Commandline {
fn name(&self) -> &str {
"commandline"
}
fn signature(&self) -> Signature {
Signature::build("commandline")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.switch(
"append",
"appends the string to the end of the buffer",
Some('a'),
)
.switch(
"insert",
"inserts the string into the buffer at the cursor position",
Some('i'),
)
.switch(
"replace",
"replaces the current contents of the buffer (default)",
Some('r'),
)
.optional(
"cmd",
SyntaxShape::String,
"the string to perform the operation with",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"View or modify the current command line input buffer"
}
fn search_terms(&self) -> Vec<&str> {
vec!["repl", "interactive"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
let mut ops = engine_state
.repl_operation_queue
.lock()
.expect("repl op queue mutex");
ops.push_back(if call.has_flag("append") {
ReplOperation::Append(cmd.as_string()?)
} else if call.has_flag("insert") {
ReplOperation::Insert(cmd.as_string()?)
} else {
ReplOperation::Replace(cmd.as_string()?)
});
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
} else if let Some(ref cmd) = *engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex")
{
Ok(Value::String {
val: cmd.clone(),
span: call.head,
}
.into_pipeline_data())
} else {
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
}
}
}

View File

@ -1,104 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Const;
impl Command for Const {
fn name(&self) -> &str {
"const"
}
fn usage(&self) -> &str {
"Create a parse-time constant."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("const")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by constant value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["set", "let"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
if let Some(constval) = engine_state.find_constant(var_id, &[]) {
// Instead of creating a second copy of the value in the stack, we could change
// stack.get_var() to check engine_state.find_constant().
stack.add_var(var_id, constval.clone());
Ok(PipelineData::empty())
} else {
Err(ShellError::NushellFailedSpanned(
"Missing Constant".to_string(),
"constant not added by the parser".to_string(),
call.head,
))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create a new parse-time constant.",
example: "const x = 10",
result: None,
},
Example {
description: "Create a composite constant value",
example: "const x = { a: 10, b: 20 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Const {})
}
#[test]
fn test_command_type() {
assert!(matches!(Const.command_type(), CommandType::Keyword));
}
}

View File

@ -1,49 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
#[derive(Clone)]
pub struct Continue;
impl Command for Continue {
fn name(&self) -> &str {
"continue"
}
fn usage(&self) -> &str {
"Continue a loop from the next iteration"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("continue")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Err(ShellError::Continue(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Continue a loop from the next iteration",
example: r#"for i in 1..10 { if $i == 5 { continue }; print $i }"#,
result: None,
}]
}
}

View File

@ -1,61 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Def;
impl Command for Def {
fn name(&self) -> &str {
"def"
}
fn usage(&self) -> &str {
"Define a custom command"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("def")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("body", SyntaxShape::Closure(None), "body of the definition")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Define a command and run it",
example: r#"def say-hi [] { echo 'hi' }; say-hi"#,
result: Some(Value::test_string("hi")),
},
Example {
description: "Define a command and run it with parameter(s)",
example: r#"def say-sth [sth: string] { echo $sth }; say-sth hi"#,
result: Some(Value::test_string("hi")),
},
]
}
}

View File

@ -1,55 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct DefEnv;
impl Command for DefEnv {
fn name(&self) -> &str {
"def-env"
}
fn usage(&self) -> &str {
"Define a custom command, which participates in the caller environment"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html
"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Set environment variable by call a custom command",
example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::test_string("BAZ")),
}]
}
}

View File

@ -1,105 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct Describe;
impl Command for Describe {
fn name(&self) -> &str {
"describe"
}
fn usage(&self) -> &str {
"Describe the type and structure of the value(s) piped in."
}
fn signature(&self) -> Signature {
Signature::build("describe")
.input_output_types(vec![(Type::Any, Type::String)])
.switch(
"no-collect",
"do not collect streams of structured data",
Some('n'),
)
.category(Category::Core)
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let no_collect: bool = call.has_flag("no-collect");
let description = match input {
PipelineData::ExternalStream { .. } => "raw input".into(),
PipelineData::ListStream(_, _) => {
if no_collect {
"stream".into()
} else {
let value = input.into_value(head);
let base_description = match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
};
format!("{base_description} (stream)")
}
}
_ => {
let value = input.into_value(head);
match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
}
}
};
Ok(Value::String {
val: description,
span: head,
}
.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Describe the type of a string",
example: "'hello' | describe",
result: Some(Value::test_string("string")),
},
Example {
description: "Describe a stream of data, collecting it first",
example: "[1 2 3] | each {|i| $i} | describe",
result: Some(Value::test_string("list<int> (stream)")),
},
Example {
description: "Describe the input but do not collect streams",
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
result: Some(Value::test_string("stream")),
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["type", "typeof", "info", "structure"]
}
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Describe;
use crate::test_examples;
test_examples(Describe {})
}
}

View File

@ -1,304 +0,0 @@
use std::thread;
use nu_engine::{eval_block_with_early_return, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
Type, Value,
};
#[derive(Clone)]
pub struct Do;
impl Command for Do {
fn name(&self) -> &str {
"do"
}
fn usage(&self) -> &str {
"Run a closure, providing it with the pipeline input"
}
fn signature(&self) -> Signature {
Signature::build("do")
.required("closure", SyntaxShape::Any, "the closure to run")
.input_output_types(vec![(Type::Any, Type::Any)])
.switch(
"ignore-errors",
"ignore errors as the closure runs",
Some('i'),
)
.switch(
"ignore-shell-errors",
"ignore shell errors as the closure runs",
Some('s'),
)
.switch(
"ignore-program-errors",
"ignore external program errors as the closure runs",
Some('p'),
)
.switch(
"capture-errors",
"catch errors as the closure runs, and return them",
Some('c'),
)
.rest("rest", SyntaxShape::Any, "the parameter(s) for the closure")
.category(Category::Core)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let block: Closure = call.req(engine_state, stack, 0)?;
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
let ignore_all_errors = call.has_flag("ignore-errors");
let ignore_shell_errors = ignore_all_errors || call.has_flag("ignore-shell-errors");
let ignore_program_errors = ignore_all_errors || call.has_flag("ignore-program-errors");
let capture_errors = call.has_flag("capture-errors");
let mut stack = stack.captures_to_stack(&block.captures);
let block = engine_state.get_block(block.block_id);
let params: Vec<_> = block
.signature
.required_positional
.iter()
.chain(block.signature.optional_positional.iter())
.collect();
for param in params.iter().zip(&rest) {
if let Some(var_id) = param.0.var_id {
stack.add_var(var_id, param.1.clone())
}
}
if let Some(param) = &block.signature.rest_positional {
if rest.len() > params.len() {
let mut rest_items = vec![];
for r in rest.into_iter().skip(params.len()) {
rest_items.push(r);
}
let span = if let Some(rest_item) = rest_items.first() {
rest_item.span()?
} else {
call.head
};
stack.add_var(
param
.var_id
.expect("Internal error: rest positional parameter lacks var_id"),
Value::List {
vals: rest_items,
span,
},
)
}
}
let result = eval_block_with_early_return(
engine_state,
&mut stack,
block,
input,
call.redirect_stdout,
call.redirect_stdout,
);
match result {
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code,
span,
metadata,
trim_end_newline,
}) if capture_errors => {
// Use a thread to receive stdout message.
// Or we may get a deadlock if child process sends out too much bytes to stderr.
//
// For example: in normal linux system, stderr pipe's limit is 65535 bytes.
// if child process sends out 65536 bytes, the process will be hanged because no consumer
// consumes the first 65535 bytes
// So we need a thread to receive stdout message, then the current thread can continue to consume
// stderr messages.
let stdout_handler = stdout.map(|stdout_stream| {
thread::Builder::new()
.name("stderr redirector".to_string())
.spawn(move || {
let ctrlc = stdout_stream.ctrlc.clone();
let span = stdout_stream.span;
RawStream::new(
Box::new(
vec![stdout_stream.into_bytes().map(|s| s.item)].into_iter(),
),
ctrlc,
span,
None,
)
})
.expect("Failed to create thread")
});
// Intercept stderr so we can return it in the error if the exit code is non-zero.
// The threading issues mentioned above dictate why we also need to intercept stdout.
let mut stderr_ctrlc = None;
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => {
stderr_ctrlc = stderr_stream.ctrlc.clone();
stderr_stream.into_string().map(|s| s.item)?
}
};
let stdout = if let Some(handle) = stdout_handler {
match handle.join() {
Err(err) => {
return Err(ShellError::ExternalCommand(
"Fail to receive external commands stdout message".to_string(),
format!("{err:?}"),
span,
));
}
Ok(res) => Some(res),
}
} else {
None
};
let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code {
None => vec![],
Some(exit_code_stream) => {
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
exit_code_stream.into_iter().collect()
}
};
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
if *code != 0 {
return Err(ShellError::ExternalCommand(
"External command failed".to_string(),
stderr_msg,
span,
));
}
}
Ok(PipelineData::ExternalStream {
stdout,
stderr: Some(RawStream::new(
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
stderr_ctrlc,
span,
None,
)),
exit_code: Some(ListStream::from_stream(
exit_code.into_iter(),
exit_code_ctrlc,
)),
span,
metadata,
trim_end_newline,
})
}
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: _,
span,
metadata,
trim_end_newline,
}) if ignore_program_errors && !call.redirect_stdout => {
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: None,
span,
metadata,
trim_end_newline,
})
}
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_shell_errors => {
Ok(PipelineData::empty())
}
Ok(PipelineData::ListStream(ls, metadata)) if ignore_shell_errors => {
// check if there is a `Value::Error` in given list stream first.
let mut values = vec![];
let ctrlc = ls.ctrlc.clone();
for v in ls {
if let Value::Error { .. } = v {
values.push(Value::nothing(call.head));
} else {
values.push(v)
}
}
Ok(PipelineData::ListStream(
ListStream::from_stream(values.into_iter(), ctrlc),
metadata,
))
}
r => r,
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Run the closure",
example: r#"do { echo hello }"#,
result: Some(Value::test_string("hello")),
},
Example {
description: "Run a stored first-class closure",
example: r#"let text = "I am enclosed"; let hello = {|| echo $text}; do $hello"#,
result: Some(Value::test_string("I am enclosed")),
},
Example {
description: "Run the closure and ignore both shell and external program errors",
example: r#"do -i { thisisnotarealcommand }"#,
result: None,
},
Example {
description: "Run the closure and ignore shell errors",
example: r#"do -s { thisisnotarealcommand }"#,
result: None,
},
Example {
description: "Run the closure and ignore external program errors",
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
result: None,
},
Example {
description: "Abort the pipeline if a program returns a non-zero exit code",
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
result: None,
},
Example {
description: "Run the closure, with a positional parameter",
example: r#"do {|x| 100 + $x } 77"#,
result: Some(Value::test_int(177)),
},
Example {
description: "Run the closure, with input",
example: r#"77 | do {|x| 100 + $in }"#,
result: None, // TODO: returns 177
},
]
}
}
mod test {
#[test]
fn test_examples() {
use super::Do;
use crate::test_examples;
test_examples(Do {})
}
}

View File

@ -1,87 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
pub struct Echo;
impl Command for Echo {
fn name(&self) -> &str {
"echo"
}
fn usage(&self) -> &str {
"Returns its arguments, ignoring the piped-in value."
}
fn signature(&self) -> Signature {
Signature::build("echo")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.rest("rest", SyntaxShape::Any, "the values to echo")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"When given no arguments, it returns an empty string. When given one argument,
it returns it. Otherwise, it returns a list of the arguments. There is usually
little reason to use this over just writing the values as-is."#
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
call.rest(engine_state, stack, 0).map(|to_be_echoed| {
let n = to_be_echoed.len();
match n.cmp(&1usize) {
// More than one value is converted in a stream of values
std::cmp::Ordering::Greater => PipelineData::ListStream(
ListStream::from_stream(to_be_echoed.into_iter(), engine_state.ctrlc.clone()),
None,
),
// But a single value can be forwarded as it is
std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None),
// When there are no elements, we echo the empty string
std::cmp::Ordering::Less => PipelineData::Value(Value::string("", call.head), None),
}
})
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Put a list of numbers in the pipeline. This is the same as [1 2 3].",
example: "echo 1 2 3",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
},
Example {
description:
"Returns the piped-in value, by using the special $in variable to obtain it.",
example: "echo $in",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Echo;
use crate::test_examples;
test_examples(Echo {})
}
}

View File

@ -1,144 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct ErrorMake;
impl Command for ErrorMake {
fn name(&self) -> &str {
"error make"
}
fn signature(&self) -> Signature {
Signature::build("error make")
.input_output_types(vec![(Type::Nothing, Type::Error)])
.required("error_struct", SyntaxShape::Record, "the error to create")
.switch(
"unspanned",
"remove the origin label from the error",
Some('u'),
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Create an error."
}
fn search_terms(&self) -> Vec<&str> {
vec!["panic", "crash", "throw"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let arg: Value = call.req(engine_state, stack, 0)?;
let unspanned = call.has_flag("unspanned");
if unspanned {
Err(make_error(&arg, None).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
} else {
Err(make_error(&arg, Some(span)).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create a custom error for a custom command",
example: r#"def foo [x] {
let span = (metadata $x).span;
error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } }
}"#,
result: None,
},
Example {
description: "Create a simple custom error for a custom command",
example: r#"def foo [x] {
error make {msg: "this is fishy"}
}"#,
result: None,
},
]
}
}
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
if let Value::Record { .. } = &value {
let msg = value.get_data_by_key("msg");
let label = value.get_data_by_key("label");
match (msg, &label) {
(Some(Value::String { val: message, .. }), Some(label)) => {
let label_start = label.get_data_by_key("start");
let label_end = label.get_data_by_key("end");
let label_text = label.get_data_by_key("text");
match (label_start, label_end, label_text) {
(
Some(Value::Int { val: start, .. }),
Some(Value::Int { val: end, .. }),
Some(Value::String {
val: label_text, ..
}),
) => Some(ShellError::GenericError(
message,
label_text,
Some(Span::new(start as usize, end as usize)),
None,
Vec::new(),
)),
(
None,
None,
Some(Value::String {
val: label_text, ..
}),
) => Some(ShellError::GenericError(
message,
label_text,
throw_span,
None,
Vec::new(),
)),
_ => None,
}
}
(Some(Value::String { val: message, .. }), None) => Some(ShellError::GenericError(
message,
"originates from here".to_string(),
throw_span,
None,
Vec::new(),
)),
_ => None,
}
} else {
None
}
}

View File

@ -1,66 +0,0 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct ExportCommand;
impl Command for ExportCommand {
fn name(&self) -> &str {
"export"
}
fn signature(&self) -> Signature {
Signature::build("export")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn usage(&self) -> &str {
"Export definitions or environment variables from a module."
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
&ExportCommand.signature(),
&ExportCommand.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Export a definition from a module",
example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#,
result: Some(Value::test_string("hello")),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

View File

@ -1,59 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct ExportAlias;
impl Command for ExportAlias {
fn name(&self) -> &str {
"export alias"
}
fn usage(&self) -> &str {
"Define an alias and export it from a module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "export an alias of ll to ls -l, from a module",
example: "export alias ll = ls -l",
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["aka", "abbr", "module"]
}
}

View File

@ -1,58 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct ExportDef;
impl Command for ExportDef {
fn name(&self) -> &str {
"export def"
}
fn usage(&self) -> &str {
"Define a custom command and export it from a module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::test_string("foo")),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

View File

@ -1,84 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct ExportDefEnv;
impl Command for ExportDefEnv {
fn name(&self) -> &str {
"export def-env"
}
fn usage(&self) -> &str {
"Define a custom command that participates in the environment and export it from a module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html
=== EXTRA NOTE ===
All blocks are scoped, including variable definition and environment variable changes.
Because of this, the following doesn't work:
export def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
if $arg != "" {
cd $arg
} else {
cd $fall_back_path
}
}
Instead, you have to use cd in the top level scope:
export def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
let path = if $arg != "" {
$arg
} else {
$fall_back_path
}
cd $path
}"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

View File

@ -1,55 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct ExportExtern;
impl Command for ExportExtern {
fn name(&self) -> &str {
"export extern"
}
fn usage(&self) -> &str {
"Define an extern and export it from a module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Export the signature for an external command",
example: r#"export extern echo [text: string]"#,
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["signature", "module", "declare"]
}
}

View File

@ -1,65 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct ExportUse;
impl Command for ExportUse {
fn name(&self) -> &str {
"export use"
}
fn usage(&self) -> &str {
"Use definitions from a module and export them from this module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Re-export a command from another module",
example: r#"module spam { export def foo [] { "foo" } }
module eggs { export use spam foo }
use eggs foo
foo
"#,
result: Some(Value::test_string("foo")),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["reexport", "import", "module"]
}
}

View File

@ -1,51 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Extern;
impl Command for Extern {
fn name(&self) -> &str {
"extern"
}
fn usage(&self) -> &str {
"Define a signature for an external command"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Write a signature for an external command",
example: r#"extern echo [text: string]"#,
result: None,
}]
}
}

View File

@ -1,222 +0,0 @@
use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct For;
impl Command for For {
fn name(&self) -> &str {
"for"
}
fn usage(&self) -> &str {
"Loop over a range"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("for")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required(
"var_name",
SyntaxShape::VarWithOptType,
"name of the looping variable",
)
.required(
"range",
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)),
"range of the loop",
)
.required("block", SyntaxShape::Block, "the block to run")
.switch(
"numbered",
"return a numbered item ($it.index and $it.item)",
Some('n'),
)
.creates_scope()
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call
.positional_nth(1)
.expect("checked through parser")
.as_keyword()
.expect("internal error: missing keyword");
let values = eval_expression(engine_state, stack, keyword_expr)?;
let block: Block = call.req(engine_state, stack, 2)?;
let numbered = call.has_flag("numbered");
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let block = engine_state.get_block(block.block_id).clone();
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
match values {
Value::List { vals, .. } => {
for (idx, x) in ListStream::from_stream(vals.into_iter(), ctrlc).enumerate() {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
stack.add_var(
var_id,
if numbered {
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![Value::int(idx as i64, head), x],
span: head,
}
} else {
x
},
);
//let block = engine_state.get_block(block_id);
match eval_block(
&engine_state,
stack,
&block,
PipelineData::empty(),
redirect_stdout,
redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
}
}
}
Value::Range { val, .. } => {
for (idx, x) in val.into_range_iter(ctrlc)?.enumerate() {
stack.add_var(
var_id,
if numbered {
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![Value::int(idx as i64, head), x],
span: head,
}
} else {
x
},
);
//let block = engine_state.get_block(block_id);
match eval_block(
&engine_state,
stack,
&block,
PipelineData::empty(),
redirect_stdout,
redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
}
}
}
x => {
stack.add_var(var_id, x);
eval_block(
&engine_state,
stack,
&block,
PipelineData::empty(),
redirect_stdout,
redirect_stderr,
)?
.into_value(head);
}
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Echo the square of each integer",
example: "for x in [1 2 3] { print ($x * $x) }",
result: None,
},
Example {
description: "Work with elements of a range",
example: "for $x in 1..3 { print $x }",
result: None,
},
Example {
description: "Number each item and echo a message",
example:
"for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(For {})
}
}

View File

@ -1,254 +0,0 @@
use crate::help_aliases::help_aliases;
use crate::help_commands::help_commands;
use crate::help_modules::help_modules;
use fancy_regex::Regex;
use nu_ansi_term::{
Color::{Red, White},
Style,
};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Help;
impl Command for Help {
fn name(&self) -> &str {
"help"
}
fn signature(&self) -> Signature {
Signature::build("help")
.input_output_types(vec![(Type::Nothing, Type::String)])
.rest(
"rest",
SyntaxShape::String,
"the name of command, alias or module to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in command names, usage, and search terms",
Some('f'),
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Display help information about different parts of Nushell."
}
fn extra_usage(&self) -> &str {
r#"`help word` searches for "word" in commands, aliases and modules, in that order."#
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
if rest.is_empty() && find.is_none() {
let msg = r#"Welcome to Nushell.
Here are some tips to help you get started.
* help -h or help help - show available `help` subcommands and examples
* help commands - list all available commands
* help <name> - display help about a particular command, alias, or module
* help --find <text to search> - search through all help commands table
Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character.
Each stage in the pipeline works together to load, parse, and display information to you.
[Examples]
List the files in the current directory, sorted by size:
ls | sort-by size
Get information about the current system:
sys | get host
Get the processes on your system actively using CPU:
ps | where cpu > 0
You can also learn more at https://www.nushell.sh/book/"#;
Ok(Value::string(msg, head).into_pipeline_data())
} else if find.is_some() {
help_commands(engine_state, stack, call)
} else {
let result = help_aliases(engine_state, stack, call);
let result = if let Err(ShellError::AliasNotFound(_)) = result {
help_commands(engine_state, stack, call)
} else {
result
};
let result = if let Err(ShellError::CommandNotFound(_)) = result {
help_modules(engine_state, stack, call)
} else {
result
};
if let Err(ShellError::ModuleNotFoundAtRuntime(_, _)) = result {
let rest_spans: Vec<Span> = rest.iter().map(|arg| arg.span).collect();
Err(ShellError::NotFound(span(&rest_spans)))
} else {
result
}
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show help for single command, alias, or module",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command, alias, or module",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command names, usage and search terms",
example: "help --find char",
result: None,
},
]
}
}
pub fn highlight_search_in_table(
table: Vec<Value>, // list of records
search_string: &str,
searched_cols: &[&str],
string_style: &Style,
) -> Result<Vec<Value>, ShellError> {
let orig_search_string = search_string;
let search_string = search_string.to_lowercase();
let mut matches = vec![];
for record in table {
let (cols, mut vals, record_span) = if let Value::Record { cols, vals, span } = record {
(cols, vals, span)
} else {
return Err(ShellError::NushellFailedSpanned(
"Expected record".to_string(),
format!("got {}", record.get_type()),
record.span()?,
));
};
let has_match = cols.iter().zip(vals.iter_mut()).fold(
Ok(false),
|acc: Result<bool, ShellError>, (col, val)| {
if searched_cols.contains(&col.as_str()) {
if let Value::String { val: s, span } = val {
if s.to_lowercase().contains(&search_string) {
*val = Value::String {
val: highlight_search_string(s, orig_search_string, string_style)?,
span: *span,
};
Ok(true)
} else {
// column does not contain the searched string
acc
}
} else {
// ignore non-string values
acc
}
} else {
// don't search this column
acc
}
},
)?;
if has_match {
matches.push(Value::Record {
cols,
vals,
span: record_span,
});
}
}
Ok(matches)
}
// Highlight the search string using ANSI escape sequences and regular expressions.
pub fn highlight_search_string(
haystack: &str,
needle: &str,
string_style: &Style,
) -> Result<String, ShellError> {
let regex_string = format!("(?i){needle}");
let regex = match Regex::new(&regex_string) {
Ok(regex) => regex,
Err(err) => {
return Err(ShellError::GenericError(
"Could not compile regex".into(),
err.to_string(),
Some(Span::test_data()),
None,
Vec::new(),
));
}
};
// strip haystack to remove existing ansi style
let stripped_haystack = nu_utils::strip_ansi_likely(haystack);
let mut last_match_end = 0;
let style = Style::new().fg(White).on(Red);
let mut highlighted = String::new();
for cap in regex.captures_iter(stripped_haystack.as_ref()) {
match cap {
Ok(capture) => {
let start = match capture.get(0) {
Some(acap) => acap.start(),
None => 0,
};
let end = match capture.get(0) {
Some(acap) => acap.end(),
None => 0,
};
highlighted.push_str(
&string_style
.paint(&stripped_haystack[last_match_end..start])
.to_string(),
);
highlighted.push_str(&style.paint(&stripped_haystack[start..end]).to_string());
last_match_end = end;
}
Err(e) => {
return Err(ShellError::GenericError(
"Error with regular expression capture".into(),
e.to_string(),
None,
None,
Vec::new(),
));
}
}
}
highlighted.push_str(
&string_style
.paint(&stripped_haystack[last_match_end..])
.to_string(),
);
Ok(highlighted)
}

View File

@ -1,181 +0,0 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Cow;
#[derive(Clone)]
pub struct HelpAliases;
impl Command for HelpAliases {
fn name(&self) -> &str {
"help aliases"
}
fn usage(&self) -> &str {
"Show help on nushell aliases."
}
fn signature(&self) -> Signature {
Signature::build("help aliases")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of alias to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in alias names and usage",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all aliases",
example: "help aliases",
result: None,
},
Example {
description: "show help for single alias",
example: "help aliases my-alias",
result: None,
},
Example {
description: "search for string in alias names and usages",
example: "help aliases --find my-alias",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_aliases(engine_state, stack, call)
}
}
pub fn help_aliases(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_aliases(engine_state, stack, head);
let found_cmds_vec =
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_aliases(engine_state, stack, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let alias_id = if let Some(id) = engine_state.find_alias(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::AliasNotFound(span(
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
)));
};
let alias_expansion = engine_state
.get_alias(alias_id)
.iter()
.map(|span| String::from_utf8_lossy(engine_state.get_span_contents(span)))
.collect::<Vec<Cow<str>>>()
.join(" ");
let alias_usage = engine_state.build_alias_usage(alias_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
const C: &str = "\x1b[36m"; // cyan
const RESET: &str = "\x1b[0m"; // reset
let mut long_desc = String::new();
if let Some((usage, extra_usage)) = alias_usage {
long_desc.push_str(&usage);
long_desc.push_str("\n\n");
if !extra_usage.is_empty() {
long_desc.push_str(&extra_usage);
long_desc.push_str("\n\n");
}
}
long_desc.push_str(&format!("{G}Alias{RESET}: {C}{name}{RESET}"));
long_desc.push_str("\n\n");
long_desc.push_str(&format!("{G}Expansion{RESET}:\n {alias_expansion}"));
let config = engine_state.get_config();
if !config.use_ansi_coloring {
long_desc = nu_utils::strip_ansi_string_likely(long_desc);
}
Ok(Value::String {
val: long_desc,
span: call.head,
}
.into_pipeline_data())
}
}
fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope_data = ScopeData::new(engine_state, stack);
scope_data.populate_aliases();
scope_data.collect_aliases(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpAliases;
use crate::test_examples;
test_examples(HelpAliases {})
}
}

View File

@ -1,185 +0,0 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{get_full_help, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Borrow;
#[derive(Clone)]
pub struct HelpCommands;
impl Command for HelpCommands {
fn name(&self) -> &str {
"help commands"
}
fn usage(&self) -> &str {
"Show help on nushell commands."
}
fn signature(&self) -> Signature {
Signature::build("help commands")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of command to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in command names, usage, and search terms",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_commands(engine_state, stack, call)
}
}
pub fn help_commands(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_commands(engine_state, head);
let found_cmds_vec = highlight_search_in_table(
all_cmds_vec,
&f.item,
&["name", "usage", "search_terms"],
&string_style,
)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_commands(engine_state, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let output = engine_state
.get_signatures_with_examples(false)
.iter()
.filter(|(signature, _, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _, is_parser_keyword)| {
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
})
.collect::<Vec<String>>();
if !output.is_empty() {
Ok(Value::String {
val: output.join("======================\n\n"),
span: call.head,
}
.into_pipeline_data())
} else {
Err(ShellError::CommandNotFound(span(&[
rest[0].span,
rest[rest.len() - 1].span,
])))
}
}
}
fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
let commands = engine_state.get_decls_sorted(false);
let mut found_cmds_vec = Vec::new();
for (name_bytes, decl_id) in commands {
let mut cols = vec![];
let mut vals = vec![];
let name = String::from_utf8_lossy(&name_bytes).to_string();
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(name, decl.borrow());
let signatures = sig.to_string().trim_start().replace("\n ", "\n");
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
cols.push("name".into());
vals.push(Value::String { val: key, span });
cols.push("category".into());
vals.push(Value::string(sig.category.to_string(), span));
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span,
});
cols.push("usage".into());
vals.push(Value::String { val: usage, span });
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span,
});
cols.push("search_terms".into());
vals.push(Value::String {
val: search_terms.join(", "),
span,
});
found_cmds_vec.push(Value::Record { cols, vals, span });
}
found_cmds_vec
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpCommands;
use crate::test_examples;
test_examples(HelpCommands {})
}
}

View File

@ -1,254 +0,0 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, AliasId, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData,
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct HelpModules;
impl Command for HelpModules {
fn name(&self) -> &str {
"help modules"
}
fn usage(&self) -> &str {
"Show help on nushell modules."
}
fn extra_usage(&self) -> &str {
r#"When requesting help for a single module, its commands and aliases will be highlighted if they
are also available in the current scope. Commands/aliases that were imported under a different name
(such as with a prefix after `use some-module`) will be highlighted in parentheses."#
}
fn signature(&self) -> Signature {
Signature::build("help modules")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of module to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in module names and usage",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all modules",
example: "help modules",
result: None,
},
Example {
description: "show help for single module",
example: "help modules my-module",
result: None,
},
Example {
description: "search for string in module names and usages",
example: "help modules --find my-module",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_modules(engine_state, stack, call)
}
}
pub fn help_modules(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_modules(engine_state, stack, head);
let found_cmds_vec =
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_modules(engine_state, stack, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let module_id = if let Some(id) = engine_state.find_module(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::ModuleNotFoundAtRuntime(
name,
span(&rest.iter().map(|r| r.span).collect::<Vec<Span>>()),
));
};
let module = engine_state.get_module(module_id);
let module_usage = engine_state.build_module_usage(module_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
const C: &str = "\x1b[36m"; // cyan
const CB: &str = "\x1b[1;36m"; // cyan bold
const RESET: &str = "\x1b[0m"; // reset
let mut long_desc = String::new();
if let Some((usage, extra_usage)) = module_usage {
long_desc.push_str(&usage);
long_desc.push_str("\n\n");
if !extra_usage.is_empty() {
long_desc.push_str(&extra_usage);
long_desc.push_str("\n\n");
}
}
long_desc.push_str(&format!("{G}Module{RESET}: {C}{name}{RESET}"));
long_desc.push_str("\n\n");
if !module.decls.is_empty() || module.main.is_some() {
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
let mut module_commands = module.decls();
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
let commands_str = module_commands
.iter()
.map(|(name_bytes, id)| {
let name = String::from_utf8_lossy(name_bytes);
if let Some((used_name_bytes, _)) =
commands.iter().find(|(_, decl_id)| id == decl_id)
{
if engine_state.find_decl(name.as_bytes(), &[]).is_some() {
format!("{CB}{name}{RESET}")
} else {
let command_name = String::from_utf8_lossy(used_name_bytes);
format!("{name} ({CB}{command_name}{RESET})")
}
} else {
format!("{name}")
}
})
.collect::<Vec<String>>()
.join(", ");
long_desc.push_str(&format!("{G}Exported commands{RESET}:\n {commands_str}"));
long_desc.push_str("\n\n");
}
if !module.aliases.is_empty() {
let aliases: Vec<(Vec<u8>, AliasId)> = engine_state.get_aliases_sorted(false).collect();
let mut module_aliases: Vec<(&[u8], AliasId)> = module
.aliases
.iter()
.map(|(name, id)| (name.as_ref(), *id))
.collect();
module_aliases.sort_by(|a, b| a.0.cmp(b.0));
let aliases_str = module_aliases
.iter()
.map(|(name_bytes, id)| {
let name = String::from_utf8_lossy(name_bytes);
if let Some((used_name_bytes, _)) =
aliases.iter().find(|(_, alias_id)| id == alias_id)
{
if engine_state.find_alias(name.as_bytes(), &[]).is_some() {
format!("{CB}{name}{RESET}")
} else {
let alias_name = String::from_utf8_lossy(used_name_bytes);
format!("{name} ({CB}{alias_name}{RESET})")
}
} else {
format!("{name}")
}
})
.collect::<Vec<String>>()
.join(", ");
long_desc.push_str(&format!("{G}Exported aliases{RESET}:\n {aliases_str}"));
long_desc.push_str("\n\n");
}
if module.env_block.is_some() {
long_desc.push_str(&format!("This module {C}exports{RESET} environment."));
} else {
long_desc.push_str(&format!(
"This module {C}does not export{RESET} environment."
));
}
let config = engine_state.get_config();
if !config.use_ansi_coloring {
long_desc = nu_utils::strip_ansi_string_likely(long_desc);
}
Ok(Value::String {
val: long_desc,
span: call.head,
}
.into_pipeline_data())
}
}
fn build_help_modules(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope_data = ScopeData::new(engine_state, stack);
scope_data.populate_modules();
scope_data.collect_modules(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpModules;
use crate::test_examples;
test_examples(HelpModules {})
}
}

View File

@ -1,329 +0,0 @@
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct HelpOperators;
impl Command for HelpOperators {
fn name(&self) -> &str {
"help operators"
}
fn usage(&self) -> &str {
"Show help on nushell operators."
}
fn signature(&self) -> Signature {
Signature::build("help operators")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let op_info = generate_operator_info();
let mut recs = vec![];
for op in op_info {
let mut cols = vec![];
let mut vals = vec![];
cols.push("type".into());
vals.push(Value::string(op.op_type, head));
cols.push("operator".into());
vals.push(Value::string(op.operator, head));
cols.push("name".into());
vals.push(Value::string(op.name, head));
cols.push("description".into());
vals.push(Value::string(op.description, head));
cols.push("precedence".into());
vals.push(Value::int(op.precedence, head));
recs.push(Value::Record {
cols,
vals,
span: head,
})
}
Ok(recs
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
}
}
struct OperatorInfo {
op_type: String,
operator: String,
name: String,
description: String,
precedence: i64,
}
fn generate_operator_info() -> Vec<OperatorInfo> {
vec![
OperatorInfo {
op_type: "Assignment".into(),
operator: "=".into(),
name: "Assign".into(),
description: "Assigns a value to a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "+=".into(),
name: "PlusAssign".into(),
description: "Adds a value to a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "++=".into(),
name: "AppendAssign".into(),
description: "Appends a list or a value to a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "-=".into(),
name: "MinusAssign".into(),
description: "Subtracts a value from a variable.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "*=".into(),
name: "MultiplyAssign".into(),
description: "Multiplies a variable by a value.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Assignment".into(),
operator: "/=".into(),
name: "DivideAssign".into(),
description: "Divides a variable by a value.".into(),
precedence: 10,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "==".into(),
name: "Equal".into(),
description: "Checks if two values are equal.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "!=".into(),
name: "NotEqual".into(),
description: "Checks if two values are not equal.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "<".into(),
name: "LessThan".into(),
description: "Checks if a value is less than another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "<=".into(),
name: "LessThanOrEqual".into(),
description: "Checks if a value is less than or equal to another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: ">".into(),
name: "GreaterThan".into(),
description: "Checks if a value is greater than another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: ">=".into(),
name: "GreaterThanOrEqual".into(),
description: "Checks if a value is greater than or equal to another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "=~".into(),
name: "RegexMatch".into(),
description: "Checks if a value matches a regular expression.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "!~".into(),
name: "NotRegexMatch".into(),
description: "Checks if a value does not match a regular expression.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "in".into(),
name: "In".into(),
description: "Checks if a value is in a list or string.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "not-in".into(),
name: "NotIn".into(),
description: "Checks if a value is not in a list or string.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "starts-with".into(),
name: "StartsWith".into(),
description: "Checks if a string starts with another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "ends-with".into(),
name: "EndsWith".into(),
description: "Checks if a string ends with another.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Comparison".into(),
operator: "not".into(),
name: "UnaryNot".into(),
description: "Negates a value or expression.".into(),
precedence: 0,
},
OperatorInfo {
op_type: "Math".into(),
operator: "+".into(),
name: "Plus".into(),
description: "Adds two values.".into(),
precedence: 90,
},
OperatorInfo {
op_type: "Math".into(),
operator: "++".into(),
name: "Append".into(),
description: "Appends two lists or a list and a value.".into(),
precedence: 80,
},
OperatorInfo {
op_type: "Math".into(),
operator: "-".into(),
name: "Minus".into(),
description: "Subtracts two values.".into(),
precedence: 90,
},
OperatorInfo {
op_type: "Math".into(),
operator: "*".into(),
name: "Multiply".into(),
description: "Multiplies two values.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "/".into(),
name: "Divide".into(),
description: "Divides two values.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "//".into(),
name: "FloorDivision".into(),
description: "Divides two values and floors the result.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "mod".into(),
name: "Modulo".into(),
description: "Divides two values and returns the remainder.".into(),
precedence: 95,
},
OperatorInfo {
op_type: "Math".into(),
operator: "**".into(),
name: "Pow ".into(),
description: "Raises one value to the power of another.".into(),
precedence: 100,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-or".into(),
name: "BitOr".into(),
description: "Performs a bitwise OR on two values.".into(),
precedence: 60,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-xor".into(),
name: "BitXor".into(),
description: "Performs a bitwise XOR on two values.".into(),
precedence: 70,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-and".into(),
name: "BitAnd".into(),
description: "Performs a bitwise AND on two values.".into(),
precedence: 75,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-shl".into(),
name: "ShiftLeft".into(),
description: "Shifts a value left by another.".into(),
precedence: 85,
},
OperatorInfo {
op_type: "Bitwise".into(),
operator: "bit-shr".into(),
name: "ShiftRight".into(),
description: "Shifts a value right by another.".into(),
precedence: 85,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "and".into(),
name: "And".into(),
description: "Checks if two values are true.".into(),
precedence: 50,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "or".into(),
name: "Or".into(),
description: "Checks if either value is true.".into(),
precedence: 40,
},
OperatorInfo {
op_type: "Boolean".into(),
operator: "xor".into(),
name: "Xor".into(),
description: "Checks if one value is true and the other is false.".into(),
precedence: 45,
},
]
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpOperators;
use crate::test_examples;
test_examples(HelpOperators {})
}
}

View File

@ -1,64 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Hide;
impl Command for Hide {
fn name(&self) -> &str {
"hide"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Hide definitions in the current scope"
}
fn extra_usage(&self) -> &str {
r#"Definitions are hidden by priority: First aliases, then custom commands.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Hide the alias just defined",
example: r#"alias lll = ls -l; hide lll"#,
result: None,
},
Example {
description: "Hide a custom command",
example: r#"def say-hi [] { echo 'Hi!' }; hide say-hi"#,
result: None,
},
]
}
}

View File

@ -1,76 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Type, Value,
};
#[derive(Clone)]
pub struct HideEnv;
impl Command for HideEnv {
fn name(&self) -> &str {
"hide-env"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest(
"name",
SyntaxShape::String,
"environment variable names to hide",
)
.switch(
"ignore-errors",
"do not throw an error if an environment variable was not found",
Some('i'),
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Hide environment variables in the current scope"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let env_var_names: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
let ignore_errors = call.has_flag("ignore-errors");
for name in env_var_names {
if !stack.remove_env_var(engine_state, &name.item) && !ignore_errors {
let all_names: Vec<String> = stack
.get_env_var_names(engine_state)
.iter()
.cloned()
.collect();
if let Some(closest_match) = did_you_mean(&all_names, &name.item) {
return Err(ShellError::DidYouMeanCustom(
format!("Environment variable '{}' not found", name.item),
closest_match,
name.span,
));
} else {
return Err(ShellError::EnvVarNotFoundAtRuntime(name.item, name.span));
}
}
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Hide an environment variable",
example: r#"let-env HZ_ENV_ABC = 1; hide-env HZ_ENV_ABC; 'HZ_ENV_ABC' in (env).name"#,
result: Some(Value::test_bool(false)),
}]
}
}

View File

@ -1,154 +0,0 @@
use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct If;
impl Command for If {
fn name(&self) -> &str {
"if"
}
fn usage(&self) -> &str {
"Conditionally run a block."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("if")
.input_output_types(vec![(Type::Any, Type::Any)])
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"then_block",
SyntaxShape::Block,
"block to run if check succeeds",
)
.optional(
"else_expression",
SyntaxShape::Keyword(
b"else".to_vec(),
Box::new(SyntaxShape::OneOf(vec![
SyntaxShape::Block,
SyntaxShape::Expression,
])),
),
"expression or block to run if check fails",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cond = call.positional_nth(0).expect("checked through parser");
let then_block: Block = call.req(engine_state, stack, 1)?;
let else_case = call.positional_nth(2);
let result = eval_expression(engine_state, stack, cond)?;
match &result {
Value::Bool { val, .. } => {
if *val {
let block = engine_state.get_block(then_block.block_id);
eval_block(
engine_state,
stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
} else if let Some(else_case) = else_case {
if let Some(else_expr) = else_case.as_keyword() {
if let Some(block_id) = else_expr.as_block() {
let block = engine_state.get_block(block_id);
eval_block(
engine_state,
stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)
} else {
eval_expression_with_input(
engine_state,
stack,
else_expr,
input,
call.redirect_stdout,
call.redirect_stderr,
)
.map(|res| res.0)
}
} else {
eval_expression_with_input(
engine_state,
stack,
else_case,
input,
call.redirect_stdout,
call.redirect_stderr,
)
.map(|res| res.0)
}
} else {
Ok(PipelineData::empty())
}
}
x => Err(ShellError::CantConvert(
"bool".into(),
x.get_type().to_string(),
result.span()?,
None,
)),
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Output a value if a condition matches, otherwise return nothing",
example: "if 2 < 3 { 'yes!' }",
result: Some(Value::test_string("yes!")),
},
Example {
description: "Output a value if a condition matches, else return another value",
example: "if 5 < 3 { 'yes!' } else { 'no!' }",
result: Some(Value::test_string("no!")),
},
Example {
description: "Chain multiple if's together",
example: "if 5 < 3 { 'yes!' } else if 4 < 5 { 'no!' } else { 'okay!' }",
result: Some(Value::test_string("no!")),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(If {})
}
}

View File

@ -1,55 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
#[derive(Clone)]
pub struct Ignore;
impl Command for Ignore {
fn name(&self) -> &str {
"ignore"
}
fn usage(&self) -> &str {
"Ignore the output of the previous command in the pipeline"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("ignore")
.input_output_types(vec![(Type::Any, Type::Nothing)])
.category(Category::Core)
}
fn search_terms(&self) -> Vec<&str> {
vec!["silent", "quiet", "out-null"]
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
input.into_value(call.head);
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Ignore the output of an echo command",
example: "echo done | ignore",
result: Some(Value::nothing(Span::test_data())),
}]
}
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Ignore;
use crate::test_examples;
test_examples(Ignore {})
}
}

View File

@ -1,119 +0,0 @@
use nu_engine::eval_expression_with_input;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Let;
impl Command for Let {
fn name(&self) -> &str {
"let"
}
fn usage(&self) -> &str {
"Create a variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("let")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["set", "const"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call
.positional_nth(1)
.expect("checked through parser")
.as_keyword()
.expect("internal error: missing keyword");
let (rhs, external_failed) = eval_expression_with_input(
engine_state,
stack,
keyword_expr,
input,
call.redirect_stdout,
call.redirect_stderr,
)?;
if external_failed {
// rhs must be a PipelineData::ExternalStream and it's failed
// return the failed stream (with a non-zero exit code) so the engine knows to stop running
Ok(rhs)
} else {
stack.add_var(var_id, rhs.into_value(call.head));
Ok(PipelineData::empty())
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Set a variable to a value",
example: "let x = 10",
result: None,
},
Example {
description: "Set a variable to the result of an expression",
example: "let x = 10 + 100",
result: None,
},
Example {
description: "Set a variable based on the condition",
example: "let x = if false { -1 } else { 1 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Let {})
}
#[test]
fn test_command_type() {
assert!(matches!(Let.command_type(), CommandType::Keyword));
}
}

View File

@ -1,99 +0,0 @@
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Loop;
impl Command for Loop {
fn name(&self) -> &str {
"loop"
}
fn usage(&self) -> &str {
"Run a block in a loop."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("loop")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("block", SyntaxShape::Block, "block to loop")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let block: Block = call.req(engine_state, stack, 0)?;
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
let block = engine_state.get_block(block.block_id);
match eval_block(
engine_state,
stack,
block,
PipelineData::empty(),
call.redirect_stdout,
call.redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.print(engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
}
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Loop while a condition is true",
example: "mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x",
result: Some(Value::test_int(11)),
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Loop {})
}
}

View File

@ -1,82 +0,0 @@
mod alias;
mod break_;
mod commandline;
mod const_;
mod continue_;
mod def;
mod def_env;
mod describe;
mod do_;
mod echo;
mod error_make;
mod export;
mod export_alias;
mod export_def;
mod export_def_env;
mod export_extern;
mod export_use;
mod extern_;
mod for_;
pub mod help;
pub mod help_aliases;
pub mod help_commands;
pub mod help_modules;
mod help_operators;
mod hide;
mod hide_env;
mod if_;
mod ignore;
mod let_;
mod loop_;
mod module;
mod mut_;
pub(crate) mod overlay;
mod return_;
mod try_;
mod use_;
mod version;
mod while_;
pub use alias::Alias;
pub use break_::Break;
pub use commandline::Commandline;
pub use const_::Const;
pub use continue_::Continue;
pub use def::Def;
pub use def_env::DefEnv;
pub use describe::Describe;
pub use do_::Do;
pub use echo::Echo;
pub use error_make::ErrorMake;
pub use export::ExportCommand;
pub use export_alias::ExportAlias;
pub use export_def::ExportDef;
pub use export_def_env::ExportDefEnv;
pub use export_extern::ExportExtern;
pub use export_use::ExportUse;
pub use extern_::Extern;
pub use for_::For;
pub use help::Help;
pub use help_aliases::HelpAliases;
pub use help_commands::HelpCommands;
pub use help_modules::HelpModules;
pub use help_operators::HelpOperators;
pub use hide::Hide;
pub use hide_env::HideEnv;
pub use if_::If;
pub use ignore::Ignore;
pub use let_::Let;
pub use loop_::Loop;
pub use module::Module;
pub use mut_::Mut;
pub use overlay::*;
pub use return_::Return;
pub use try_::Try;
pub use use_::Use;
pub use version::Version;
pub use while_::While;
#[cfg(feature = "plugin")]
mod register;
#[cfg(feature = "plugin")]
pub use register::Register;

View File

@ -1,65 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Module;
impl Command for Module {
fn name(&self) -> &str {
"module"
}
fn usage(&self) -> &str {
"Define a custom module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("module")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module_name", SyntaxShape::String, "module name")
.required("block", SyntaxShape::Block, "body of the module")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::test_string("foo")),
},
Example {
description: "Define an environment variable in a module",
example: r#"module foo { export-env { let-env FOO = "BAZ" } }; use foo; $env.FOO"#,
result: Some(Value::test_string("BAZ")),
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
]
}
}

View File

@ -1,122 +0,0 @@
use nu_engine::eval_expression_with_input;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Mut;
impl Command for Mut {
fn name(&self) -> &str {
"mut"
}
fn usage(&self) -> &str {
"Create a mutable variable and give it a value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("mut")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn search_terms(&self) -> Vec<&str> {
vec!["set", "mutable"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
let keyword_expr = call
.positional_nth(1)
.expect("checked through parser")
.as_keyword()
.expect("internal error: missing keyword");
let rhs = eval_expression_with_input(
engine_state,
stack,
keyword_expr,
input,
call.redirect_stdout,
call.redirect_stderr,
)?
.0;
//println!("Adding: {:?} to {}", rhs, var_id);
stack.add_var(var_id, rhs.into_value(call.head));
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Set a mutable variable to a value, then update it",
example: "mut x = 10; $x = 12",
result: None,
},
Example {
description: "Upsert a value inside a mutable data structure",
example: "mut a = {b:{c:1}}; $a.b.c = 2",
result: None,
},
Example {
description: "Set a mutable variable to the result of an expression",
example: "mut x = 10 + 100",
result: None,
},
Example {
description: "Set a mutable variable based on the condition",
example: "mut x = if false { -1 } else { 1 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Mut {})
}
#[test]
fn test_command_type() {
assert!(matches!(Mut.command_type(), CommandType::Keyword));
}
}

View File

@ -1,56 +0,0 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct Overlay;
impl Command for Overlay {
fn name(&self) -> &str {
"overlay"
}
fn signature(&self) -> Signature {
Signature::build("overlay")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
"Commands for manipulating overlays."
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html
You must use one of the following subcommands. Using this command as-is will only produce this help message."#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
&Overlay.signature(),
&[],
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
.into_pipeline_data())
}
}

View File

@ -1,132 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
};
#[derive(Clone)]
pub struct OverlayHide;
impl Command for OverlayHide {
fn name(&self) -> &str {
"overlay hide"
}
fn usage(&self) -> &str {
"Hide an active overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay hide")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.optional("name", SyntaxShape::String, "Overlay to hide")
.switch(
"keep-custom",
"Keep all newly added commands and aliases in the next activated overlay",
Some('k'),
)
.named(
"keep-env",
SyntaxShape::List(Box::new(SyntaxShape::String)),
"List of environment variables to keep in the next activated overlay",
Some('e'),
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let overlay_name: Spanned<String> = if let Some(name) = call.opt(engine_state, stack, 0)? {
name
} else {
Spanned {
item: stack.last_overlay_name()?,
span: call.head,
}
};
if !stack.is_overlay_active(&overlay_name.item) {
return Err(ShellError::OverlayNotFoundAtRuntime(
overlay_name.item,
overlay_name.span,
));
}
let keep_env: Option<Vec<Spanned<String>>> =
call.get_flag(engine_state, stack, "keep-env")?;
let env_vars_to_keep = if let Some(env_var_names_to_keep) = keep_env {
let mut env_vars_to_keep = vec![];
for name in env_var_names_to_keep.into_iter() {
match stack.get_env_var(engine_state, &name.item) {
Some(val) => env_vars_to_keep.push((name.item, val.clone())),
None => return Err(ShellError::EnvVarNotFoundAtRuntime(name.item, name.span)),
}
}
env_vars_to_keep
} else {
vec![]
};
stack.remove_overlay(&overlay_name.item);
for (name, val) in env_vars_to_keep {
stack.add_env_var(name, val);
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Keep a custom command after hiding the overlay",
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam
def bar [] { "bar" }
overlay hide spam --keep-custom
bar
"#,
result: None,
},
Example {
description: "Hide an overlay created from a file",
example: r#"'export alias f = "foo"' | save spam.nu
overlay use spam.nu
overlay hide spam"#,
result: None,
},
Example {
description: "Hide the last activated overlay",
example: r#"module spam { export-env { let-env FOO = "foo" } }
overlay use spam
overlay hide"#,
result: None,
},
Example {
description: "Keep the current working directory when removing an overlay",
example: r#"overlay new spam
cd some-dir
overlay hide --keep-env [ PWD ] spam"#,
result: None,
},
]
}
}

View File

@ -1,58 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct OverlayList;
impl Command for OverlayList {
fn name(&self) -> &str {
"overlay list"
}
fn usage(&self) -> &str {
"List all active overlays"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay list")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
}
fn extra_usage(&self) -> &str {
"The overlays are listed in the order they were activated."
}
fn run(
&self,
_engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let active_overlays_engine: Vec<Value> = stack
.active_overlays
.iter()
.map(|s| Value::string(s, call.head))
.collect();
Ok(Value::List {
vals: active_overlays_engine,
span: call.head,
}
.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get the last activated overlay",
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam
overlay list | last"#,
result: Some(Value::test_string("spam")),
}]
}
}

View File

@ -1,11 +0,0 @@
mod command;
mod hide;
mod list;
mod new;
mod use_;
pub use command::Overlay;
pub use hide::OverlayHide;
pub use list::OverlayList;
pub use new::OverlayNew;
pub use use_::OverlayUse;

View File

@ -1,78 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
};
#[derive(Clone)]
pub struct OverlayNew;
impl Command for OverlayNew {
fn name(&self) -> &str {
"overlay new"
}
fn usage(&self) -> &str {
"Create an empty overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay new")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("name", SyntaxShape::String, "Name of the overlay")
// TODO:
// .switch(
// "prefix",
// "Prepend module name to the imported symbols",
// Some('p'),
// )
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"The command will first create an empty module, then add it as an overlay.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
stack.add_overlay(name_arg.item);
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create an empty overlay",
example: r#"overlay new spam"#,
result: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(OverlayNew {})
}
}

View File

@ -1,199 +0,0 @@
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
use nu_parser::trim_quotes_str;
use nu_protocol::ast::{Call, Expr};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
};
use std::path::Path;
#[derive(Clone)]
pub struct OverlayUse;
impl Command for OverlayUse {
fn name(&self) -> &str {
"overlay use"
}
fn usage(&self) -> &str {
"Use definitions from a module as an overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required(
"name",
SyntaxShape::String,
"Module name to use overlay for",
)
.optional(
"as",
SyntaxShape::Keyword(b"as".to_vec(), Box::new(SyntaxShape::String)),
"as keyword followed by a new name",
)
.switch(
"prefix",
"Prepend module name to the imported commands and aliases",
Some('p'),
)
.switch(
"reload",
"If the overlay already exists, reload its definitions and environment.",
Some('r'),
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
caller_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id = if let Some(overlay_expr) = call.parser_info_nth(0) {
if let Expr::Overlay(module_id) = overlay_expr.expr {
module_id
} else {
return Err(ShellError::NushellFailedSpanned(
"Not an overlay".to_string(),
"requires an overlay (path or a string)".to_string(),
overlay_expr.span,
));
}
} else {
return Err(ShellError::NushellFailedSpanned(
"Missing positional".to_string(),
"missing required overlay".to_string(),
call.head,
));
};
let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
name
} else if engine_state
.find_overlay(name_arg.item.as_bytes())
.is_some()
{
name_arg.item.clone()
} else if let Some(os_str) = Path::new(&name_arg.item).file_stem() {
if let Some(name) = os_str.to_str() {
name.to_string()
} else {
return Err(ShellError::NonUtf8(name_arg.span));
}
} else {
return Err(ShellError::OverlayNotFoundAtRuntime(
name_arg.item,
name_arg.span,
));
};
if let Some(module_id) = maybe_origin_module_id {
// Add environment variables only if:
// a) adding a new overlay
// b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(module_id);
// Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block {
let maybe_path = find_in_dirs_env(&name_arg.item, engine_state, caller_stack)?;
let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack.gather_captures(&block.captures);
if let Some(path) = &maybe_path {
// Set the currently evaluated directory, if the argument is a valid path
let mut parent = path.clone();
parent.pop();
let file_pwd = Value::string(parent.to_string_lossy(), call.head);
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
}
let _ = eval_block(
engine_state,
&mut callee_stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
// The export-env block should see the env vars *before* activating this overlay
caller_stack.add_overlay(overlay_name);
// Merge the block's environment to the current stack
redirect_env(engine_state, caller_stack, &callee_stack);
} else {
caller_stack.add_overlay(overlay_name);
}
} else {
caller_stack.add_overlay(overlay_name);
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create an overlay from a module",
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam
foo"#,
result: None,
},
Example {
description: "Create an overlay from a module and rename it",
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam as spam_new
foo"#,
result: None,
},
Example {
description: "Create an overlay with a prefix",
example: r#"'export def foo { "foo" }'
overlay use --prefix spam
spam foo"#,
result: None,
},
Example {
description: "Create an overlay from a file",
example: r#"'export-env { let-env FOO = "foo" }' | save spam.nu
overlay use spam.nu
$env.FOO"#,
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(OverlayUse {})
}
}

View File

@ -1,72 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Register;
impl Command for Register {
fn name(&self) -> &str {
"register"
}
fn usage(&self) -> &str {
"Register a plugin"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("register")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"plugin",
SyntaxShape::Filepath,
"path of executable for plugin",
)
.optional(
"signature",
SyntaxShape::Any,
"Block with signature description as json object",
)
.named(
"shell",
SyntaxShape::Filepath,
"path of shell used to run plugin (cmd, sh, python, etc)",
Some('s'),
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Register `nu_plugin_query` plugin from ~/.cargo/bin/ dir",
example: r#"register ~/.cargo/bin/nu_plugin_query"#,
result: None,
},
Example {
description: "Register `nu_plugin_query` plugin from `nu -c`(plugin will be available in that nu session only)",
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
result: None,
},
]
}
}

View File

@ -1,61 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Return;
impl Command for Return {
fn name(&self) -> &str {
"return"
}
fn usage(&self) -> &str {
"Return early from a function"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("return")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.optional("return_value", SyntaxShape::Any, "optional value to return")
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let return_value: Option<Value> = call.opt(engine_state, stack, 0)?;
if let Some(value) = return_value {
Err(ShellError::Return(call.head, Box::new(value)))
} else {
Err(ShellError::Return(
call.head,
Box::new(Value::nothing(call.head)),
))
}
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Return early",
example: r#"def foo [] { return }"#,
result: None,
}]
}
}

View File

@ -1,153 +0,0 @@
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
pub struct Try;
impl Command for Try {
fn name(&self) -> &str {
"try"
}
fn usage(&self) -> &str {
"Try to run a block, if it fails optionally run a catch block"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("try")
.input_output_types(vec![(Type::Any, Type::Any)])
.required("try_block", SyntaxShape::Block, "block to run")
.optional(
"catch_block",
SyntaxShape::Keyword(
b"catch".to_vec(),
Box::new(SyntaxShape::Closure(Some(vec![SyntaxShape::Any]))),
),
"block to run if try block fails",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let try_block: Block = call.req(engine_state, stack, 0)?;
let catch_block: Option<Closure> = call.opt(engine_state, stack, 1)?;
let try_block = engine_state.get_block(try_block.block_id);
let result = eval_block(engine_state, stack, try_block, input, false, false);
match result {
Err(error) | Ok(PipelineData::Value(Value::Error { error }, ..)) => {
if let nu_protocol::ShellError::Break(_) = error {
return Err(error);
} else if let nu_protocol::ShellError::Continue(_) = error {
return Err(error);
} else if let nu_protocol::ShellError::Return(_, _) = error {
return Err(error);
}
if let Some(catch_block) = catch_block {
let catch_block = engine_state.get_block(catch_block.block_id);
let err_value = Value::Error { error };
// Put the error value in the positional closure var
if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, err_value.clone());
}
}
eval_block(
engine_state,
stack,
catch_block,
// Make the error accessible with $in, too
err_value.into_pipeline_data(),
false,
false,
)
} else {
Ok(PipelineData::empty())
}
}
// external command may fail to run
Ok(pipeline) => {
let (pipeline, external_failed) = pipeline.is_external_failed();
if external_failed {
if let Some(catch_block) = catch_block {
let catch_block = engine_state.get_block(catch_block.block_id);
if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
// Because external command errors aren't "real" errors,
// (unless do -c is in effect)
// they can't be passed in as Nushell values.
let err_value = Value::nothing(call.head);
stack.add_var(*var_id, err_value);
}
}
eval_block(
engine_state,
stack,
catch_block,
// The same null as in the above block is set as the $in value.
Value::nothing(call.head).into_pipeline_data(),
false,
false,
)
} else {
Ok(PipelineData::empty())
}
} else {
Ok(pipeline)
}
}
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Try to run a missing command",
example: "try { asdfasdf }",
result: None,
},
Example {
description: "Try to run a missing command",
example: "try { asdfasdf } catch { echo 'missing' } ",
result: Some(Value::test_string("missing")),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Try {})
}
}

View File

@ -1,134 +0,0 @@
use nu_engine::{eval_block, find_in_dirs_env, redirect_env};
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Use;
impl Command for Use {
fn name(&self) -> &str {
"use"
}
fn usage(&self) -> &str {
"Use definitions from a module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
caller_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let import_pattern = if let Some(Expression {
expr: Expr::ImportPattern(pat),
..
}) = call.parser_info_nth(0)
{
pat
} else {
return Err(ShellError::GenericError(
"Unexpected import".into(),
"import pattern not supported".into(),
Some(call.head),
None,
Vec::new(),
));
};
if let Some(module_id) = import_pattern.head.id {
let module = engine_state.get_module(module_id);
// Evaluate the export-env block if there is one
if let Some(block_id) = module.env_block {
let block = engine_state.get_block(block_id);
// See if the module is a file
let module_arg_str = String::from_utf8_lossy(
engine_state.get_span_contents(&import_pattern.head.span),
);
let maybe_parent = if let Some(path) =
find_in_dirs_env(&module_arg_str, engine_state, caller_stack)?
{
path.parent().map(|p| p.to_path_buf()).or(None)
} else {
None
};
let mut callee_stack = caller_stack.gather_captures(&block.captures);
// If so, set the currently evaluated directory (file-relative PWD)
if let Some(parent) = maybe_parent {
let file_pwd = Value::string(parent.to_string_lossy(), call.head);
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
}
// Run the block (discard the result)
let _ = eval_block(
engine_state,
&mut callee_stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
)?;
// Merge the block's environment to the current stack
redirect_env(engine_state, caller_stack, &callee_stack);
}
} else {
return Err(ShellError::GenericError(
format!(
"Could not import from '{}'",
String::from_utf8_lossy(&import_pattern.head.name)
),
"module does not exist".to_string(),
Some(import_pattern.head.span),
None,
Vec::new(),
));
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::test_string("foo")),
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
]
}
}

View File

@ -1,195 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value};
pub mod shadow {
include!(concat!(env!("OUT_DIR"), "/shadow.rs"));
}
#[derive(Clone)]
pub struct Version;
impl Command for Version {
fn name(&self) -> &str {
"version"
}
fn signature(&self) -> Signature {
Signature::build("version")
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
.allow_variants_without_examples(true)
}
fn usage(&self) -> &str {
"Display Nu version."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
version(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Display Nu version",
example: "version",
result: None,
}]
}
}
pub fn version(
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let tag = call.head;
let mut cols = vec![];
let mut vals = vec![];
cols.push("version".to_string());
vals.push(Value::string(env!("CARGO_PKG_VERSION"), tag));
cols.push("branch".to_string());
vals.push(Value::string(shadow::BRANCH, call.head));
let commit_hash: Option<&str> = option_env!("NU_COMMIT_HASH");
if let Some(commit_hash) = commit_hash {
cols.push("commit_hash".to_string());
vals.push(Value::string(commit_hash, call.head));
}
let build_os: Option<&str> = Some(shadow::BUILD_OS).filter(|x| !x.is_empty());
if let Some(build_os) = build_os {
cols.push("build_os".to_string());
vals.push(Value::string(build_os, call.head));
}
let build_target: Option<&str> = Some(shadow::BUILD_TARGET).filter(|x| !x.is_empty());
if let Some(build_target) = build_target {
cols.push("build_target".to_string());
vals.push(Value::string(build_target, call.head));
}
let rust_version: Option<&str> = Some(shadow::RUST_VERSION).filter(|x| !x.is_empty());
if let Some(rust_version) = rust_version {
cols.push("rust_version".to_string());
vals.push(Value::string(rust_version, call.head));
}
let rust_channel: Option<&str> = Some(shadow::RUST_CHANNEL).filter(|x| !x.is_empty());
if let Some(rust_channel) = rust_channel {
cols.push("rust_channel".to_string());
vals.push(Value::string(rust_channel, call.head));
}
let cargo_version: Option<&str> = Some(shadow::CARGO_VERSION).filter(|x| !x.is_empty());
if let Some(cargo_version) = cargo_version {
cols.push("cargo_version".to_string());
vals.push(Value::string(cargo_version, call.head));
}
let pkg_version: Option<&str> = Some(shadow::PKG_VERSION).filter(|x| !x.is_empty());
if let Some(pkg_version) = pkg_version {
cols.push("pkg_version".to_string());
vals.push(Value::string(pkg_version, call.head));
}
let build_time: Option<&str> = Some(shadow::BUILD_TIME).filter(|x| !x.is_empty());
if let Some(build_time) = build_time {
cols.push("build_time".to_string());
vals.push(Value::string(build_time, call.head));
}
let build_rust_channel: Option<&str> =
Some(shadow::BUILD_RUST_CHANNEL).filter(|x| !x.is_empty());
if let Some(build_rust_channel) = build_rust_channel {
cols.push("build_rust_channel".to_string());
vals.push(Value::string(build_rust_channel, call.head));
}
cols.push("features".to_string());
vals.push(Value::String {
val: features_enabled().join(", "),
span: call.head,
});
// Get a list of command names and check for plugins
let installed_plugins = engine_state
.plugin_decls()
.filter(|x| x.is_plugin().is_some())
.map(|x| x.name())
.collect::<Vec<_>>();
cols.push("installed_plugins".to_string());
vals.push(Value::String {
val: installed_plugins.join(", "),
span: call.head,
});
Ok(Value::Record {
cols,
vals,
span: call.head,
}
.into_pipeline_data())
}
fn features_enabled() -> Vec<String> {
let mut names = vec!["default".to_string()];
// NOTE: There should be another way to know features on.
#[cfg(feature = "which-support")]
{
names.push("which".to_string());
}
// always include it?
names.push("zip".to_string());
#[cfg(feature = "trash-support")]
{
names.push("trash".to_string());
}
#[cfg(feature = "sqlite")]
{
names.push("database".to_string());
}
#[cfg(feature = "dataframe")]
{
names.push("dataframe".to_string());
}
#[cfg(feature = "static-link-openssl")]
{
names.push("static-link-openssl".to_string());
}
#[cfg(feature = "extra")]
{
names.push("extra".to_string());
}
names.sort();
names
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Version;
use crate::test_examples;
test_examples(Version {})
}
}

View File

@ -1,123 +0,0 @@
use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct While;
impl Command for While {
fn name(&self) -> &str {
"while"
}
fn usage(&self) -> &str {
"Conditionally run a block in a loop."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("while")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"block",
SyntaxShape::Block,
"block to loop if check succeeds",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cond = call.positional_nth(0).expect("checked through parser");
let block: Block = call.req(engine_state, stack, 1)?;
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
let result = eval_expression(engine_state, stack, cond)?;
match &result {
Value::Bool { val, .. } => {
if *val {
let block = engine_state.get_block(block.block_id);
match eval_block(
engine_state,
stack,
block,
PipelineData::empty(),
call.redirect_stdout,
call.redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code =
pipeline.print(engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
}
} else {
break;
}
}
x => {
return Err(ShellError::CantConvert(
"bool".into(),
x.get_type().to_string(),
result.span()?,
None,
))
}
}
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Loop while a condition is true",
example: "mut x = 0; while $x < 10 { $x = $x + 1 }",
result: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(While {})
}
}

View File

@ -8,7 +8,7 @@ use nu_protocol::{
use super::eager::ToDataFrame;
use super::expressions::ExprCol;
use super::lazy::{LazyCollect, ToLazyFrame};
use crate::Let;
use nu_cmd_lang::Let;
pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
if cmds.is_empty() {

View File

@ -3,7 +3,7 @@ use nu_protocol::engine::{EngineState, StateWorkingSet};
use crate::*;
pub fn create_default_context() -> EngineState {
let mut engine_state = EngineState::new();
let mut engine_state = nu_cmd_lang::create_default_context();
let delta = {
let mut working_set = StateWorkingSet::new(&engine_state);
@ -26,52 +26,6 @@ pub fn create_default_context() -> EngineState {
#[cfg(feature = "sqlite")]
add_database_decls(&mut working_set);
// Core
bind_command! {
Alias,
Break,
Commandline,
Const,
Continue,
Def,
DefEnv,
Describe,
Do,
Echo,
ErrorMake,
ExportAlias,
ExportCommand,
ExportDef,
ExportDefEnv,
ExportExtern,
ExportUse,
Extern,
For,
Help,
HelpAliases,
HelpCommands,
HelpModules,
HelpOperators,
Hide,
HideEnv,
If,
Ignore,
Overlay,
OverlayUse,
OverlayList,
OverlayNew,
OverlayHide,
Let,
Loop,
Module,
Mut,
Return,
Try,
Use,
Version,
While,
};
// Charts
bind_command! {
Histogram
@ -498,9 +452,6 @@ pub fn create_default_context() -> EngineState {
MathEvalDeprecated,
};
#[cfg(feature = "plugin")]
bind_command!(Register);
working_set.render()
};

View File

@ -9,12 +9,13 @@ pub fn test_examples(cmd: impl Command + 'static) {
#[cfg(test)]
mod test_examples {
use super::super::{
Ansi, Date, Echo, Enumerate, Flatten, From, Get, If, Into, IntoString, Let, LetEnv, Math,
MathEuler, MathPi, MathRound, ParEach, Path, Random, Sort, SortBy, Split, SplitColumn,
SplitRow, Str, StrJoin, StrLength, StrReplace, Update, Url, Values, Wrap,
Ansi, Date, Enumerate, Flatten, From, Get, Into, IntoString, LetEnv, Math, MathEuler,
MathPi, MathRound, ParEach, Path, Random, Sort, SortBy, Split, SplitColumn, SplitRow, Str,
StrJoin, StrLength, StrReplace, Update, Url, Values, Wrap,
};
use crate::{Break, Each, Mut, To};
use crate::{Each, To};
use itertools::Itertools;
use nu_cmd_lang::{Break, Echo, If, Let, Mut};
use nu_protocol::{
ast::Block,
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},

View File

@ -1,4 +1,4 @@
use crate::help::highlight_search_string;
use nu_cmd_lang::help::highlight_search_string;
use fancy_regex::Regex;
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};

View File

@ -2,7 +2,6 @@ mod bits;
mod bytes;
mod charting;
mod conversions;
mod core_commands;
mod date;
mod debug;
mod default_context;
@ -33,7 +32,6 @@ pub use bits::*;
pub use bytes::*;
pub use charting::*;
pub use conversions::*;
pub use core_commands::*;
pub use date::*;
pub use debug::*;
pub use default_context::*;