diff --git a/Readme.org b/Readme.org index 07e62fa..a8cab6a 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,19 +165,16 @@ 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 -- tab completion for symbols also attempts to complete paths -- finish basic goals in the [[file:snippets/interactive-devel.f][interactive development library]] +- 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]] +- all autocomplete is done via configurable userfunction, default documented with 1:1 functionality +- 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..303d90f 100644 --- a/Shell.org +++ b/Shell.org @@ -143,6 +143,13 @@ 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. + +WARNING: Symbols will be taken as strings unless wrapped in a form. + > Example: cd HOME => cd 'HOME' + > Example: cd (eval HOME) => cd /path/to/home/dir + ** 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/bin/flesh.rs b/src/bin/flesh.rs index 95bfc19..4ca41be 100644 --- a/src/bin/flesh.rs +++ b/src/bin/flesh.rs @@ -106,7 +106,7 @@ impl Completer for CustomCompleter { .expect("current dir bad?"); let (tok, is_str, start) = get_token_to_complete(line, pos); let mut sugg = vec![]; - if !is_str { + if !is_str && !tok.contains('/') { let mut offcenter_match = vec![]; for sym in &self.0 { if sym.starts_with(tok.as_str()) { 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 0257fad..6fd6f6a 100644 --- a/src/stl/posix.rs +++ b/src/stl/posix.rs @@ -41,7 +41,7 @@ use { io::Result as IOResult, rc::Rc, fs::{ - File, OpenOptions + File, OpenOptions, canonicalize, }, path::Path, process::{ @@ -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,22 +715,73 @@ fn fg_callback( } const CD_DOCSTRING: &str = - "Expects 1 argument (a string). Changes to a new directory"; -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 { - Ok(Ctr::None) - } - } else { - Err(start_trace( - ("cd", "expected input to be a string") + "Expects 1 argument (a string, symbol, or form). Changes to a directory. +WARNING: Symbols will be taken as strings unless wrapped in a form. + > Example: cd HOME => cd 'HOME' + > Example: cd (eval HOME) => cd /path/to/home/dir +If CFG_RELISH_CD_CB is defined, cd will evaluate it. +cd returns the result that CFG_RELISH_CD_CB returns, or None"; +fn cd_callback(ast: &Seg, syms: &mut SymTable) -> Result { + 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, set by cd"), + &Seg::from_mono(Box::new( + Ctr::String(abs.to_string_lossy().into()))), + None, + )); + + 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())) + } + } } pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc>) { @@ -931,11 +983,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() + }, + ); }