From cc1bf55b9996d556080c4104e211f24508b29cd8 Mon Sep 17 00:00:00 2001 From: Jonas Maier Date: Sat, 9 May 2026 17:25:28 +0200 Subject: can set custom prompt using the PROMPT variable --- src/lib.rs | 101 ++++++++++++++++++++++++++++++++++++----------------- src/parse/mod.rs | 16 ++------- src/run/builtin.rs | 5 ++- src/run/mod.rs | 8 ++++- 4 files changed, 82 insertions(+), 48 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cf57cee..2af7e08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ use std::collections::HashMap; use std::ffi::OsStr; use std::fs::{self, File}; -use std::hash::Hash; use std::io::{self, Read, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -79,6 +78,18 @@ type BString = Vec; #[allow(non_camel_case_types)] type bstr = [u8]; +trait PushAll { + fn push_all(&mut self, other: &bstr); +} + +impl PushAll for BString { + fn push_all(&mut self, other: &bstr) { + for &c in other { + self.push(c); + } + } +} + pub struct Session { raw: Option, line: LineBuf, @@ -142,35 +153,60 @@ fn relative_path(root: &Path, target: &Path) -> Option { } } -impl Session { - fn pretty_cwd_res(&self) -> io::Result { - let dir = std::env::current_dir()?; - let mut s = if let Some(home_dir) = std::env::home_dir() { - if let Some(rela) = relative_path(&home_dir, &dir) { - format!("~{rela}") - } else { - dir.to_string_lossy().to_string() - } +fn pretty_cwd_res() -> io::Result { + let dir = std::env::current_dir()?; + let mut s = if let Some(home_dir) = std::env::home_dir() { + if let Some(rela) = relative_path(&home_dir, &dir) { + format!("~{rela}") } else { dir.to_string_lossy().to_string() - }; - while s.ends_with("/") && s.len() > 1 { - s.remove(s.len() - 1); } - Ok(s) + } else { + dir.to_string_lossy().to_string() + }; + while s.ends_with("/") && s.len() > 1 { + s.remove(s.len() - 1); } + Ok(s) +} - fn pretty_cwd(&self) -> String { - self.pretty_cwd_res().unwrap_or_else(|_| String::new()) +fn pretty_cwd() -> String { + pretty_cwd_res().unwrap_or_else(|_| String::new()) +} + +impl Session { + fn load_unevaled_prompt(&self) -> BString { + match self.vars.get(&b"PROMPT"[..]) { + Some(prompt) => prompt.clone(), + None => { + let mut prompt = BString::new(); + if cfg!(debug_assertions) { + prompt.push_all(b"dev "); + } + prompt.push_all(b"[$CWD_PRETTY]# "); + prompt + } + } } - // TODO: prompt should be BString as well - fn prompt(&self) -> String { - #[cfg(debug_assertions)] - let dev = "dev "; - #[cfg(not(debug_assertions))] - let dev = ""; - format!("{dev}[{}]# ", self.pretty_cwd()) + fn prompt(this: Arc>) -> BString { + let mut prompt = this.lock().unwrap().load_unevaled_prompt(); + let mut x = Vec::with_capacity(prompt.len() + 2); + x.push(b'"'); + x.append(&mut prompt); + x.push(b'"'); + let parsed = match crate::parse::ExpString::parse_from_bytes(&x) { + Ok(x) => x, + Err(e) => { + println!("{e:?}"); + return b"PARSE_ERROR$ ".to_vec(); + } + }; + let mut expander = run::Executor::new(this); + let Ok(expanded) = parsed.expand(&mut expander) else { + return b"EXEC_ERROR$ ".to_vec(); + }; + expanded } fn clear_prompt(&mut self) { @@ -187,10 +223,11 @@ impl Session { self.history_visit = 0; } - fn reprint_prompt(&self) { - print!("{}", self.prompt()); - self.line.display_pre(); - self.line.display_post(b""); + fn reprint_prompt(this: Arc>) { + io::stdout().write_all(&Self::prompt(this.clone())).unwrap(); + let this = this.lock().unwrap(); + this.line.display_pre(); + this.line.display_post(b""); } fn display_historic_entry(&mut self) { @@ -372,7 +409,8 @@ impl Session { io::stdout().lock().write_all(&s.display).unwrap(); println!(); } - se.reprint_prompt(); + drop(se); + Self::reprint_prompt(session); } } @@ -400,9 +438,9 @@ impl Session { } } - fn screen_clear(&mut self) { + fn screen_clear(this: Arc>) { clear_screen(); - self.reprint_prompt(); + Self::reprint_prompt(this); } fn raw_enable(&self) { @@ -461,8 +499,7 @@ pub fn event_loop() { exec_rc_file(session.clone()); session.lock().unwrap().loud = true; - - print!("{}", session.lock().unwrap().prompt()); + Session::reprint_prompt(session.clone()); completion::populate_path_cache(session.clone()); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index c26e0c0..2c578cb 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,4 +1,4 @@ -use crate::{BString, bstr}; +use crate::{BString, bstr, PushAll}; #[cfg(test)] mod test; @@ -562,7 +562,7 @@ pub struct ExpString { } impl ExpString { - fn expand(self, e: &mut E) -> Res { + pub fn expand(self, e: &mut E) -> Res { let mut out = BString::new(); for part in self.parts.into_iter() { let mut x = match part { @@ -671,18 +671,6 @@ pub enum StringDelimiter { StrictCustom(BString), } -trait PushAll { - fn push_all(&mut self, other: &bstr); -} - -impl PushAll for BString { - fn push_all(&mut self, other: &bstr) { - for &c in other { - self.push(c); - } - } -} - /// gets the largest ident this slice starts with, might be empty fn peek_ident(b: &[u8]) -> &[u8] { if b.is_empty() || !b[0].is_ascii_alphabetic() { diff --git a/src/run/builtin.rs b/src/run/builtin.rs index f085005..bd82e27 100644 --- a/src/run/builtin.rs +++ b/src/run/builtin.rs @@ -836,7 +836,10 @@ impl Builtin for ct { b"cursor_right_word" => se.cursor_right_word(), b"cursor_left_word" => se.cursor_left_word(), b"prompt_clear" => se.prompt_clear(), - b"screen_clear" => se.screen_clear(), + b"screen_clear" => { + drop(se); + Session::screen_clear(session); + }, b"history_previous" => se.history_up(), b"history_next" => se.history_down(), b"prompt_del_left" => se.del_left(), diff --git a/src/run/mod.rs b/src/run/mod.rs index 7e575ce..bae9567 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -463,6 +463,11 @@ impl parse::Expander for Executor { var: BString, default: Option, ) -> Result { + match &var[..] { + b"CWD_PRETTY" => return Ok(crate::pretty_cwd().into_bytes()), + _ => {} + } + if var.is_empty() { return Err(ExecError::UnknownVariable(var)); } @@ -610,7 +615,8 @@ pub fn run(se: Arc>, parsed: Ast) { Err(e) => format!("{}\r\n", e.error_message()), }; - print!("\r{status_string}{}", se.lock().unwrap().prompt()); + print!("\r{status_string}"); + Session::reprint_prompt(se); let _ = std::io::stdout().lock().flush(); } } -- cgit v1.2.3