From b550fbb3e4794be681655c7cb1acb6df47efe882 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Sun, 20 Apr 2025 17:03:43 -0300 Subject: [PATCH] Only initialize Rustyline if we are in a tty --- Cargo.lock | 6 +++-- cli/Cargo.toml | 2 ++ cli/app.rs | 59 ++++++++++++++++++++++++++++++++++++++++---------- cli/main.rs | 37 +++++++++++++++++++++++-------- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 810b9983e..a463c03b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1575,9 +1575,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" @@ -1702,8 +1702,10 @@ dependencies = [ "ctrlc", "dirs 5.0.1", "env_logger 0.10.2", + "libc", "limbo_core", "miette", + "nix 0.29.0", "nu-ansi-term 0.50.1", "rustyline", "shlex", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2f1625420..65f39ad15 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -28,10 +28,12 @@ csv = "1.3.1" ctrlc = "3.4.4" dirs = "5.0.1" env_logger = "0.10.1" +libc = "0.2.172" limbo_core = { path = "../core", default-features = true, features = [ "completion", ] } miette = { version = "7.4.0", features = ["fancy"] } +nix = "0.29.0" nu-ansi-term = "0.50.1" rustyline = { version = "15.0.0", default-features = true, features = [ "derive", diff --git a/cli/app.rs b/cli/app.rs index bb9515660..16c464b41 100644 --- a/cli/app.rs +++ b/cli/app.rs @@ -7,6 +7,7 @@ use crate::{ helper::LimboHelper, input::{get_io, get_writer, DbLocation, OutputMode, Settings}, opcodes_dictionary::OPCODE_DESCRIPTIONS, + HISTORY_FILE, }; use comfy_table::{Attribute, Cell, CellAlignment, Color, ContentArrangement, Row, Table}; use limbo_core::{Database, LimboError, OwnedValue, Statement, StepResult}; @@ -14,10 +15,10 @@ use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; use clap::Parser; -use rustyline::{history::DefaultHistory, Editor}; +use rustyline::{error::ReadlineError, history::DefaultHistory, Editor}; use std::{ fmt, - io::{self, Write}, + io::{self, BufRead as _, Write}, path::PathBuf, rc::Rc, sync::{ @@ -62,7 +63,7 @@ pub struct Opts { const PROMPT: &str = "limbo> "; -pub struct Limbo<'a> { +pub struct Limbo { pub prompt: String, io: Arc, writer: Box, @@ -70,7 +71,7 @@ pub struct Limbo<'a> { pub interrupt_count: Arc, input_buff: String, opts: Settings, - pub rl: &'a mut Editor, + pub rl: Option>, } struct QueryStatistics { @@ -105,8 +106,8 @@ macro_rules! query_internal { static COLORS: &[Color] = &[Color::Green, Color::Black, Color::Grey]; -impl<'a> Limbo<'a> { - pub fn new(rl: &'a mut rustyline::Editor) -> anyhow::Result { +impl Limbo { + pub fn new() -> anyhow::Result { let opts = Opts::parse(); let db_file = opts .database @@ -133,8 +134,6 @@ impl<'a> Limbo<'a> { ) }; let conn = db.connect()?; - let h = LimboHelper::new(conn.clone(), io.clone()); - rl.set_helper(Some(h)); let interrupt_count = Arc::new(AtomicUsize::new(0)); { let interrupt_count: Arc = Arc::clone(&interrupt_count); @@ -154,12 +153,19 @@ impl<'a> Limbo<'a> { interrupt_count, input_buff: String::new(), opts: Settings::from(opts), - rl, + rl: None, }; app.first_run(sql, quiet)?; Ok(app) } + pub fn with_readline(mut self, mut rl: Editor) -> Self { + let h = LimboHelper::new(self.conn.clone(), self.io.clone()); + rl.set_helper(Some(h)); + self.rl = Some(rl); + self + } + fn first_run(&mut self, sql: Option, quiet: bool) -> io::Result<()> { if let Some(sql) = sql { self.handle_first_input(&sql); @@ -470,8 +476,9 @@ impl<'a> Limbo<'a> { } } - fn reset_line(&mut self, line: &str) -> rustyline::Result<()> { - self.rl.add_history_entry(line.to_owned())?; + fn reset_line(&mut self, _line: &str) -> rustyline::Result<()> { + // Entry is auto added to history + // self.rl.add_history_entry(line.to_owned())?; self.interrupt_count.store(0, Ordering::SeqCst); Ok(()) } @@ -973,4 +980,34 @@ impl<'a> Limbo<'a> { self.run_query(buff.as_str()); self.reset_input(); } + + pub fn readline(&mut self) -> Result { + if let Some(rl) = &mut self.rl { + Ok(rl.readline(&self.prompt)?) + } else { + let mut input = String::new(); + println!(""); + let mut reader = std::io::stdin().lock(); + if reader.read_line(&mut input)? == 0 { + return Err(ReadlineError::Eof.into()); + } + // Remove trailing newline + if input.ends_with('\n') { + input.pop(); + if input.ends_with('\r') { + input.pop(); + } + } + + Ok(input) + } + } +} + +impl Drop for Limbo { + fn drop(&mut self) { + if let Some(rl) = &mut self.rl { + let _ = rl.save_history(HISTORY_FILE.as_path()); + } + } } diff --git a/cli/main.rs b/cli/main.rs index ec81b64af..f0fe4c934 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -5,26 +5,41 @@ mod helper; mod input; mod opcodes_dictionary; +use nix::unistd::isatty; use rustyline::{error::ReadlineError, Config, Editor}; -use std::sync::atomic::Ordering; +use std::{ + path::PathBuf, + sync::{atomic::Ordering, LazyLock}, +}; fn rustyline_config() -> Config { Config::builder() .completion_type(rustyline::CompletionType::List) + .auto_add_history(true) .build() } +pub static HOME_DIR: LazyLock = + LazyLock::new(|| dirs::home_dir().expect("Could not determine home directory")); + +pub static HISTORY_FILE: LazyLock = LazyLock::new(|| HOME_DIR.join(".limbo_history")); + fn main() -> anyhow::Result<()> { - let mut rl = Editor::with_config(rustyline_config())?; - let mut app = app::Limbo::new(&mut rl)?; + let mut app = app::Limbo::new()?; let _guard = app.init_tracing()?; - let home = dirs::home_dir().expect("Could not determine home directory"); - let history_file = home.join(".limbo_history"); - if history_file.exists() { - app.rl.load_history(history_file.as_path())?; + + if is_a_tty() { + let mut rl = Editor::with_config(rustyline_config())?; + if HISTORY_FILE.exists() { + rl.load_history(HISTORY_FILE.as_path())?; + } + app = app.with_readline(rl); + } else { + tracing::debug!("not in tty"); } + loop { - let readline = app.rl.readline(&app.prompt); + let readline = app.readline(); match readline { Ok(line) => match app.handle_input_line(line.trim()) { Ok(_) => {} @@ -54,6 +69,10 @@ fn main() -> anyhow::Result<()> { } } } - rl.save_history(history_file.as_path())?; Ok(()) } + +/// Return whether or not STDIN is a TTY +fn is_a_tty() -> bool { + isatty(libc::STDIN_FILENO).unwrap_or(false) +}