Add --no-newline option to nu (#12410)

# Description
I have `nu` set as my shell in my editor, which allows me to easily pipe
selections of text to things like `str pascal-case` or even more complex
string operation pipelines, which I find super handy. However, the only
annoying thing is that I pretty much always have to add `| print -n` at
the end, because `nu` adds a newline when it prints the resulting value.

This adds a `--no-newline` option to stop that from happening, and then
you don't need to pipe to `print -n` anymore, you can just have your
shell command for your editor contain that flag.

# User-Facing Changes
- Add `--no-newline` command line option

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
This commit is contained in:
Devyn Cairns 2024-04-09 07:04:00 -07:00 committed by GitHub
parent 00b3a07efe
commit 14b0ff3f05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 41 additions and 6 deletions

View File

@ -85,6 +85,7 @@ fn bench_command_with_custom_stack_and_engine(
&mut stack.clone(),
PipelineData::empty(),
None,
false,
)
.unwrap();
})
@ -104,6 +105,7 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
&mut stack,
PipelineData::empty(),
None,
false,
)
.unwrap();
@ -263,6 +265,7 @@ mod eval_commands {
&mut nu_protocol::engine::Stack::new(),
PipelineData::empty(),
None,
false,
)
.unwrap();
})

View File

@ -15,6 +15,7 @@ pub fn evaluate_commands(
stack: &mut Stack,
input: PipelineData,
table_mode: Option<Value>,
no_newline: bool,
) -> Result<Option<i64>> {
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
@ -60,7 +61,13 @@ pub fn evaluate_commands(
if let Some(t_mode) = table_mode {
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default();
}
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
crate::eval_file::print_table_or_error(
engine_state,
stack,
pipeline_data,
&mut config,
no_newline,
)
}
Err(err) => {
let working_set = StateWorkingSet::new(engine_state);

View File

@ -185,6 +185,7 @@ pub(crate) fn print_table_or_error(
stack: &mut Stack,
mut pipeline_data: PipelineData,
config: &mut Config,
no_newline: bool,
) -> Option<i64> {
let exit_code = match &mut pipeline_data {
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
@ -203,7 +204,7 @@ pub(crate) fn print_table_or_error(
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
let command = engine_state.get_decl(decl_id);
if command.get_block_id().is_some() {
print_or_exit(pipeline_data, engine_state, config);
print_or_exit(pipeline_data, engine_state, config, no_newline);
} else {
// The final call on table command, it's ok to set redirect_output to false.
let call = Call::new(Span::new(0, 0));
@ -211,7 +212,7 @@ pub(crate) fn print_table_or_error(
match table {
Ok(table) => {
print_or_exit(table, engine_state, config);
print_or_exit(table, engine_state, config, no_newline);
}
Err(error) => {
let working_set = StateWorkingSet::new(engine_state);
@ -221,7 +222,7 @@ pub(crate) fn print_table_or_error(
}
}
} else {
print_or_exit(pipeline_data, engine_state, config);
print_or_exit(pipeline_data, engine_state, config, no_newline);
}
// Make sure everything has finished
@ -238,7 +239,12 @@ pub(crate) fn print_table_or_error(
}
}
fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, config: &Config) {
fn print_or_exit(
pipeline_data: PipelineData,
engine_state: &mut EngineState,
config: &Config,
no_newline: bool,
) {
for item in pipeline_data {
if let Value::Error { error, .. } = item {
let working_set = StateWorkingSet::new(engine_state);
@ -248,7 +254,10 @@ fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, co
std::process::exit(1);
}
let out = item.to_expanded_string("\n", config) + "\n";
let mut out = item.to_expanded_string("\n", config);
if !no_newline {
out.push('\n');
}
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
}
}

View File

@ -97,6 +97,7 @@ pub(crate) fn parse_commandline_args(
let execute = call.get_flag_expr("execute");
let table_mode: Option<Value> =
call.get_flag(engine_state, &mut stack, "table-mode")?;
let no_newline = call.get_named_arg("no-newline");
// ide flags
let lsp = call.has_flag(engine_state, &mut stack, "lsp")?;
@ -190,6 +191,7 @@ pub(crate) fn parse_commandline_args(
ide_check,
ide_ast,
table_mode,
no_newline,
});
}
}
@ -224,6 +226,7 @@ pub(crate) struct NushellCliArgs {
pub(crate) log_target: Option<Spanned<String>>,
pub(crate) execute: Option<Spanned<String>>,
pub(crate) table_mode: Option<Value>,
pub(crate) no_newline: Option<Spanned<String>>,
pub(crate) include_path: Option<Spanned<String>>,
pub(crate) lsp: bool,
pub(crate) ide_goto_def: Option<Value>,
@ -270,6 +273,7 @@ impl Command for Nu {
"the table mode to use. rounded is default.",
Some('m'),
)
.switch("no-newline", "print the result for --commands(-c) without a newline", None)
.switch(
"no-config-file",
"start with no config file and no env file",

View File

@ -118,6 +118,7 @@ pub(crate) fn run_commands(
&mut stack,
input,
parsed_nu_cli_args.table_mode,
parsed_nu_cli_args.no_newline.is_some(),
);
perf(
"evaluate_commands",

View File

@ -273,6 +273,17 @@ fn run_in_noninteractive_mode() {
assert!(child_output.stderr.is_empty());
}
#[test]
fn run_with_no_newline() {
let child_output = std::process::Command::new(nu_test_support::fs::executable_path())
.args(["--no-newline", "-c", "\"hello world\""])
.output()
.expect("failed to run nu");
assert_eq!("hello world", String::from_utf8_lossy(&child_output.stdout)); // with no newline
assert!(child_output.stderr.is_empty());
}
#[test]
fn main_script_can_have_subcommands1() {
Playground::setup("main_subcommands", |dirs, sandbox| {