summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs30
-rw-r--r--src/utils/input.rs47
-rw-r--r--src/utils/mod.rs2
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;