shell fully configurable

This commit is contained in:
Ava Apples Affine 2023-03-23 11:52:36 -07:00
parent 1ce5fd3454
commit a711b32730
Signed by: affine
GPG key ID: 3A4645B8CF806069
4 changed files with 117 additions and 29 deletions

View file

@ -395,8 +395,12 @@ Errors during configuration are non-terminal. In such a case any defaults which
*** Configuration Values *** Configuration Values
- CFG_RELISH_POSIX (default false): when true, enables POSIX style job control. - CFG_RELISH_POSIX (default false): when true, enables POSIX style job control.
- CFG_RELISH_ENV (default true): when true, interpreter's variable table and environment variable table are kept in sync. - CFG_RELISH_ENV (default true): when true, interpreter's variable table and environment variable table are kept in sync.
- CFG_RELISH_PROMPT (default (echo "λ ")): A *function* definition which is called in order to output the prompt for each loop of the REPL. - CFG_RELISH_L_PROMPT (default 'λ'): a function that is called with no arguments to output the left hand of the prompt
This function will be reloaded each REPL loop and will be called by the interpreter with no arguments. - CFG_RELISH_R_PROMPT (default ''): a function that is called with no arguments to output the right hand of the prompt
- CFG_RELISH_PROMPT_DELIMITER (default '>'): a function that is called with no arguments to output the delimiter separating prompt from user input
*** Prompt design
For an example of prompt design see file:snippets/mood-prompt.rls
*** Further configuration *** Further configuration
Further configuration can be done by loading scripts that contain more functions and data to evaluate. Further configuration can be done by loading scripts that contain more functions and data to evaluate.
@ -477,12 +481,16 @@ Note: this section will not show the status of each item unless you are viewing
Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered. Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.
** TODO Pre-alpha tasks ** TODO Pre-alpha tasks
- Shell prompt is fully configurable (History, L, R, and Delim) - paths function
- add-path function
- Shell module - Shell module
- Only loadable via POSIX config var - Only loadable via POSIX config var
- Overload Load function to hook into this lib - Overload Load function to hook into this lib
- arg processor because these are control flow - arg processor because these are control flow
- Process launching with environment variables - Process launching with environment variables
- "flag" function
- "switch" function
- "with" function (load with)
- Optional form of process which allows fd redirecting - Optional form of process which allows fd redirecting
- Background processes (bg function) - Background processes (bg function)
- Foreground process TTY (fg function) - Foreground process TTY (fg function)
@ -492,6 +500,9 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt
- make const all the error messages - make const all the error messages
** TODO alpha tasks ** TODO alpha tasks
- History length configurable
- Search delim configurable
- Escape sequences in strings
- Rename to Flesh - Rename to Flesh
- master branch -> main branch - master branch -> main branch
- Create a dedicated community channel on matrix.sunnypup.io - Create a dedicated community channel on matrix.sunnypup.io

15
snippets/mood-prompt.rls Normal file
View file

@ -0,0 +1,15 @@
#!/bin/relish
(def __mood 'current mood' ':3')
(def CFG_RELISH_L_PROMPT 'mood left prompt'
() (concat "(" __mood ")" ))
(def CFG_RELISH_R_PROMPT 'mood right prompt'
() 'call set-mood to change prompt')
(def CFG_RELISH_PROMPT_DELIMITER 'mood prompt delim'
() '>')
(def set-mood 'set the little ascii face in your prompt'
(mood) (set (q __mood) mood))

View file

