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_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)

View file

@ -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.

View file

@ -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,

View file

@ -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<Ctr, Traceback> {
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<RefCell<ShellState>
"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<Ctr, Traceback> {
Ok(Ctr::None)
})),
..Default::default()
},
);
}