configurable cd hook

Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
Ava Apples Affine 2024-02-20 14:27:11 -08:00
parent 6351ac63d2
commit 50ebfd2e37
4 changed files with 77 additions and 29 deletions

View file

@ -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_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_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_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 ** Prompt design
For an example of prompt design see [[file:snippets/mood-prompt.f][the mood prompt]] 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 ** TODO v1.0 tasks
- Can pass args to flesh scripts (via command line) - Can pass args to flesh scripts (via command line)
- Can pass args to flesh scripts (via interpreter) - 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 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 - Release CI
- Make an icon if you feel like it - Make an icon if you feel like it
- Post release to relevant channels - Post release to relevant channels
** TODO v1.1 tasks ** TODO v1.1 tasks
- emacs lsp implementation - Pipe also operates on stderr
- finish stretch goals in the [[file:snippets/interactive-devel.f][interactive development library]] - better emacs language mode
- finish the [[file:snippets/interactive-devel.f][interactive development library]]
- History length configurable (env var?) - History length configurable (env var?)
- execute configurable function on cd
- Post to relevant channels - Post to relevant channels
- Implement Compose for lambdas - Implement Compose for lambdas
(add to readme) (add to readme)

View file

@ -143,6 +143,9 @@ Flesh also provides a ~cd~ utility to change current working directory:
(cd (concat HOME '/repositories')) ;; $ cd ~/repositories (cd (concat HOME '/repositories')) ;; $ cd ~/repositories
#+END_EXAMPLE #+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 ** Creating bindings for shell commands
Daily Flesh users will long for first class shell commands that are accounted for in autocomplete. Daily Flesh users will long for first class shell commands that are accounted for in autocomplete.

View file

@ -33,7 +33,6 @@ pub mod ast {
} }
pub mod stdlib { pub mod stdlib {
pub use crate::stl::{};
pub use crate::stl::{ pub use crate::stl::{
dynamic_stdlib, static_stdlib, dynamic_stdlib, static_stdlib,
load_defaults, load_environment, load_defaults, load_environment,

View file

@ -63,6 +63,7 @@ use {
}; };
pub const POSIX_LOAD_NAME: &str = "load"; pub const POSIX_LOAD_NAME: &str = "load";
pub const CD_USER_CB: &str = "CFG_FLESH_CD_CB";
pub struct ShellState { pub struct ShellState {
pub parent_pid: Pid, pub parent_pid: Pid,
@ -714,38 +715,69 @@ fn fg_callback(
} }
const CD_DOCSTRING: &str = 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<Ctr, Traceback> { fn cd_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::String(ref dir) = *ast.car { 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); let dirp = Path::new(dir);
match canonicalize(dirp) {
Ok(abs) => {
if let Err(s) = set_current_dir(dirp) { if let Err(s) = set_current_dir(dirp) {
Err(start_trace( Err(start_trace(
("cd", s.to_string()) ("cd", s.to_string())
.into())) .into()))
} else { } else {
if let Ok(abs) = canonicalize(dirp) {
syms.insert( syms.insert(
String::from("PWD"), String::from("PWD"),
Symbol::from_ast( Symbol::from_ast(
&String::from("PWD"), &String::from("PWD"),
&String::from("current working directory"), &String::from("current working directory, set by cd"),
&Seg::from_mono(Box::new( &Seg::from_mono(Box::new(
Ctr::String(abs.to_string_lossy().into()))), Ctr::String(abs.to_string_lossy().into()))),
None, None,
)); ));
// Run configurable user function match syms.call_symbol(
&CD_USER_CB.to_string(),
Ok(Ctr::None) &Seg::new(),
} else { true,
Err(start_trace( ) {
("cd", "cannot make absolute path").into())) Ok(r) => {
Ok(*r)
},
Err(s) => {
Err(s.with_trace(("cd", "failed to call callback").into()))
},
} }
} }
} else { },
Err(start_trace( Err(e) => {
("cd", "expected input to be a string") Err(
.into())) start_trace( ("cd", e.to_string()).into())
.with_trace(("cd", "could not make absolute path").into()))
}
} }
} }
@ -948,11 +980,25 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
"cd".to_string(), "cd".to_string(),
Symbol { Symbol {
name: String::from("cd"), name: String::from("cd"),
args: Args::Strict(vec![Type::String]), args: Args::Lazy(1),
conditional_branches: false, conditional_branches: true,
docs: CD_DOCSTRING.to_string(), docs: CD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(cd_callback)), value: ValueType::Internal(Rc::new(cd_callback)),
..Default::default() ..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<Ctr, Traceback> {
Ok(Ctr::None)
})),
..Default::default()
},
);
} }