diff options
| author | renato-umeton <25375373+renato-umeton@users.noreply.github.com> | 2025-08-02 05:50:34 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-02 17:50:34 +0800 |
| commit | 1cce99014cd777151d7ab7328612c3c435ef1a06 (patch) | |
| tree | 5ebbea6f9c0fccb36bc765c6f181a88fccc7dc1b | |
| parent | a5d484c7275a1bf438eedc7dda7c217c78d9d2d7 (diff) | |
| download | aichat-1cce99014cd777151d7ab7328612c3c435ef1a06.tar.gz | |
feat: eliminate return requirement for -e mode option selection (#1374)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: sigoden <sigoden@gmail.com>
| -rw-r--r-- | src/main.rs | 30 | ||||
| -rw-r--r-- | src/utils/input.rs | 47 | ||||
| -rw-r--r-- | src/utils/mod.rs | 2 |
3 files changed, 57 insertions, 22 deletions
diff --git a/src/main.rs b/src/main.rs index 444340b..369a282 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,12 +26,10 @@ use crate::utils::*; use anyhow::{bail, Result}; use clap::Parser; -use inquire::validator::Validation; use inquire::Text; -use is_terminal::IsTerminal; use parking_lot::RwLock; use simplelog::{format_description, ConfigBuilder, LevelFilter, SimpleLogger, WriteLogger}; -use std::{env, io::stdin, process, sync::Arc}; +use std::{env, process, sync::Arc}; #[tokio::main] async fn main() -> Result<()> { @@ -262,9 +260,6 @@ async fn shell_execute( return Ok(()); } if *IS_STDOUT_TERMINAL { - if cfg!(target_os = "macos") && !stdin().is_terminal() { - bail!("Unable to read the pipe for shell execution on MacOS") - } let options = ["execute", "revise", "describe", "copy", "quit"]; let command = color_text(eval_str.trim(), nu_ansi_term::Color::Rgb(255, 165, 0)); let first_letter_color = nu_ansi_term::Color::Cyan; @@ -275,20 +270,11 @@ async fn shell_execute( .join(&dimmed_text(" | ")); loop { println!("{command}"); - let answer = Text::new(&format!("{prompt_text}:")) - .with_default("e") - .with_validator( - |input: &str| match matches!(input, "e" | "r" | "d" | "c" | "q") { - true => Ok(Validation::Valid), - false => Ok(Validation::Invalid( - "Invalid option, choice one of e, r, d, c or q".into(), - )), - }, - ) - .prompt()?; + let answer_char = + read_single_key(&['e', 'r', 'd', 'c', 'q'], 'e', &format!("{prompt_text}: "))?; - match answer.as_str() { - "e" => { + match answer_char { + 'e' => { debug!("{} {:?}", shell.cmd, &[&shell.arg, &eval_str]); let code = run_command(&shell.cmd, &[&shell.arg, &eval_str], None)?; if code == 0 && config.read().save_shell_history { @@ -296,13 +282,13 @@ async fn shell_execute( } process::exit(code); } - "r" => { + 'r' => { let revision = Text::new("Enter your revision:").prompt()?; let text = format!("{}\n{revision}", input.text()); input.set_text(text); return shell_execute(config, shell, input, abort_signal.clone()).await; } - "d" => { + 'd' => { let role = config.read().retrieve_role(EXPLAIN_SHELL_ROLE)?; let input = Input::from_str(config, &eval_str, Some(role)); if input.stream() { @@ -325,7 +311,7 @@ async fn shell_execute( println!(); continue; } - "c" => { + 'c' => { set_text(&eval_str)?; println!("{}", dimmed_text("✓ Copied the command.")); } diff --git a/src/utils/input.rs b/src/utils/input.rs new file mode 100644 index 0000000..ebcb3cf --- /dev/null +++ b/src/utils/input.rs @@ -0,0 +1,47 @@ +use anyhow::Result; +use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use std::io::{stdout, Write}; + +/// Reads a single character from stdin without requiring Enter +/// Returns the character if it's one of the valid options, or the default if Enter is pressed +pub fn read_single_key(valid_chars: &[char], default: char, prompt: &str) -> Result<char> { + print!("{prompt}"); + stdout().flush()?; + + enable_raw_mode()?; + + let result = loop { + if let Ok(Event::Key(KeyEvent { + code, modifiers, .. + })) = event::read() + { + match code { + KeyCode::Char('c') if modifiers.contains(KeyModifiers::CONTROL) => { + break Err(anyhow::anyhow!("Interrupted")); + } + KeyCode::Char(c) => { + if valid_chars.contains(&c) { + break Ok(c); + } + // Invalid character, continue loop + } + KeyCode::Enter => { + break Ok(default); + } + _ => { + // Other keys are ignored, continue loop + } + } + } + }; + + disable_raw_mode()?; + + // Print the chosen character and newline for clean output + if let Ok(chosen) = &result { + println!("{chosen}"); + } + + result +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 8a90516..31a05e2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,7 @@ mod clipboard; mod command; mod crypto; mod html_to_md; +mod input; mod loader; mod path; mod render_prompt; @@ -15,6 +16,7 @@ pub use self::clipboard::set_text; pub use self::command::*; pub use self::crypto::*; pub use self::html_to_md::*; +pub use self::input::*; pub use self::loader::*; pub use self::path::*; pub use self::render_prompt::render_prompt; |
