Add -c flag and others to cmdline args (#853)

* Add -c flag and others to cmdline args

* finish a little bit of cleanup

* Oops, forgot file
This commit is contained in:
JT 2022-01-26 12:26:43 -05:00 committed by GitHub
parent 8ee619954d
commit 83ec374995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 219 additions and 39 deletions

View File

@ -206,7 +206,10 @@ pub fn get_documentation(
} }
if let Some(rest_positional) = &sig.rest_positional { if let Some(rest_positional) = &sig.rest_positional {
long_desc.push_str(&format!(" ...args: {}\n", rest_positional.desc)); long_desc.push_str(&format!(
" ...{}: {}\n",
rest_positional.name, rest_positional.desc
));
} }
} }
@ -267,7 +270,7 @@ fn get_flags_section(signature: &Signature) -> String {
if let Some(short) = flag.short { if let Some(short) = flag.short {
if flag.required { if flag.required {
format!( format!(
" -{}{} (required parameter) {:?} {}\n", " -{}{} (required parameter) {:?}\n {}\n",
short, short,
if !flag.long.is_empty() { if !flag.long.is_empty() {
format!(", --{}", flag.long) format!(", --{}", flag.long)
@ -279,7 +282,7 @@ fn get_flags_section(signature: &Signature) -> String {
) )
} else { } else {
format!( format!(
" -{}{} {:?} {}\n", " -{}{} <{:?}>\n {}\n",
short, short,
if !flag.long.is_empty() { if !flag.long.is_empty() {
format!(", --{}", flag.long) format!(", --{}", flag.long)
@ -292,16 +295,16 @@ fn get_flags_section(signature: &Signature) -> String {
} }
} else if flag.required { } else if flag.required {
format!( format!(
" --{} (required parameter) {:?} {}\n", " --{} (required parameter) <{:?}>\n {}\n",
flag.long, arg, flag.desc flag.long, arg, flag.desc
) )
} else { } else {
format!(" --{} {:?} {}\n", flag.long, arg, flag.desc) format!(" --{} {:?}\n {}\n", flag.long, arg, flag.desc)
} }
} else if let Some(short) = flag.short { } else if let Some(short) = flag.short {
if flag.required { if flag.required {
format!( format!(
" -{}{} (required parameter) {}\n", " -{}{} (required parameter)\n {}\n",
short, short,
if !flag.long.is_empty() { if !flag.long.is_empty() {
format!(", --{}", flag.long) format!(", --{}", flag.long)
@ -312,7 +315,7 @@ fn get_flags_section(signature: &Signature) -> String {
) )
} else { } else {
format!( format!(
" -{}{} {}\n", " -{}{}\n {}\n",
short, short,
if !flag.long.is_empty() { if !flag.long.is_empty() {
format!(", --{}", flag.long) format!(", --{}", flag.long)
@ -323,9 +326,12 @@ fn get_flags_section(signature: &Signature) -> String {
) )
} }
} else if flag.required { } else if flag.required {
format!(" --{} (required parameter) {}\n", flag.long, flag.desc) format!(
" --{} (required parameter)\n {}\n",
flag.long, flag.desc
)
} else { } else {
format!(" --{} {}\n", flag.long, flag.desc) format!(" --{}\n {}\n", flag.long, flag.desc)
}; };
long_desc.push_str(&msg); long_desc.push_str(&msg);
} }

View File

@ -13,7 +13,7 @@ pub use flatten::{
pub use lex::{lex, Token, TokenContents}; pub use lex::{lex, Token, TokenContents};
pub use lite_parse::{lite_parse, LiteBlock}; pub use lite_parse::{lite_parse, LiteBlock};
pub use parser::{find_captures_in_expr, parse, trim_quotes, Import}; pub use parser::{find_captures_in_expr, parse, parse_block, trim_quotes, Import};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
pub use parse_keywords::parse_register; pub use parse_keywords::parse_register;

136
src/commands.rs Normal file
View File

@ -0,0 +1,136 @@
use miette::Result;
use nu_engine::{convert_env_values, eval_block};
use std::path::Path;
use nu_parser::{lex, lite_parse, parse_block, trim_quotes};
use nu_protocol::{
engine::{EngineState, StateDelta, StateWorkingSet},
Config, PipelineData, Span, Spanned, Value, CONFIG_VARIABLE_ID,
};
use crate::utils::{gather_parent_env_vars, report_error};
pub(crate) fn evaluate(
commands: &Spanned<String>,
init_cwd: &Path,
engine_state: &mut EngineState,
input: PipelineData,
) -> Result<()> {
// First, set up env vars as strings only
gather_parent_env_vars(engine_state);
// Run a command (or commands) given to us by the user
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let (input, span_offset) =
if commands.item.starts_with('\'') || commands.item.starts_with('"') {
(
trim_quotes(commands.item.as_bytes()),
commands.span.start + 1,
)
} else {
(commands.item.as_bytes(), commands.span.start)
};
let (output, err) = lex(input, span_offset, &[], &[], false);
if let Some(err) = err {
report_error(&working_set, &err);
std::process::exit(1);
}
let (output, err) = lite_parse(&output);
if let Some(err) = err {
report_error(&working_set, &err);
std::process::exit(1);
}
let (output, err) = parse_block(&mut working_set, &output, false);
if let Some(err) = err {
report_error(&working_set, &err);
std::process::exit(1);
}
if let Some(err) = err {
report_error(&working_set, &err);
std::process::exit(1);
}
(output, working_set.render())
};
if let Err(err) = engine_state.merge_delta(delta, None, init_cwd) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err);
}
let mut stack = nu_protocol::engine::Stack::new();
// Set up our initial config to start from
stack.vars.insert(
CONFIG_VARIABLE_ID,
Value::Record {
cols: vec![],
vals: vec![],
span: Span { start: 0, end: 0 },
},
);
let config = match stack.get_config() {
Ok(config) => config,
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
Config::default()
}
};
// Merge the delta in case env vars changed in the config
match nu_engine::env::current_dir(engine_state, &stack) {
Ok(cwd) => {
if let Err(e) = engine_state.merge_delta(StateDelta::new(), Some(&mut stack), cwd) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
}
}
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
}
}
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, &stack, &config) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
std::process::exit(1);
}
match eval_block(engine_state, &mut stack, &block, input) {
Ok(pipeline_data) => {
for item in pipeline_data {
if let Value::Error { error } = item {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &error);
std::process::exit(1);
}
println!("{}", item.into_string("\n", &config));
}
}
Err(err) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err);
std::process::exit(1);
}
}
Ok(())
}

View File

@ -1,3 +1,4 @@
mod commands;
mod config_files; mod config_files;
mod eval_file; mod eval_file;
mod logger; mod logger;
@ -75,9 +76,17 @@ fn main() -> Result<()> {
let mut collect_arg_nushell = false; let mut collect_arg_nushell = false;
for arg in std::env::args().skip(1) { for arg in std::env::args().skip(1) {
if !script_name.is_empty() { if !script_name.is_empty() {
args_to_script.push(arg); args_to_script.push(if arg.contains(' ') {
format!("'{}'", arg)
} else {
arg
});
} else if collect_arg_nushell { } else if collect_arg_nushell {
args_to_nushell.push(arg); args_to_nushell.push(if arg.contains(' ') {
format!("'{}'", arg)
} else {
arg
});
collect_arg_nushell = false; collect_arg_nushell = false;
} else if arg.starts_with('-') { } else if arg.starts_with('-') {
// Cool, it's a flag // Cool, it's a flag
@ -106,23 +115,25 @@ fn main() -> Result<()> {
match nushell_config { match nushell_config {
Ok(nushell_config) => { Ok(nushell_config) => {
if !script_name.is_empty() { let input = if let Some(redirect_stdin) = &nushell_config.redirect_stdin {
let input = if let Some(redirect_stdin) = &nushell_config.redirect_stdin { let stdin = std::io::stdin();
let stdin = std::io::stdin(); let buf_reader = BufReader::new(stdin);
let buf_reader = BufReader::new(stdin);
PipelineData::ByteStream( PipelineData::ByteStream(
ByteStream { ByteStream {
stream: Box::new(BufferedReader::new(buf_reader)), stream: Box::new(BufferedReader::new(buf_reader)),
ctrlc: Some(ctrlc), ctrlc: Some(ctrlc),
}, },
redirect_stdin.span, redirect_stdin.span,
None, None,
) )
} else { } else {
PipelineData::new(Span::new(0, 0)) PipelineData::new(Span::new(0, 0))
}; };
if let Some(commands) = &nushell_config.commands {
commands::evaluate(commands, &init_cwd, &mut engine_state, input)
} else if !script_name.is_empty() && nushell_config.interactive_shell.is_none() {
eval_file::evaluate( eval_file::evaluate(
script_name, script_name,
&args_to_script, &args_to_script,
@ -131,7 +142,7 @@ fn main() -> Result<()> {
input, input,
) )
} else { } else {
repl::evaluate(ctrlc, &mut engine_state) repl::evaluate(&mut engine_state)
} }
} }
Err(_) => std::process::exit(1), Err(_) => std::process::exit(1),
@ -178,6 +189,20 @@ fn parse_commandline_args(
}) = expressions.get(0) }) = expressions.get(0)
{ {
let redirect_stdin = call.get_named_arg("stdin"); let redirect_stdin = call.get_named_arg("stdin");
let login_shell = call.get_named_arg("login");
let interactive_shell = call.get_named_arg("interactive");
let commands: Option<Expression> = call.get_flag_expr("commands");
let commands = if let Some(expression) = commands {
let contents = engine_state.get_span_contents(&expression.span);
Some(Spanned {
item: String::from_utf8_lossy(contents).to_string(),
span: expression.span,
})
} else {
None
};
let help = call.has_flag("help"); let help = call.has_flag("help");
@ -188,7 +213,12 @@ fn parse_commandline_args(
std::process::exit(1); std::process::exit(1);
} }
return Ok(NushellConfig { redirect_stdin }); return Ok(NushellConfig {
redirect_stdin,
login_shell,
interactive_shell,
commands,
});
} }
} }
@ -200,6 +230,10 @@ fn parse_commandline_args(
struct NushellConfig { struct NushellConfig {
redirect_stdin: Option<Spanned<String>>, redirect_stdin: Option<Spanned<String>>,
#[allow(dead_code)]
login_shell: Option<Spanned<String>>,
interactive_shell: Option<Spanned<String>>,
commands: Option<Spanned<String>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -214,6 +248,14 @@ impl Command for Nu {
Signature::build("nu") Signature::build("nu")
.desc("The nushell language and shell.") .desc("The nushell language and shell.")
.switch("stdin", "redirect the stdin", None) .switch("stdin", "redirect the stdin", None)
.switch("login", "start as a login shell", Some('l'))
.switch("interactive", "start as an interactive shell", Some('i'))
.named(
"commands",
SyntaxShape::String,
"run the given commands and then exit",
Some('c'),
)
.optional( .optional(
"script file", "script file",
SyntaxShape::Filepath, SyntaxShape::Filepath,

View File

@ -1,10 +1,4 @@
use std::{ use std::{sync::atomic::Ordering, time::Instant};
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
time::Instant,
};
use crate::{config_files, prompt_update, reedline_config}; use crate::{config_files, prompt_update, reedline_config};
use crate::{ use crate::{
@ -23,7 +17,7 @@ use nu_protocol::{
}; };
use reedline::{DefaultHinter, Emacs, Vi}; use reedline::{DefaultHinter, Emacs, Vi};
pub(crate) fn evaluate(ctrlc: Arc<AtomicBool>, engine_state: &mut EngineState) -> Result<()> { pub(crate) fn evaluate(engine_state: &mut EngineState) -> Result<()> {
use crate::logger::{configure, logger}; use crate::logger::{configure, logger};
use reedline::{FileBackedHistory, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
@ -97,7 +91,9 @@ pub(crate) fn evaluate(ctrlc: Arc<AtomicBool>, engine_state: &mut EngineState) -
}; };
//Reset the ctrl-c handler //Reset the ctrl-c handler
ctrlc.store(false, Ordering::SeqCst); if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst);
}
let line_editor = Reedline::create() let line_editor = Reedline::create()
.into_diagnostic()? .into_diagnostic()?

View File

@ -72,7 +72,7 @@ fn in_variable_6() -> TestResult {
#[test] #[test]
fn help_works_with_missing_requirements() -> TestResult { fn help_works_with_missing_requirements() -> TestResult {
run_test(r#"each --help | lines | length"#, "15") run_test(r#"each --help | lines | length"#, "17")
} }
#[test] #[test]