From fd4a04211a3af4a76e1aee38ab2e8394c42da060 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Mon, 18 Aug 2025 06:24:17 +1000 Subject: [PATCH] feat: `commandline edit --accept` to instantly execute command (#16193) # Description Fixes - #11065 Revised - #15092 Depends on - https://github.com/nushell/reedline/pull/933 Adds the `--accept` flag to the `commandline` command, to immediately accept the input. Example use case: atuin # User-facing changes Users get the ability to pass `--accept` or `-A` to `commandline edit` in order to immediately execute the resulting commandline. # Tests + Formatting I added two test cases that execute `commandline edit -A`. There is also some documentation about their unintuitive expectations output. # After Submitting The [docs](https://www.nushell.sh/commands/docs/commandline_edit.html) can be updated to the new flag: --accept, -A: immediately execute the command (no additional return required) > [!NOTE] > > This PR will be revised if / when https://github.com/nushell/reedline/pull/933 is accepted # Demo [![asciicast](https://asciinema.org/a/Ql4r1oWu8J0C4MecmIZjxw5Pg.svg)](https://asciinema.org/a/Ql4r1oWu8J0C4MecmIZjxw5Pg) --- crates/nu-cli/src/commands/commandline/edit.rs | 8 ++++++++ crates/nu-cli/src/repl.rs | 16 +++++++++++++--- crates/nu-protocol/src/engine/engine_state.rs | 4 ++++ tests/repl/test_commandline.rs | 8 ++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/nu-cli/src/commands/commandline/edit.rs b/crates/nu-cli/src/commands/commandline/edit.rs index 7b51ba0002..2a01321ae5 100644 --- a/crates/nu-cli/src/commands/commandline/edit.rs +++ b/crates/nu-cli/src/commands/commandline/edit.rs @@ -26,6 +26,11 @@ impl Command for CommandlineEdit { "replaces the current contents of the buffer (default)", Some('r'), ) + .switch( + "accept", + "immediately executes the result after edit", + Some('A'), + ) .required( "str", SyntaxShape::String, @@ -61,6 +66,9 @@ impl Command for CommandlineEdit { repl.buffer = str; repl.cursor_pos = repl.buffer.len(); } + + repl.accept = call.has_flag(engine_state, stack, "accept")?; + Ok(Value::nothing(call.head).into_pipeline_data()) } } diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 7ec0b725c9..be66618537 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -491,7 +491,9 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { // CLEAR STACK-REFERENCE 1 .with_highlighter(Box::::default()) // CLEAR STACK-REFERENCE 2 - .with_completer(Box::::default()); + .with_completer(Box::::default()) + // Ensure immediately accept is always cleared + .with_immediately_accept(false); // Let's grab the shell_integration configs let shell_integration_osc2 = config.shell_integration.osc2; @@ -671,7 +673,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { run_shell_integration_reset_application_mode(); } - flush_engine_state_repl_buffer(engine_state, &mut line_editor); + line_editor = flush_engine_state_repl_buffer(engine_state, line_editor); } Ok(Signal::CtrlC) => { // `Reedline` clears the line content. New prompt is shown @@ -1126,7 +1128,10 @@ fn run_shell_integration_reset_application_mode() { /// /// Clear the screen and output anything remaining in the EngineState buffer. /// -fn flush_engine_state_repl_buffer(engine_state: &mut EngineState, line_editor: &mut Reedline) { +fn flush_engine_state_repl_buffer( + engine_state: &mut EngineState, + mut line_editor: Reedline, +) -> Reedline { let mut repl = engine_state.repl_state.lock().expect("repl state mutex"); line_editor.run_edit_commands(&[ EditCommand::Clear, @@ -1136,8 +1141,13 @@ fn flush_engine_state_repl_buffer(engine_state: &mut EngineState, line_editor: & select: false, }, ]); + if repl.accept { + line_editor = line_editor.with_immediately_accept(true) + } + repl.accept = false; repl.buffer = "".to_string(); repl.cursor_pos = 0; + line_editor } /// diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 11335cfe06..45e7d13ef9 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -46,6 +46,8 @@ pub struct ReplState { pub buffer: String, // A byte position, as `EditCommand::MoveToPosition` is also a byte position pub cursor_pos: usize, + /// Immediately accept the buffer on the next loop. + pub accept: bool, } pub struct IsDebugging(AtomicBool); @@ -185,6 +187,7 @@ impl EngineState { repl_state: Arc::new(Mutex::new(ReplState { buffer: "".to_string(), cursor_pos: 0, + accept: false, })), table_decl_id: None, #[cfg(feature = "plugin")] @@ -1078,6 +1081,7 @@ impl EngineState { self.repl_state = Arc::new(Mutex::new(ReplState { buffer: "".to_string(), cursor_pos: 0, + accept: false, })); } if Mutex::is_poisoned(&self.jobs) { diff --git a/tests/repl/test_commandline.rs b/tests/repl/test_commandline.rs index 1b52797fc4..c40923cddb 100644 --- a/tests/repl/test_commandline.rs +++ b/tests/repl/test_commandline.rs @@ -140,3 +140,11 @@ fn commandline_test_cursor_end() -> TestResult { fn commandline_test_cursor_type() -> TestResult { run_test("commandline get-cursor | describe", "int") } + +#[test] +fn commandline_test_accepted_command() -> TestResult { + run_test( + "commandline edit --accept \"print accepted\"\n | commandline", + "print accepted", + ) +}