@ -28,22 +28,22 @@ use std::borrow::Cow;
use std::env; use std::env;
#[derive(Clone)] #[derive(Clone)]
pub struct CustomPrompt<'a>(&'a str); pub struct CustomPrompt(String, String, String);
impl Prompt for CustomPrompt<'_> { impl Prompt for CustomPrompt {
fn render_prompt_left(&self) -> Cow<str> { fn render_prompt_left(&self) -> Cow<str> {
{ {
Cow::Owned(self.0.to_string()) Cow::Owned(self.0.to_owned())
} }
} }
fn render_prompt_right(&self) -> Cow<str> { fn render_prompt_right(&self) -> Cow<str> {
{ {
Cow::Owned(format!("")) Cow::Owned(self.1.to_owned())
} }
} }
fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow<str> { fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow<str> {
Cow::Owned("> ".to_string()) Cow::Owned(self.2.to_owned())
} }
fn render_prompt_multiline_indicator(&self) -> Cow<str> { fn render_prompt_multiline_indicator(&self) -> Cow<str> {
@ -117,20 +117,7 @@ fn main() {
// repl :) // repl :)
loop { loop {
let s = *syms let readline_prompt = make_prompt(&mut syms);
.call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true)
.unwrap_or_else(|err: String| {
eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string()))
});
let p_str: String;
if let Ctr::String(s) = s {
p_str = s;
} else {
p_str = s.to_string();
}
let readline_prompt = CustomPrompt(p_str.as_str());
let user_doc = rl.read_line(&readline_prompt).unwrap(); let user_doc = rl.read_line(&readline_prompt).unwrap();
match user_doc { match user_doc {
Signal::Success(line) => { Signal::Success(line) => {
@ -156,3 +143,45 @@ fn main() {
} }
} }
} }
fn make_prompt(syms: &mut SymTable) -> CustomPrompt {
let l_ctr = *syms
.call_symbol(&"CFG_RELISH_L_PROMPT".to_string(), &Seg::new(), true)
.unwrap_or_else(|err: String| {
eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string()))
});
let r_ctr = *syms
.call_symbol(&"CFG_RELISH_R_PROMPT".to_string(), &Seg::new(), true)
.unwrap_or_else(|err: String| {
eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string()))
});
let d_ctr = *syms
.call_symbol(&"CFG_RELISH_PROMPT_DELIMITER".to_string(), &Seg::new(), true)
.unwrap_or_else(|err: String| {
eprintln!("{}", err);
Box::new(Ctr::String("<prompt broken!>".to_string()))
});
let l_str: String;
let r_str: String;
let d_str: String;
if let Ctr::String(s) = l_ctr {
l_str = s;
} else {
l_str = l_ctr.to_string();
}
if let Ctr::String(s) = r_ctr {
r_str = s;
} else {
r_str = r_ctr.to_string();
}
if let Ctr::String(s) = d_ctr {
d_str = s;
} else {
d_str = d_ctr.to_string();
}
CustomPrompt(l_str, r_str, d_str)
}

View file

@ -25,8 +25,16 @@ use std::iter::FromIterator;
use std::env::{vars, var, current_dir}; use std::env::{vars, var, current_dir};
use std::rc::Rc; use std::rc::Rc;
fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> { fn l_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::Symbol("λ".to_string())) Ok(Ctr::String("λ".to_string()))
}
fn r_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::String(String::new()))
}
fn prompt_delimiter_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::String(">".to_string()))
} }
fn get_paths() -> Vec<String> { fn get_paths() -> Vec<String> {
@ -70,15 +78,40 @@ default value: 1 (set)
); );
syms.insert( syms.insert(
"CFG_RELISH_PROMPT".to_string(), "CFG_RELISH_L_PROMPT".to_string(),
Symbol { Symbol {
name: String::from("default relish prompt"), name: String::from("default relish left prompt"),
args: Args::None, args: Args::None,
conditional_branches: false, conditional_branches: false,
docs: "function called to output prompt. this function is called with no arguments. docs: "function called to output prompt on left hand. this function is called with no arguments."
default value (<lambda>)"
.to_string(), .to_string(),
value: ValueType::Internal(Rc::new(prompt_default_callback)), value: ValueType::Internal(Rc::new(l_prompt_default_callback)),
..Default::default()
},
);
syms.insert(
"CFG_RELISH_R_PROMPT".to_string(),
Symbol {
name: String::from("default relish right prompt"),
args: Args::None,
conditional_branches: false,
docs: "function called to output prompt on right hand. this function is called with no arguments."
.to_string(),
value: ValueType::Internal(Rc::new(r_prompt_default_callback)),
..Default::default()
},
);
syms.insert(
"CFG_RELISH_PROMPT_DELIMITER".to_string(),
Symbol {
name: String::from("default relish prompt delimiter"),
args: Args::None,
conditional_branches: false,
docs: "function called to output prompt delimiter. this function is called with no arguments."
.to_string(),
value: ValueType::Internal(Rc::new(prompt_delimiter_default_callback)),
..Default::default() ..Default::default()
}, },
); );