Merge branch 'auto-complete-stuff' into 'main'

add tab completion for unescaped paths

See merge request whom/relish!12
This commit is contained in:
Ava Apples Affine 2024-02-20 22:40:37 +00:00
commit 13135def5a
5 changed files with 97 additions and 27 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,19 +165,16 @@ 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 - declare macros
- ~ 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]]
- 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 - all autocomplete is done via configurable userfunction, default documented with 1:1 functionality
- 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?) - 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,13 @@ 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.
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 ** 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

@ -106,7 +106,7 @@ impl Completer for CustomCompleter {
.expect("current dir bad?"); .expect("current dir bad?");
let (tok, is_str, start) = get_token_to_complete(line, pos); let (tok, is_str, start) = get_token_to_complete(line, pos);
let mut sugg = vec![]; let mut sugg = vec![];
if !is_str { if !is_str && !tok.contains('/') {
let mut offcenter_match = vec![]; let mut offcenter_match = vec![];
for sym in &self.0 { for sym in &self.0 {
if sym.starts_with(tok.as_str()) { if sym.starts_with(tok.as_str()) {

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

@ -41,7 +41,7 @@ use {
io::Result as IOResult, io::Result as IOResult,
rc::Rc, rc::Rc,
fs::{ fs::{
File, OpenOptions File, OpenOptions, canonicalize,
}, },
path::Path, path::Path,
process::{ process::{
@ -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,22 +715,73 @@ 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, symbol, or form). Changes to a directory.
fn cd_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> { WARNING: Symbols will be taken as strings unless wrapped in a form.
if let Ctr::String(ref dir) = *ast.car { > Example: cd HOME => cd 'HOME'
let dirp = Path::new(dir); > Example: cd (eval HOME) => cd /path/to/home/dir
if let Err(s) = set_current_dir(dirp) { If CFG_RELISH_CD_CB is defined, cd will evaluate it.
Err(start_trace( cd returns the result that CFG_RELISH_CD_CB returns, or None";
("cd", s.to_string()) fn cd_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
.into())) let dir: &String;
} else { match *ast.car {
Ok(Ctr::None) Ctr::String(ref d) => dir = d,
} Ctr::Symbol(ref d) => dir = d,
} else { Ctr::Seg(ref s) => {
Err(start_trace( match eval(s, syms) {
("cd", "expected input to be a string") 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())) .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<RefCell<ShellState>>) { pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>>) {
@ -931,11 +983,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()
},
);
} }