diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d65891e..7eb6215 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,21 +1,6 @@ default: image: rust:latest -fmt: - stage: build - script: - - rustup component add rustfmt - - rustfmt --check tests/* - - rustfmt --check src/*.rs - - rustfmt --check src/stl/* - - rustfmt --check src/bin/* - -lint: - stage: build - script: - - cargo clippy - allow_failure: true - compile: stage: build script: diff --git a/Cargo.toml b/Cargo.toml index 2f93b35..6938440 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,6 @@ edition = "2018" [dependencies] dirs = "3.0" -rustyline = "8.2.0" +nu-ansi-term = "0.47.0" +reedline = "0.17.0" + diff --git a/Readme.org b/Readme.org index 9ea66b0..ff079da 100644 --- a/Readme.org +++ b/Readme.org @@ -387,7 +387,7 @@ Overload Load function to call a binary too **** TODO Foreground process TTY (fg function) **** TODO list jobs (j function) **** TODO ESSENTIAL: DOCUMENT POSIX MODULE -*** TODO Can enter multiple lines of text, with formatting in repl +*** TODO Configurable RPROMPT *** TODO Rename to Flesh *** TODO Create a dedicated community channel on matrix.sunnypup.io *** TODO Post to relevant channels diff --git a/src/bin/main.rs b/src/bin/main.rs index c91bb4f..e5a5a3e 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -14,18 +14,60 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +use nu_ansi_term::{Color, Style}; use dirs::home_dir; use relish::ast::{eval, lex, Ctr, Seg, SymTable}; use relish::aux::configure; use relish::stdlib::{dynamic_stdlib, static_stdlib}; -use rustyline::error::ReadlineError; -use rustyline::Editor; +use reedline::{ + FileBackedHistory, DefaultHinter, DefaultValidator, Reedline, Signal, + Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, +}; +use std::borrow::Cow; + + use std::env; -fn main() { - let mut rl = Editor::<()>::new(); +#[derive(Clone)] +pub struct CustomPrompt<'a>(&'a str); +impl Prompt for CustomPrompt<'_> { + fn render_prompt_left(&self) -> Cow { + { + Cow::Owned(self.0.to_string()) + } + } + fn render_prompt_right(&self) -> Cow { + { + Cow::Owned(format!(" <")) + } + } + + fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow { + Cow::Owned("> ".to_string()) + } + + fn render_prompt_multiline_indicator(&self) -> Cow { + Cow::Borrowed("++++") + } + + fn render_prompt_history_search_indicator( + &self, + history_search: PromptHistorySearch, + ) -> Cow { + let prefix = match history_search.status { + PromptHistorySearchStatus::Passing => "", + PromptHistorySearchStatus::Failing => "failing ", + }; + + Cow::Owned(format!( + "({}reverse-search: {}) ", + prefix, history_search.term + )) + } +} + +fn main() -> ! { const HIST_FILE: &str = "/.relish_hist"; const CONFIG_FILE_DEFAULT: &str = "/.relishrc"; @@ -33,11 +75,19 @@ fn main() { let hist_file_name = home_dir.clone() + HIST_FILE; let cfg_file_name = home_dir + CONFIG_FILE_DEFAULT; + let mut rl = Reedline::create(); + let maybe_hist: Box; if !hist_file_name.is_empty() { - rl.load_history(&hist_file_name) - .unwrap_or_else(|err: ReadlineError| eprintln!("{}", err)); + maybe_hist = Box::new(FileBackedHistory::with_file(5, hist_file_name.into()) + .expect("error reading history!")); + rl = rl.with_history(maybe_hist); } + rl = rl.with_hinter(Box::new( + DefaultHinter::default() + .with_style(Style::new().italic().fg(Color::LightGray)), + )).with_validator(Box::new(DefaultValidator)); + let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); dynamic_stdlib(&mut syms).unwrap_or_else(|err: String| eprintln!("{}", err)); @@ -56,12 +106,12 @@ fn main() { eprintln!("{}", err); Box::new(Ctr::String("".to_string())) }); - let readline_prompt = s.to_string(); + let p_str = s.to_string(); + let readline_prompt = CustomPrompt(p_str.as_str()); - let user_doc = rl.readline(&readline_prompt); + let user_doc = rl.read_line(&readline_prompt).unwrap(); match user_doc { - Ok(line) => { - rl.add_history_entry(line.as_str()); + Signal::Success(line) => { let l = line.as_str().to_owned(); match lex(&l) { @@ -71,17 +121,16 @@ fn main() { }, Err(s) => println!("{}", s), } - } + }, - Err(ReadlineError::Interrupted) => break, - Err(ReadlineError::Eof) => return, - Err(err) => { - eprintln!("Prompt error: {:?}", err); - break; - } + Signal::CtrlD => { + println!("EOF!"); + panic!(); + }, + + Signal::CtrlC => { + println!("Interrupted!"); + }, } } - if !hist_file_name.is_empty() { - rl.save_history(&hist_file_name).unwrap(); - } } diff --git a/src/config.rs b/src/config.rs index ee0e95e..c361c59 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,7 +24,7 @@ use std::io; use std::rc::Rc; fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result { - Ok(Ctr::Symbol("λ ".to_string())) + Ok(Ctr::Symbol("λ".to_string())) } /* loads defaults, evaluates config script */ @@ -80,8 +80,6 @@ default value ()" config_document = "(".to_string() + &config_document; let config_tree = lex(&config_document)?; - let config_result = eval(&config_tree, syms)?; - - println!("config result: {config_result}"); + eval(&config_tree, syms)?; Ok(()) } diff --git a/src/eval.rs b/src/eval.rs index 2714310..c88d888 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -23,7 +23,6 @@ use crate::sym::{SymTable, call_lambda}; * representing the simplest possible form of the input */ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result, String> { - println!("E: {}", ast); // data to return let mut ret = Box::from(Ctr::None); let mut first = true; diff --git a/src/stl/decl.rs b/src/stl/decl.rs index 3e9cf38..aa65df5 100644 --- a/src/stl/decl.rs +++ b/src/stl/decl.rs @@ -101,7 +101,6 @@ pub const STORE_DOCSTRING: &str = "allows user to define functions and variables (def useless-var)"; pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result { - println!("def: {}", ast); let is_var = ast.len() == 3; if let Ctr::Symbol(ref identifier) = *ast.car { match &*ast.cdr { diff --git a/src/sym.rs b/src/sym.rs index 45e4c8e..e8bf507 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -281,7 +281,6 @@ impl Symbol { evaluated_args = args; } - println!("args: {}", evaluated_args); self.args.validate_inputs(evaluated_args)?; match &self.value { ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())), @@ -362,18 +361,15 @@ impl Symbol { let value: ValueType; if let Some(ref arg_syms) = arg_list { - println!("def a func form"); value = ValueType::FuncForm(UserFn{ ast: Box::new(ast.clone()), arg_syms: arg_syms.clone(), }); args = Args::Lazy(arg_syms.len() as u128); } else if let Ctr::Lambda(ref l) = *ast.car { - println!("def a func form (lambda)"); args = Args::Lazy(l.arg_syms.len() as u128); value = ValueType::FuncForm(l.clone()); } else { - println!("def a var form"); args = Args::None; value = ValueType::VarForm(ast.car.clone()); }