From 50ebfd2e37ee6298f1e637629bffb6f4b3505587 Mon Sep 17 00:00:00 2001 From: Ava Affine Date: Tue, 20 Feb 2024 14:27:11 -0800 Subject: [PATCH] configurable cd hook Signed-off-by: Ava Affine --- Readme.org | 12 +++---- Shell.org | 3 ++ src/lib.rs | 1 - src/stl/posix.rs | 90 ++++++++++++++++++++++++++++++++++++------------ 4 files changed, 77 insertions(+), 29 deletions(-) diff --git a/Readme.org b/Readme.org index 89683bc..fd8c6d3 100644 --- a/Readme.org +++ b/Readme.org @@ -68,6 +68,7 @@ Errors during configuration are non-terminal. In such a case any defaults which - CFG_FLESH_L_PROMPT (default 'λ'): a function that is called with no arguments to output the left hand of the prompt - CFG_FLESH_R_PROMPT (default ''): a function that is called with no arguments to output the right hand of the prompt - CFG_FLESH_PROMPT_DELIMITER (default '>'): a function that is called with no arguments to output the delimiter separating prompt from user input +- CFG_FLESH_CD_CB (default None): optional function / lambda that is called with no arguments on every change of CD. See [[file:Shell.org][The Shell Documentation]] for more information. ** Prompt design For an example of prompt design see [[file:snippets/mood-prompt.f][the mood prompt]] @@ -164,18 +165,17 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt ** TODO v1.0 tasks - Can pass args to flesh scripts (via command line) - Can pass args to flesh scripts (via interpreter) -- Pipe also operates on stderr -- ~ if possible (string replacement?) - tab completion for symbols also attempts to complete binaries -- finish basic goals in the [[file:snippets/interactive-devel.f][interactive development library]] +- cd accepts symbols as well as strings (emulate shell) +- declare macros - Release CI - Make an icon if you feel like it - Post release to relevant channels ** TODO v1.1 tasks -- emacs lsp implementation -- finish stretch goals in the [[file:snippets/interactive-devel.f][interactive development library]] +- Pipe also operates on stderr +- better emacs language mode +- finish the [[file:snippets/interactive-devel.f][interactive development library]] - History length configurable (env var?) -- execute configurable function on cd - Post to relevant channels - Implement Compose for lambdas (add to readme) diff --git a/Shell.org b/Shell.org index 1b1cac3..5b15362 100644 --- a/Shell.org +++ b/Shell.org @@ -143,6 +143,9 @@ Flesh also provides a ~cd~ utility to change current working directory: (cd (concat HOME '/repositories')) ;; $ cd ~/repositories #+END_EXAMPLE +This ~cd~ routine will keep the ~PWD~ variable up to date. +Every invokation of ~cd~ will call whatever lambda or function is stored in ~CFG_FLESH_CD_CB~ with no arguments. A function stored in this variable can then perform on the fly reconfiguration or set additional variables based on the directory that the user has entered. + ** Creating bindings for shell commands Daily Flesh users will long for first class shell commands that are accounted for in autocomplete. diff --git a/src/lib.rs b/src/lib.rs index 5eb99fa..579373c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,6 @@ pub mod ast { } pub mod stdlib { - pub use crate::stl::{}; pub use crate::stl::{ dynamic_stdlib, static_stdlib, load_defaults, load_environment, diff --git a/src/stl/posix.rs b/src/stl/posix.rs index d8e5230..8c9f47b 100644 --- a/src/stl/posix.rs +++ b/src/stl/posix.rs @@ -63,6 +63,7 @@ use { }; pub const POSIX_LOAD_NAME: &str = "load"; +pub const CD_USER_CB: &str = "CFG_FLESH_CD_CB"; pub struct ShellState { pub parent_pid: Pid, @@ -714,38 +715,69 @@ fn fg_callback( } const CD_DOCSTRING: &str = - "Expects 1 argument (a string). Changes to a new directory"; + "Expects 1 argument (a string). Changes to a new directory. +If CFG_RELISH_CD_CB is defined, cd will evaluate it. +cd returns the result that CFG_RELISH_CD_CB returns."; fn cd_callback(ast: &Seg, syms: &mut SymTable) -> Result { - if let Ctr::String(ref dir) = *ast.car { - let dirp = Path::new(dir); - if let Err(s) = set_current_dir(dirp) { - Err(start_trace( - ("cd", s.to_string()) - .into())) - } else { - if let Ok(abs) = canonicalize(dirp) { + let dir: &String; + match *ast.car { + Ctr::String(ref d) => dir = d, + Ctr::Symbol(ref d) => dir = d, + Ctr::Seg(ref s) => { + match eval(s, syms) { + Ok(r) => { + if r == ast.car { + return Err(start_trace(("cd", "innapropriate input.").into())); + } + return cd_callback(&Seg::from_mono(r), syms); + } + Err(e) => { + return Err(e.with_trace(("cd", "couldnt evaluate input").into())); + } + } + }, + _ => return Err(start_trace( + ("cd", "expected input to be a string or symbol") + .into())) + } + + let dirp = Path::new(dir); + match canonicalize(dirp) { + Ok(abs) => { + if let Err(s) = set_current_dir(dirp) { + Err(start_trace( + ("cd", s.to_string()) + .into())) + } else { syms.insert( String::from("PWD"), Symbol::from_ast( &String::from("PWD"), - &String::from("current working directory"), + &String::from("current working directory, set by cd"), &Seg::from_mono(Box::new( Ctr::String(abs.to_string_lossy().into()))), None, )); - // Run configurable user function - - Ok(Ctr::None) - } else { - Err(start_trace( - ("cd", "cannot make absolute path").into())) + match syms.call_symbol( + &CD_USER_CB.to_string(), + &Seg::new(), + true, + ) { + Ok(r) => { + Ok(*r) + }, + Err(s) => { + Err(s.with_trace(("cd", "failed to call callback").into())) + }, + } } + }, + Err(e) => { + Err( + start_trace( ("cd", e.to_string()).into()) + .with_trace(("cd", "could not make absolute path").into())) } - } else { - Err(start_trace( - ("cd", "expected input to be a string") - .into())) } } @@ -948,11 +980,25 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc "cd".to_string(), Symbol { name: String::from("cd"), - args: Args::Strict(vec![Type::String]), - conditional_branches: false, + args: Args::Lazy(1), + conditional_branches: true, docs: CD_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(cd_callback)), ..Default::default() }, ); + + syms.insert( + String::from(CD_USER_CB), + Symbol { + name: String::from(CD_USER_CB), + args: Args::None, + conditional_branches: false, + docs: String::from("lambda called at each invocation of cd"), + value: ValueType::Internal(Rc::new(move |_ast: &Seg, _sym: &mut SymTable| -> Result { + Ok(Ctr::None) + })), + ..Default::default() + }, + ); }