From 5b35f0391299c11fd8e8aad66efd91652bcfb43a Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 00:05:26 -0400 Subject: [PATCH 01/34] initial --- src/cli/helper.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++ src/cli/main.rs | 1 + 2 files changed, 112 insertions(+) create mode 100644 src/cli/helper.rs diff --git a/src/cli/helper.rs b/src/cli/helper.rs new file mode 100644 index 0000000..6fc22f0 --- /dev/null +++ b/src/cli/helper.rs @@ -0,0 +1,111 @@ +#![allow(clippy::module_name_repetitions)] + +use std::cell::RefCell; + +use rustmatheval::model::{functions::Function, variables::Variable}; + +use rustyline::{Helper, completion::{Candidate, Completer}, highlight::Highlighter, hint::{Hint, Hinter}, validate::Validator}; + +#[allow(dead_code)] +pub struct MyHelper<'cell> { + funcs: &'cell RefCell>, + vars: &'cell RefCell> +} + +pub struct MyCandidate; + +impl Candidate for MyCandidate { + fn display(&self) -> &str { + todo!() + } + + fn replacement(&self) -> &str { + todo!() + } +} + +impl Completer for MyHelper<'_> { + type Candidate = MyCandidate; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &rustyline::Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + let _ = (line, pos, ctx); + Ok((0, Vec::with_capacity(0))) + } + + fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { + let end = line.pos(); + line.replace(start..end, elected); + } +} + +pub struct MyHint; + +impl Hint for MyHint { + fn display(&self) -> &str { + todo!() + } + + fn completion(&self) -> Option<&str> { + todo!() + } +} + +impl Hinter for MyHelper<'_> { + type Hint = MyHint; + + fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { + let _ = (line, pos, ctx); + None + } +} + +impl Highlighter for MyHelper<'_> { + fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { + let _ = pos; + std::borrow::Cow::Borrowed(line) + } + + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> std::borrow::Cow<'b, str> { + let _ = default; + std::borrow::Cow::Borrowed(prompt) + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { + std::borrow::Cow::Borrowed(hint) + } + + fn highlight_candidate<'c>( + &self, + candidate: &'c str, + completion: rustyline::CompletionType, + ) -> std::borrow::Cow<'c, str> { + let _ = completion; + std::borrow::Cow::Borrowed(candidate) + } + + fn highlight_char(&self, _line: &str, _pos: usize) -> bool { + true + } +} + +impl Validator for MyHelper<'_> { + fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result { + let _ = ctx; + Ok(rustyline::validate::ValidationResult::Valid(None)) + } + + fn validate_while_typing(&self) -> bool { + false + } +} + +impl Helper for MyHelper<'_> {} \ No newline at end of file diff --git a/src/cli/main.rs b/src/cli/main.rs index 8368ca1..5f1ba75 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -8,6 +8,7 @@ mod rcfile; mod stringify; mod utils; mod vars; +mod helper; use lib::{doeval, model::EvaluationContext}; pub use rustmatheval as lib; From 062e7366f23e43a36b64538ec5681c2fac22726c Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:16:01 -0400 Subject: [PATCH 02/34] a little cleaner --- src/cli/helper.rs | 113 +++++++++++++++++++++++++++++++++------------- src/cli/main.rs | 23 +++++++--- src/cli/utils.rs | 13 ++++++ 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/src/cli/helper.rs b/src/cli/helper.rs index 6fc22f0..781030c 100644 --- a/src/cli/helper.rs +++ b/src/cli/helper.rs @@ -1,26 +1,41 @@ #![allow(clippy::module_name_repetitions)] -use std::cell::RefCell; +use std::{borrow::Cow, cell::RefCell}; -use rustmatheval::model::{functions::Function, variables::Variable}; +use colored::Colorize; +use rustmatheval::{model::{EvaluationContext, functions::Function, variables::Variable}, tokenize}; -use rustyline::{Helper, completion::{Candidate, Completer}, highlight::Highlighter, hint::{Hint, Hinter}, validate::Validator}; +use rustyline::{Helper, completion::{Candidate, Completer}, highlight::Highlighter, hint::{Hint, Hinter}, validate::{ValidationResult, Validator}}; + +use crate::utils::find_last; #[allow(dead_code)] pub struct MyHelper<'cell> { - funcs: &'cell RefCell>, - vars: &'cell RefCell> + pub funcs: &'cell RefCell>, + pub vars: &'cell RefCell>, + + pub valid: RefCell } -pub struct MyCandidate; +pub struct MyCandidate(String); impl Candidate for MyCandidate { fn display(&self) -> &str { - todo!() + &self.0 } fn replacement(&self) -> &str { - todo!() + &self.0 + } +} + +impl Hint for MyCandidate { + fn display(&self) -> &str { + &self.0 + } + + fn completion(&self) -> Option<&str> { + Some(&self.0) } } @@ -33,8 +48,24 @@ impl Completer for MyHelper<'_> { pos: usize, ctx: &rustyline::Context<'_>, ) -> rustyline::Result<(usize, Vec)> { - let _ = (line, pos, ctx); - Ok((0, Vec::with_capacity(0))) + if let Some(npos) = find_last('#', &line[..pos]) { + let line = &line[npos + 1..pos]; + let funcs = self.funcs.borrow(); + + if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { + return rustyline::Result::Ok((pos-npos, vec![MyCandidate(func.name[pos-npos-1..].to_string())])); + } + } else if let Some(npos) = find_last('$', &line[..pos]) { + let line = &line[npos + 1..pos]; + let vars = self.vars.borrow(); + + if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { + // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); + return rustyline::Result::Ok((pos-npos, vec![MyCandidate(var.repr[pos-npos-1..].to_string())])); + } + } + + rustyline::Result::Ok((0, vec![])) } fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { @@ -43,31 +74,33 @@ impl Completer for MyHelper<'_> { } } -pub struct MyHint; +impl Hinter for MyHelper<'_> { + type Hint = MyCandidate; -impl Hint for MyHint { - fn display(&self) -> &str { - todo!() - } + fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { + if let Some(npos) = find_last('#', &line[..pos]) { + let line = &line[npos + 1..pos]; + let funcs = self.funcs.borrow(); - fn completion(&self) -> Option<&str> { - todo!() - } -} + if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { + return Some(MyCandidate(func.name[pos-npos-1..].to_string())); + } + } else if let Some(npos) = find_last('$', &line[..pos]) { + let line = &line[npos + 1..pos]; + let vars = self.vars.borrow(); -impl Hinter for MyHelper<'_> { - type Hint = MyHint; + if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { + return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); + } + } - fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { - let _ = (line, pos, ctx); None } } impl Highlighter for MyHelper<'_> { fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { - let _ = pos; - std::borrow::Cow::Borrowed(line) + Cow::Borrowed(line) } fn highlight_prompt<'b, 's: 'b, 'p: 'b>( @@ -75,12 +108,16 @@ impl Highlighter for MyHelper<'_> { prompt: &'p str, default: bool, ) -> std::borrow::Cow<'b, str> { - let _ = default; - std::borrow::Cow::Borrowed(prompt) + let valid = *self.valid.borrow(); + if valid { + Cow::Borrowed(prompt) + } else { + Cow::Owned(prompt.red().to_string()) + } } fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { - std::borrow::Cow::Borrowed(hint) + Cow::Owned(hint.black().on_white().to_string()) } fn highlight_candidate<'c>( @@ -88,8 +125,8 @@ impl Highlighter for MyHelper<'_> { candidate: &'c str, completion: rustyline::CompletionType, ) -> std::borrow::Cow<'c, str> { - let _ = completion; - std::borrow::Cow::Borrowed(candidate) + let form = candidate.red(); + Cow::Owned(form.to_string()) } fn highlight_char(&self, _line: &str, _pos: usize) -> bool { @@ -98,12 +135,24 @@ impl Highlighter for MyHelper<'_> { } impl Validator for MyHelper<'_> { - fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result { - let _ = ctx; + fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result { + let line = ctx.input(); + let context = EvaluationContext::default(); + + // dbg!(line); + + let (valid_, result) = if tokenize(line, &context).is_err() { + (false, ValidationResult::Incomplete) + } else { + (true, ValidationResult::Valid(None)) + }; + *self.valid.borrow_mut() = valid_; + rustyline::Result::Ok(result); Ok(rustyline::validate::ValidationResult::Valid(None)) } fn validate_while_typing(&self) -> bool { + // true false } } diff --git a/src/cli/main.rs b/src/cli/main.rs index 5f1ba75..fde2a52 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -17,11 +17,11 @@ use config::HISTORY_FILE; use rustyline::Editor; use error::Error; -use std::{env, process}; +use std::{cell::RefCell, env, process}; use cli::{handle_errors, handle_input}; -use crate::cli::handle_library_errors; +use crate::{cli::handle_library_errors, helper::MyHelper}; pub fn main() -> ! { // One-shot mode @@ -52,10 +52,10 @@ pub fn main() -> ! { process::exit(code); } - let mut vars = vec![]; - let mut funcs = vec![]; + let vars = RefCell::new(vec![]); + let funcs = RefCell::new(vec![]); - if let Err(inner) = rcfile::load(&mut vars, &mut funcs) { + if let Err(inner) = rcfile::load(&mut vars.borrow_mut(), &mut funcs.borrow_mut()) { match inner { Error::Io(inner) => { println!("Error loading RCFile: {:#?}", inner); @@ -64,7 +64,16 @@ pub fn main() -> ! { } }; - let mut editor = Editor::<()>::new(); + let mut editor = Editor::::new(); + + let helper = MyHelper { + funcs: &funcs, + vars: &vars, + + valid: RefCell::new(false) + }; + + editor.set_helper(Some(helper)); if let Some(path) = HISTORY_FILE.as_deref() { editor.load_history(path).ok(); @@ -89,7 +98,7 @@ pub fn main() -> ! { // Add the line to the history editor.add_history_entry(&input); - match handle_input(&input, &mut vars, &mut funcs) { + match handle_input(&input, &mut vars.borrow_mut(), &mut funcs.borrow_mut()) { Ok(formatted) => println!("{}", formatted), Err(error) => { let msg = handle_errors(&error, &input); diff --git a/src/cli/utils.rs b/src/cli/utils.rs index 3eb0ebe..ef4dbc1 100644 --- a/src/cli/utils.rs +++ b/src/cli/utils.rs @@ -14,3 +14,16 @@ where } } } + +/// Find the position of the last instance of `c` +/// +/// ## Examples +/// +/// ``` +/// let s = "abc #foo bar"; +/// let pos = find_last('#', s).unwrap(); +/// assert_eq!(s.chars().nth(pos), '#'); +/// ``` +pub fn find_last(c: char, str: &str) -> Option { + str.chars().into_iter().rev().position(|ch| ch == c).map(|pos| str.chars().count() - pos - 1) +} From 8446075e41030dfed8e06703db04fc77f09e9336 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:16:23 -0400 Subject: [PATCH 03/34] lint --- src/cli/helper.rs | 38 ++++++++++++++++++++++++++++---------- src/cli/main.rs | 4 ++-- src/cli/utils.rs | 6 +++++- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/cli/helper.rs b/src/cli/helper.rs index 781030c..be43925 100644 --- a/src/cli/helper.rs +++ b/src/cli/helper.rs @@ -3,9 +3,18 @@ use std::{borrow::Cow, cell::RefCell}; use colored::Colorize; -use rustmatheval::{model::{EvaluationContext, functions::Function, variables::Variable}, tokenize}; - -use rustyline::{Helper, completion::{Candidate, Completer}, highlight::Highlighter, hint::{Hint, Hinter}, validate::{ValidationResult, Validator}}; +use rustmatheval::{ + model::{functions::Function, variables::Variable, EvaluationContext}, + tokenize, +}; + +use rustyline::{ + completion::{Candidate, Completer}, + highlight::Highlighter, + hint::{Hint, Hinter}, + validate::{ValidationResult, Validator}, + Helper, +}; use crate::utils::find_last; @@ -14,7 +23,7 @@ pub struct MyHelper<'cell> { pub funcs: &'cell RefCell>, pub vars: &'cell RefCell>, - pub valid: RefCell + pub valid: RefCell, } pub struct MyCandidate(String); @@ -53,7 +62,10 @@ impl Completer for MyHelper<'_> { let funcs = self.funcs.borrow(); if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - return rustyline::Result::Ok((pos-npos, vec![MyCandidate(func.name[pos-npos-1..].to_string())])); + return rustyline::Result::Ok(( + pos - npos, + vec![MyCandidate(func.name[pos - npos - 1..].to_string())], + )); } } else if let Some(npos) = find_last('$', &line[..pos]) { let line = &line[npos + 1..pos]; @@ -61,7 +73,10 @@ impl Completer for MyHelper<'_> { if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); - return rustyline::Result::Ok((pos-npos, vec![MyCandidate(var.repr[pos-npos-1..].to_string())])); + return rustyline::Result::Ok(( + pos - npos, + vec![MyCandidate(var.repr[pos - npos - 1..].to_string())], + )); } } @@ -83,14 +98,14 @@ impl Hinter for MyHelper<'_> { let funcs = self.funcs.borrow(); if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - return Some(MyCandidate(func.name[pos-npos-1..].to_string())); + return Some(MyCandidate(func.name[pos - npos - 1..].to_string())); } } else if let Some(npos) = find_last('$', &line[..pos]) { let line = &line[npos + 1..pos]; let vars = self.vars.borrow(); if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { - return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); + return Some(MyCandidate(var.repr[pos - npos - 1..].to_string())); } } @@ -135,7 +150,10 @@ impl Highlighter for MyHelper<'_> { } impl Validator for MyHelper<'_> { - fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result { + fn validate( + &self, + ctx: &mut rustyline::validate::ValidationContext, + ) -> rustyline::Result { let line = ctx.input(); let context = EvaluationContext::default(); @@ -157,4 +175,4 @@ impl Validator for MyHelper<'_> { } } -impl Helper for MyHelper<'_> {} \ No newline at end of file +impl Helper for MyHelper<'_> {} diff --git a/src/cli/main.rs b/src/cli/main.rs index fde2a52..f6a611e 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -4,11 +4,11 @@ mod cli; mod config; mod error; mod funcs; +mod helper; mod rcfile; mod stringify; mod utils; mod vars; -mod helper; use lib::{doeval, model::EvaluationContext}; pub use rustmatheval as lib; @@ -70,7 +70,7 @@ pub fn main() -> ! { funcs: &funcs, vars: &vars, - valid: RefCell::new(false) + valid: RefCell::new(false), }; editor.set_helper(Some(helper)); diff --git a/src/cli/utils.rs b/src/cli/utils.rs index ef4dbc1..1baf268 100644 --- a/src/cli/utils.rs +++ b/src/cli/utils.rs @@ -25,5 +25,9 @@ where /// assert_eq!(s.chars().nth(pos), '#'); /// ``` pub fn find_last(c: char, str: &str) -> Option { - str.chars().into_iter().rev().position(|ch| ch == c).map(|pos| str.chars().count() - pos - 1) + str.chars() + .into_iter() + .rev() + .position(|ch| ch == c) + .map(|pos| str.chars().count() - pos - 1) } From a0992c7ba930f656f844a98e729d14e3c91334e2 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:27:54 -0400 Subject: [PATCH 04/34] refactor --- src/cli/editor/candidate.rs | 0 src/cli/editor/completer.rs | 46 ++++++++++ src/cli/editor/highlighter.rs | 42 +++++++++ src/cli/editor/hinter.rs | 29 +++++++ src/cli/editor/mod.rs | 60 +++++++++++++ src/cli/editor/validator.rs | 30 +++++++ src/cli/helper.rs | 158 ---------------------------------- src/cli/main.rs | 14 +-- 8 files changed, 210 insertions(+), 169 deletions(-) create mode 100644 src/cli/editor/candidate.rs create mode 100644 src/cli/editor/completer.rs create mode 100644 src/cli/editor/highlighter.rs create mode 100644 src/cli/editor/hinter.rs create mode 100644 src/cli/editor/mod.rs create mode 100644 src/cli/editor/validator.rs diff --git a/src/cli/editor/candidate.rs b/src/cli/editor/candidate.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs new file mode 100644 index 0000000..984c3de --- /dev/null +++ b/src/cli/editor/completer.rs @@ -0,0 +1,46 @@ +use rustyline::completion::Completer; + +use crate::utils::find_last; + +use super::{MyCandidate, MyHelper}; + +impl Completer for MyHelper<'_> { + type Candidate = MyCandidate; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &rustyline::Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + if let Some(npos) = find_last('#', &line[..pos]) { + let line = &line[npos + 1..pos]; + let funcs = self.funcs.borrow(); + + if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { + return rustyline::Result::Ok(( + pos - npos, + vec![MyCandidate(func.name[pos - npos - 1..].to_string())], + )); + } + } else if let Some(npos) = find_last('$', &line[..pos]) { + let line = &line[npos + 1..pos]; + let vars = self.vars.borrow(); + + if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { + // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); + return rustyline::Result::Ok(( + pos - npos, + vec![MyCandidate(var.repr[pos - npos - 1..].to_string())], + )); + } + } + + rustyline::Result::Ok((0, vec![])) + } + + fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { + let end = line.pos(); + line.replace(start..end, elected); + } +} \ No newline at end of file diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs new file mode 100644 index 0000000..01f29db --- /dev/null +++ b/src/cli/editor/highlighter.rs @@ -0,0 +1,42 @@ +use std::borrow::Cow; + +use colored::Colorize; +use rustyline::highlight::Highlighter; + +use super::MyHelper; + +impl Highlighter for MyHelper<'_> { + fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { + Cow::Borrowed(line) + } + + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> std::borrow::Cow<'b, str> { + let valid = *self.valid.borrow(); + if valid { + Cow::Borrowed(prompt) + } else { + Cow::Owned(prompt.red().to_string()) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { + Cow::Owned(hint.black().on_white().to_string()) + } + + fn highlight_candidate<'c>( + &self, + candidate: &'c str, + completion: rustyline::CompletionType, + ) -> std::borrow::Cow<'c, str> { + let form = candidate.red(); + Cow::Owned(form.to_string()) + } + + fn highlight_char(&self, _line: &str, _pos: usize) -> bool { + true + } +} \ No newline at end of file diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs new file mode 100644 index 0000000..3fbb1cd --- /dev/null +++ b/src/cli/editor/hinter.rs @@ -0,0 +1,29 @@ +use rustyline::hint::Hinter; + +use crate::utils::find_last; + +use super::{MyCandidate, MyHelper}; + +impl Hinter for MyHelper<'_> { + type Hint = MyCandidate; + + fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { + if let Some(npos) = find_last('#', &line[..pos]) { + let line = &line[npos + 1..pos]; + let funcs = self.funcs.borrow(); + + if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { + return Some(MyCandidate(func.name[pos - npos - 1..].to_string())); + } + } else if let Some(npos) = find_last('$', &line[..pos]) { + let line = &line[npos + 1..pos]; + let vars = self.vars.borrow(); + + if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { + return Some(MyCandidate(var.repr[pos - npos - 1..].to_string())); + } + } + + None + } +} diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs new file mode 100644 index 0000000..606e82e --- /dev/null +++ b/src/cli/editor/mod.rs @@ -0,0 +1,60 @@ +use std::cell::RefCell; + +use rustmatheval::model::{functions::Function, variables::Variable}; +use rustyline::{Editor, Helper, completion::Candidate, hint::Hint}; + +use super::config::HISTORY_FILE; + +mod candidate; + +mod hinter; +mod completer; +mod highlighter; +mod validator; + +pub fn editor<'a>(funcs: &'a RefCell>, vars: &'a RefCell>) -> Editor> { + let mut editor = Editor::::new(); + + let helper = MyHelper { + funcs, + vars, + + valid: RefCell::new(false), + }; + + editor.set_helper(Some(helper)); + + editor + +} + +pub struct MyHelper<'cell> { + pub funcs: &'cell RefCell>, + pub vars: &'cell RefCell>, + + pub valid: RefCell, +} + +impl Helper for MyHelper<'_> {} + +pub struct MyCandidate(String); + +impl Candidate for MyCandidate { + fn display(&self) -> &str { + &self.0 + } + + fn replacement(&self) -> &str { + &self.0 + } +} + +impl Hint for MyCandidate { + fn display(&self) -> &str { + &self.0 + } + + fn completion(&self) -> Option<&str> { + Some(&self.0) + } +} \ No newline at end of file diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs new file mode 100644 index 0000000..c7dda88 --- /dev/null +++ b/src/cli/editor/validator.rs @@ -0,0 +1,30 @@ +use rustmatheval::{model::EvaluationContext, tokenize}; +use rustyline::validate::{ValidationResult, Validator}; + +use super::MyHelper; + +impl Validator for MyHelper<'_> { + fn validate( + &self, + ctx: &mut rustyline::validate::ValidationContext, + ) -> rustyline::Result { + let line = ctx.input(); + let context = EvaluationContext::default(); + + // dbg!(line); + + let (valid_, result) = if tokenize(line, &context).is_err() { + (false, ValidationResult::Incomplete) + } else { + (true, ValidationResult::Valid(None)) + }; + *self.valid.borrow_mut() = valid_; + rustyline::Result::Ok(result); + Ok(rustyline::validate::ValidationResult::Valid(None)) + } + + fn validate_while_typing(&self) -> bool { + // true + false + } +} \ No newline at end of file diff --git a/src/cli/helper.rs b/src/cli/helper.rs index be43925..6c4898a 100644 --- a/src/cli/helper.rs +++ b/src/cli/helper.rs @@ -18,161 +18,3 @@ use rustyline::{ use crate::utils::find_last; -#[allow(dead_code)] -pub struct MyHelper<'cell> { - pub funcs: &'cell RefCell>, - pub vars: &'cell RefCell>, - - pub valid: RefCell, -} - -pub struct MyCandidate(String); - -impl Candidate for MyCandidate { - fn display(&self) -> &str { - &self.0 - } - - fn replacement(&self) -> &str { - &self.0 - } -} - -impl Hint for MyCandidate { - fn display(&self) -> &str { - &self.0 - } - - fn completion(&self) -> Option<&str> { - Some(&self.0) - } -} - -impl Completer for MyHelper<'_> { - type Candidate = MyCandidate; - - fn complete( - &self, - line: &str, - pos: usize, - ctx: &rustyline::Context<'_>, - ) -> rustyline::Result<(usize, Vec)> { - if let Some(npos) = find_last('#', &line[..pos]) { - let line = &line[npos + 1..pos]; - let funcs = self.funcs.borrow(); - - if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - return rustyline::Result::Ok(( - pos - npos, - vec![MyCandidate(func.name[pos - npos - 1..].to_string())], - )); - } - } else if let Some(npos) = find_last('$', &line[..pos]) { - let line = &line[npos + 1..pos]; - let vars = self.vars.borrow(); - - if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { - // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); - return rustyline::Result::Ok(( - pos - npos, - vec![MyCandidate(var.repr[pos - npos - 1..].to_string())], - )); - } - } - - rustyline::Result::Ok((0, vec![])) - } - - fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { - let end = line.pos(); - line.replace(start..end, elected); - } -} - -impl Hinter for MyHelper<'_> { - type Hint = MyCandidate; - - fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { - if let Some(npos) = find_last('#', &line[..pos]) { - let line = &line[npos + 1..pos]; - let funcs = self.funcs.borrow(); - - if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - return Some(MyCandidate(func.name[pos - npos - 1..].to_string())); - } - } else if let Some(npos) = find_last('$', &line[..pos]) { - let line = &line[npos + 1..pos]; - let vars = self.vars.borrow(); - - if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { - return Some(MyCandidate(var.repr[pos - npos - 1..].to_string())); - } - } - - None - } -} - -impl Highlighter for MyHelper<'_> { - fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { - Cow::Borrowed(line) - } - - fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, - prompt: &'p str, - default: bool, - ) -> std::borrow::Cow<'b, str> { - let valid = *self.valid.borrow(); - if valid { - Cow::Borrowed(prompt) - } else { - Cow::Owned(prompt.red().to_string()) - } - } - - fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { - Cow::Owned(hint.black().on_white().to_string()) - } - - fn highlight_candidate<'c>( - &self, - candidate: &'c str, - completion: rustyline::CompletionType, - ) -> std::borrow::Cow<'c, str> { - let form = candidate.red(); - Cow::Owned(form.to_string()) - } - - fn highlight_char(&self, _line: &str, _pos: usize) -> bool { - true - } -} - -impl Validator for MyHelper<'_> { - fn validate( - &self, - ctx: &mut rustyline::validate::ValidationContext, - ) -> rustyline::Result { - let line = ctx.input(); - let context = EvaluationContext::default(); - - // dbg!(line); - - let (valid_, result) = if tokenize(line, &context).is_err() { - (false, ValidationResult::Incomplete) - } else { - (true, ValidationResult::Valid(None)) - }; - *self.valid.borrow_mut() = valid_; - rustyline::Result::Ok(result); - Ok(rustyline::validate::ValidationResult::Valid(None)) - } - - fn validate_while_typing(&self) -> bool { - // true - false - } -} - -impl Helper for MyHelper<'_> {} diff --git a/src/cli/main.rs b/src/cli/main.rs index f6a611e..aff39f2 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -9,6 +9,7 @@ mod rcfile; mod stringify; mod utils; mod vars; +mod editor; use lib::{doeval, model::EvaluationContext}; pub use rustmatheval as lib; @@ -21,7 +22,7 @@ use std::{cell::RefCell, env, process}; use cli::{handle_errors, handle_input}; -use crate::{cli::handle_library_errors, helper::MyHelper}; +use crate::{cli::handle_library_errors, editor::editor}; pub fn main() -> ! { // One-shot mode @@ -64,16 +65,7 @@ pub fn main() -> ! { } }; - let mut editor = Editor::::new(); - - let helper = MyHelper { - funcs: &funcs, - vars: &vars, - - valid: RefCell::new(false), - }; - - editor.set_helper(Some(helper)); + let mut editor = editor(&funcs, &vars); if let Some(path) = HISTORY_FILE.as_deref() { editor.load_history(path).ok(); From 1fa5725577fd9740a435e56a07d2775d091166b6 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:28:04 -0400 Subject: [PATCH 05/34] lint --- src/cli/editor/candidate.rs | 1 + src/cli/editor/completer.rs | 2 +- src/cli/editor/highlighter.rs | 2 +- src/cli/editor/mod.rs | 12 +++++++----- src/cli/editor/validator.rs | 2 +- src/cli/helper.rs | 1 - src/cli/main.rs | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/cli/editor/candidate.rs b/src/cli/editor/candidate.rs index e69de29..8b13789 100644 --- a/src/cli/editor/candidate.rs +++ b/src/cli/editor/candidate.rs @@ -0,0 +1 @@ + diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 984c3de..8fe3bd8 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -43,4 +43,4 @@ impl Completer for MyHelper<'_> { let end = line.pos(); line.replace(start..end, elected); } -} \ No newline at end of file +} diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs index 01f29db..97dbd75 100644 --- a/src/cli/editor/highlighter.rs +++ b/src/cli/editor/highlighter.rs @@ -39,4 +39,4 @@ impl Highlighter for MyHelper<'_> { fn highlight_char(&self, _line: &str, _pos: usize) -> bool { true } -} \ No newline at end of file +} diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index 606e82e..c1fd464 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -1,18 +1,21 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; -use rustyline::{Editor, Helper, completion::Candidate, hint::Hint}; +use rustyline::{completion::Candidate, hint::Hint, Editor, Helper}; use super::config::HISTORY_FILE; mod candidate; -mod hinter; mod completer; mod highlighter; +mod hinter; mod validator; -pub fn editor<'a>(funcs: &'a RefCell>, vars: &'a RefCell>) -> Editor> { +pub fn editor<'a>( + funcs: &'a RefCell>, + vars: &'a RefCell>, +) -> Editor> { let mut editor = Editor::::new(); let helper = MyHelper { @@ -25,7 +28,6 @@ pub fn editor<'a>(funcs: &'a RefCell>, vars: &'a RefCell { @@ -57,4 +59,4 @@ impl Hint for MyCandidate { fn completion(&self) -> Option<&str> { Some(&self.0) } -} \ No newline at end of file +} diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs index c7dda88..16183ae 100644 --- a/src/cli/editor/validator.rs +++ b/src/cli/editor/validator.rs @@ -27,4 +27,4 @@ impl Validator for MyHelper<'_> { // true false } -} \ No newline at end of file +} diff --git a/src/cli/helper.rs b/src/cli/helper.rs index 6c4898a..abc4147 100644 --- a/src/cli/helper.rs +++ b/src/cli/helper.rs @@ -17,4 +17,3 @@ use rustyline::{ }; use crate::utils::find_last; - diff --git a/src/cli/main.rs b/src/cli/main.rs index aff39f2..a936925 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -2,6 +2,7 @@ mod cli; mod config; +mod editor; mod error; mod funcs; mod helper; @@ -9,7 +10,6 @@ mod rcfile; mod stringify; mod utils; mod vars; -mod editor; use lib::{doeval, model::EvaluationContext}; pub use rustmatheval as lib; From b554d85ad9343fe1f479aab2b8fc199936e9dd04 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:28:20 -0400 Subject: [PATCH 06/34] delete file --- src/cli/helper.rs | 19 ------------------- src/cli/main.rs | 1 - 2 files changed, 20 deletions(-) delete mode 100644 src/cli/helper.rs diff --git a/src/cli/helper.rs b/src/cli/helper.rs deleted file mode 100644 index abc4147..0000000 --- a/src/cli/helper.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![allow(clippy::module_name_repetitions)] - -use std::{borrow::Cow, cell::RefCell}; - -use colored::Colorize; -use rustmatheval::{ - model::{functions::Function, variables::Variable, EvaluationContext}, - tokenize, -}; - -use rustyline::{ - completion::{Candidate, Completer}, - highlight::Highlighter, - hint::{Hint, Hinter}, - validate::{ValidationResult, Validator}, - Helper, -}; - -use crate::utils::find_last; diff --git a/src/cli/main.rs b/src/cli/main.rs index a936925..0385cab 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -5,7 +5,6 @@ mod config; mod editor; mod error; mod funcs; -mod helper; mod rcfile; mod stringify; mod utils; From fafaa59a39fde6effdec15a1364211337773d378 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:28:30 -0400 Subject: [PATCH 07/34] cargo fix --- src/cli/editor/completer.rs | 2 +- src/cli/editor/highlighter.rs | 6 +++--- src/cli/editor/mod.rs | 2 +- src/cli/main.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 8fe3bd8..3085b4e 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -11,7 +11,7 @@ impl Completer for MyHelper<'_> { &self, line: &str, pos: usize, - ctx: &rustyline::Context<'_>, + _ctx: &rustyline::Context<'_>, ) -> rustyline::Result<(usize, Vec)> { if let Some(npos) = find_last('#', &line[..pos]) { let line = &line[npos + 1..pos]; diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs index 97dbd75..5e793e2 100644 --- a/src/cli/editor/highlighter.rs +++ b/src/cli/editor/highlighter.rs @@ -6,14 +6,14 @@ use rustyline::highlight::Highlighter; use super::MyHelper; impl Highlighter for MyHelper<'_> { - fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { + fn highlight<'l>(&self, line: &'l str, _pos: usize) -> std::borrow::Cow<'l, str> { Cow::Borrowed(line) } fn highlight_prompt<'b, 's: 'b, 'p: 'b>( &'s self, prompt: &'p str, - default: bool, + _default: bool, ) -> std::borrow::Cow<'b, str> { let valid = *self.valid.borrow(); if valid { @@ -30,7 +30,7 @@ impl Highlighter for MyHelper<'_> { fn highlight_candidate<'c>( &self, candidate: &'c str, - completion: rustyline::CompletionType, + _completion: rustyline::CompletionType, ) -> std::borrow::Cow<'c, str> { let form = candidate.red(); Cow::Owned(form.to_string()) diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index c1fd464..035c18c 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; use rustyline::{completion::Candidate, hint::Hint, Editor, Helper}; -use super::config::HISTORY_FILE; + mod candidate; diff --git a/src/cli/main.rs b/src/cli/main.rs index 0385cab..ce98069 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -14,7 +14,7 @@ use lib::{doeval, model::EvaluationContext}; pub use rustmatheval as lib; use config::HISTORY_FILE; -use rustyline::Editor; + use error::Error; use std::{cell::RefCell, env, process}; From 667c6b6993c2c86d106d95526e5c1f413181d972 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 12:28:36 -0400 Subject: [PATCH 08/34] fmt --- src/cli/editor/mod.rs | 2 -- src/cli/main.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index 035c18c..a1fe715 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -3,8 +3,6 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; use rustyline::{completion::Candidate, hint::Hint, Editor, Helper}; - - mod candidate; mod completer; diff --git a/src/cli/main.rs b/src/cli/main.rs index ce98069..16e389e 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -15,7 +15,6 @@ pub use rustmatheval as lib; use config::HISTORY_FILE; - use error::Error; use std::{cell::RefCell, env, process}; From a099f8d758c845927fa99372dafdba3afbc9a449 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 13:23:53 -0400 Subject: [PATCH 09/34] improve hinting and highlighting --- src/cli/editor/completer.rs | 16 +++++++++++----- src/cli/editor/highlighter.rs | 4 ++-- src/cli/editor/hinter.rs | 10 ++++++---- src/cli/editor/mod.rs | 14 +++++++++----- src/cli/editor/validator.rs | 20 ++++++++++---------- src/cli/funcs.rs | 9 ++++++--- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 3085b4e..80ffb55 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,6 +1,6 @@ use rustyline::completion::Completer; -use crate::utils::find_last; +use crate::{funcs::format_func_with_args, utils::find_last, vars::format_var_name}; use super::{MyCandidate, MyHelper}; @@ -17,21 +17,27 @@ impl Completer for MyHelper<'_> { let line = &line[npos + 1..pos]; let funcs = self.funcs.borrow(); - if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { + let matches: Vec<_> = funcs.iter().filter(|f| f.name.starts_with(line)) + .map(|f| MyCandidate(f.name[pos - npos - 1..].to_string(), format_func_with_args(f))).collect(); + + if !matches.is_empty() { return rustyline::Result::Ok(( pos - npos, - vec![MyCandidate(func.name[pos - npos - 1..].to_string())], + matches, )); } } else if let Some(npos) = find_last('$', &line[..pos]) { let line = &line[npos + 1..pos]; let vars = self.vars.borrow(); - if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { + let matches: Vec<_> = vars.iter().filter(|v| v.repr.starts_with(line)) + .map(|v| MyCandidate(v.repr[pos - npos - 1..].to_string(), format_var_name(&v.repr).to_string())).collect(); + + if !matches.is_empty() { // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); return rustyline::Result::Ok(( pos - npos, - vec![MyCandidate(var.repr[pos - npos - 1..].to_string())], + matches, )); } } diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs index 5e793e2..8f528f0 100644 --- a/src/cli/editor/highlighter.rs +++ b/src/cli/editor/highlighter.rs @@ -32,8 +32,8 @@ impl Highlighter for MyHelper<'_> { candidate: &'c str, _completion: rustyline::CompletionType, ) -> std::borrow::Cow<'c, str> { - let form = candidate.red(); - Cow::Owned(form.to_string()) + // We don't highlight the candidate because the completer formats with color + Cow::Borrowed(candidate) } fn highlight_char(&self, _line: &str, _pos: usize) -> bool { diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index 3fbb1cd..ab81238 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -2,10 +2,10 @@ use rustyline::hint::Hinter; use crate::utils::find_last; -use super::{MyCandidate, MyHelper}; +use super::{MyHelper, MyHint}; impl Hinter for MyHelper<'_> { - type Hint = MyCandidate; + type Hint = MyHint; fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { if let Some(npos) = find_last('#', &line[..pos]) { @@ -13,14 +13,16 @@ impl Hinter for MyHelper<'_> { let funcs = self.funcs.borrow(); if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - return Some(MyCandidate(func.name[pos - npos - 1..].to_string())); + let s = func.name[pos - npos - 1..].to_string(); + return Some(MyHint(s)); } } else if let Some(npos) = find_last('$', &line[..pos]) { let line = &line[npos + 1..pos]; let vars = self.vars.borrow(); if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { - return Some(MyCandidate(var.repr[pos - npos - 1..].to_string())); + let s = var.repr[pos - npos - 1..].to_string(); + return Some(MyHint(s)); } } diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index a1fe715..c97652e 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; -use rustyline::{completion::Candidate, hint::Hint, Editor, Helper}; +use rustyline::{Editor, Helper, completion::Candidate, config::Configurer, hint::Hint}; mod candidate; @@ -20,11 +20,13 @@ pub fn editor<'a>( funcs, vars, - valid: RefCell::new(false), + valid: RefCell::new(true), }; editor.set_helper(Some(helper)); + editor.set_completion_type(rustyline::CompletionType::List); + editor } @@ -37,11 +39,11 @@ pub struct MyHelper<'cell> { impl Helper for MyHelper<'_> {} -pub struct MyCandidate(String); +pub struct MyCandidate(String, String); impl Candidate for MyCandidate { fn display(&self) -> &str { - &self.0 + &self.1 } fn replacement(&self) -> &str { @@ -49,7 +51,9 @@ impl Candidate for MyCandidate { } } -impl Hint for MyCandidate { +pub struct MyHint(String); + +impl Hint for MyHint { fn display(&self) -> &str { &self.0 } diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs index 16183ae..43599b1 100644 --- a/src/cli/editor/validator.rs +++ b/src/cli/editor/validator.rs @@ -8,18 +8,18 @@ impl Validator for MyHelper<'_> { &self, ctx: &mut rustyline::validate::ValidationContext, ) -> rustyline::Result { - let line = ctx.input(); - let context = EvaluationContext::default(); + // let line = ctx.input(); + // let context = EvaluationContext::default(); - // dbg!(line); + // // dbg!(line); - let (valid_, result) = if tokenize(line, &context).is_err() { - (false, ValidationResult::Incomplete) - } else { - (true, ValidationResult::Valid(None)) - }; - *self.valid.borrow_mut() = valid_; - rustyline::Result::Ok(result); + // let (valid_, result) = if tokenize(line, &context).is_err() { + // (false, ValidationResult::Incomplete) + // } else { + // (true, ValidationResult::Valid(None)) + // }; + // *self.valid.borrow_mut() = valid_; + // rustyline::Result::Ok(result) Ok(rustyline::validate::ValidationResult::Valid(None)) } diff --git a/src/cli/funcs.rs b/src/cli/funcs.rs index fecbd75..4a53245 100644 --- a/src/cli/funcs.rs +++ b/src/cli/funcs.rs @@ -41,11 +41,14 @@ pub fn format_func_name(name: &str) -> ColoredString { format!("#{}", name.magenta().bold()).normal() } +pub fn format_func_with_args(func: &Function) -> String { + format!("{}({})", format_func_name(&func.name), func.args.iter().map(color_arg).join(", ")) +} + fn format_func(func: &Function, funcs: &[Function], vars: &[Variable]) -> String { format!( - "[ {}({}) = {} ]", - format_func_name(&func.name), - func.args.iter().map(color_arg).join(", "), + "[ {} = {} ]", + format_func_with_args(func), stringify_func_code(func, funcs, vars) ) } From a535a452e3a3b77c50b0ff984972834aca23839e Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 13:24:02 -0400 Subject: [PATCH 10/34] lint --- src/cli/editor/completer.rs | 34 ++++++++++++++++++++++------------ src/cli/editor/mod.rs | 2 +- src/cli/funcs.rs | 6 +++++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 80ffb55..535e0fb 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -17,28 +17,38 @@ impl Completer for MyHelper<'_> { let line = &line[npos + 1..pos]; let funcs = self.funcs.borrow(); - let matches: Vec<_> = funcs.iter().filter(|f| f.name.starts_with(line)) - .map(|f| MyCandidate(f.name[pos - npos - 1..].to_string(), format_func_with_args(f))).collect(); + let matches: Vec<_> = funcs + .iter() + .filter(|f| f.name.starts_with(line)) + .map(|f| { + MyCandidate( + f.name[pos - npos - 1..].to_string(), + format_func_with_args(f), + ) + }) + .collect(); if !matches.is_empty() { - return rustyline::Result::Ok(( - pos - npos, - matches, - )); + return rustyline::Result::Ok((pos - npos, matches)); } } else if let Some(npos) = find_last('$', &line[..pos]) { let line = &line[npos + 1..pos]; let vars = self.vars.borrow(); - let matches: Vec<_> = vars.iter().filter(|v| v.repr.starts_with(line)) - .map(|v| MyCandidate(v.repr[pos - npos - 1..].to_string(), format_var_name(&v.repr).to_string())).collect(); + let matches: Vec<_> = vars + .iter() + .filter(|v| v.repr.starts_with(line)) + .map(|v| { + MyCandidate( + v.repr[pos - npos - 1..].to_string(), + format_var_name(&v.repr).to_string(), + ) + }) + .collect(); if !matches.is_empty() { // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); - return rustyline::Result::Ok(( - pos - npos, - matches, - )); + return rustyline::Result::Ok((pos - npos, matches)); } } diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index c97652e..aca8eee 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; -use rustyline::{Editor, Helper, completion::Candidate, config::Configurer, hint::Hint}; +use rustyline::{completion::Candidate, config::Configurer, hint::Hint, Editor, Helper}; mod candidate; diff --git a/src/cli/funcs.rs b/src/cli/funcs.rs index 4a53245..78c5074 100644 --- a/src/cli/funcs.rs +++ b/src/cli/funcs.rs @@ -42,7 +42,11 @@ pub fn format_func_name(name: &str) -> ColoredString { } pub fn format_func_with_args(func: &Function) -> String { - format!("{}({})", format_func_name(&func.name), func.args.iter().map(color_arg).join(", ")) + format!( + "{}({})", + format_func_name(&func.name), + func.args.iter().map(color_arg).join(", ") + ) } fn format_func(func: &Function, funcs: &[Function], vars: &[Variable]) -> String { From 0dab155d3b36bf68d0d9a57c17f21dbea5574517 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 13:48:48 -0400 Subject: [PATCH 11/34] refactor completion logic --- src/cli/editor/completer.rs | 105 ++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 535e0fb..1192543 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,9 +1,62 @@ +use rustmatheval::model::{functions::Function, variables::Variable}; use rustyline::completion::Completer; use crate::{funcs::format_func_with_args, utils::find_last, vars::format_var_name}; use super::{MyCandidate, MyHelper}; +fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { + if let Some(pos) = find_last(Item::prefix(), line) { + // +1 because of `key` + let line = &line[pos + 1..]; + let stride = line.len() - pos; + let matches: Vec = items + .iter() + .filter(|it| it.name().starts_with(line)) + // -1 because of `key` + .map(|it| MyCandidate(it.name()[stride - 1..].to_string(), it.format())) + .collect(); + if !matches.is_empty() { + return Some((stride, matches)); + } + } + None +} + +trait Named { + fn name(&self) -> &str; + fn format(&self) -> String; + fn prefix() -> char; +} + +impl Named for Function { + fn name(&self) -> &str { + &self.name + } + + fn format(&self) -> String { + format_func_with_args(self) + } + + fn prefix() -> char { + '#' + } +} + +impl Named for Variable { + fn name(&self) -> &str { + &self.repr + } + + fn format(&self) -> String { + format_var_name(&self.repr).to_string() + } + + fn prefix() -> char { + '$' + } +} + impl Completer for MyHelper<'_> { type Candidate = MyCandidate; @@ -13,46 +66,20 @@ impl Completer for MyHelper<'_> { pos: usize, _ctx: &rustyline::Context<'_>, ) -> rustyline::Result<(usize, Vec)> { - if let Some(npos) = find_last('#', &line[..pos]) { - let line = &line[npos + 1..pos]; - let funcs = self.funcs.borrow(); - - let matches: Vec<_> = funcs - .iter() - .filter(|f| f.name.starts_with(line)) - .map(|f| { - MyCandidate( - f.name[pos - npos - 1..].to_string(), - format_func_with_args(f), - ) - }) - .collect(); - - if !matches.is_empty() { - return rustyline::Result::Ok((pos - npos, matches)); - } - } else if let Some(npos) = find_last('$', &line[..pos]) { - let line = &line[npos + 1..pos]; - let vars = self.vars.borrow(); - - let matches: Vec<_> = vars - .iter() - .filter(|v| v.repr.starts_with(line)) - .map(|v| { - MyCandidate( - v.repr[pos - npos - 1..].to_string(), - format_var_name(&v.repr).to_string(), - ) - }) - .collect(); - - if !matches.is_empty() { - // return Some(MyCandidate(var.repr[pos-npos-1..].to_string())); - return rustyline::Result::Ok((pos - npos, matches)); - } - } + let line = &line[..pos]; + + let funcs = self.funcs.borrow(); + let vars = self.vars.borrow(); + + let candidates = if let Some(candidates) = find_candidates(line, &funcs) { + candidates + } else if let Some(candidates) = find_candidates(line, &vars) { + candidates + } else { + (0, vec![]) + }; - rustyline::Result::Ok((0, vec![])) + rustyline::Result::Ok(candidates) } fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { From 5616135bda09a377c2feaa0561170b89f660f74f Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 13:50:09 -0400 Subject: [PATCH 12/34] lint --- src/cli/editor/completer.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 1192543..e5eee9a 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -14,7 +14,11 @@ fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Ve .iter() .filter(|it| it.name().starts_with(line)) // -1 because of `key` - .map(|it| MyCandidate(it.name()[stride - 1..].to_string(), it.format())) + .map(|it| { + let replacement = it.name()[stride - 1..].to_string(); + let formatted = it.format(); + MyCandidate(replacement, formatted) + }) .collect(); if !matches.is_empty() { return Some((stride, matches)); From 8b43d537dc7571b4e223d97f6afe652d73f3ce97 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 13:50:26 -0400 Subject: [PATCH 13/34] move comment --- src/cli/editor/completer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index e5eee9a..a77e31d 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -13,8 +13,8 @@ fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Ve let matches: Vec = items .iter() .filter(|it| it.name().starts_with(line)) - // -1 because of `key` .map(|it| { + // -1 because of `key` let replacement = it.name()[stride - 1..].to_string(); let formatted = it.format(); MyCandidate(replacement, formatted) From f29d383da795a081c15eb949044f865cdcb24de5 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:24:31 -0400 Subject: [PATCH 14/34] refactor --- src/cli/editor/common.rs | 82 +++++++++++++++++++++++++++++++++++++ src/cli/editor/completer.rs | 73 +++++++++------------------------ src/cli/editor/hinter.rs | 51 +++++++++++++---------- src/cli/editor/mod.rs | 27 +----------- src/cli/utils.rs | 17 -------- 5 files changed, 132 insertions(+), 118 deletions(-) create mode 100644 src/cli/editor/common.rs diff --git a/src/cli/editor/common.rs b/src/cli/editor/common.rs new file mode 100644 index 0000000..477241e --- /dev/null +++ b/src/cli/editor/common.rs @@ -0,0 +1,82 @@ +use rustmatheval::model::{functions::Function, variables::Variable}; + +use crate::funcs::format_func_with_args; +use crate::vars::format_var_name; + +/// Find the position of the last instance of `c` +/// +/// ## Examples +/// +/// ``` +/// let s = "abc #foo bar"; +/// let pos = find_last('#', s).unwrap(); +/// assert_eq!(s.chars().nth(pos), '#'); +/// ``` +pub fn find_last(c: char, str: &str) -> Option { + str.chars() + .into_iter() + .rev() + .position(|ch| ch == c) + .map(|pos| str.chars().count() - pos - 1) +} + +pub(super) fn find_items( + line: &str, + items: &[Item], + create_item: ToIntermediate, + create_output: ToOutput, +) -> Option +where + Item: Named, + ToIntermediate: Fn(usize, &Item) -> Intermediate, + ToOutput: FnOnce(usize, Vec) -> Output, +{ + if let Some(pos) = find_last(Item::prefix(), line) { + // +1 because of `key` + let line = &line[pos + 1..]; + let stride = line.len() - pos; + let matches: Vec = items + .iter() + .filter(|it| it.name().starts_with(line)) + .map(|it| create_item(stride, it)) + .collect(); + if !matches.is_empty() { + return Some(create_output(stride, matches)); + } + } + None +} + +pub trait Named { + fn name(&self) -> &str; + fn format(&self) -> String; + fn prefix() -> char; +} + +impl Named for Function { + fn name(&self) -> &str { + &self.name + } + + fn format(&self) -> String { + format_func_with_args(self) + } + + fn prefix() -> char { + '#' + } +} + +impl Named for Variable { + fn name(&self) -> &str { + &self.repr + } + + fn format(&self) -> String { + format_var_name(&self.repr).to_string() + } + + fn prefix() -> char { + '$' + } +} diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index a77e31d..5a96bab 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,64 +1,29 @@ -use rustmatheval::model::{functions::Function, variables::Variable}; -use rustyline::completion::Completer; +use rustyline::completion::{Candidate, Completer}; -use crate::{funcs::format_func_with_args, utils::find_last, vars::format_var_name}; +use super::{ + common::{find_items, Named}, + MyHelper, +}; +pub struct MyCandidate(String, String); -use super::{MyCandidate, MyHelper}; - -fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { - if let Some(pos) = find_last(Item::prefix(), line) { - // +1 because of `key` - let line = &line[pos + 1..]; - let stride = line.len() - pos; - let matches: Vec = items - .iter() - .filter(|it| it.name().starts_with(line)) - .map(|it| { - // -1 because of `key` - let replacement = it.name()[stride - 1..].to_string(); - let formatted = it.format(); - MyCandidate(replacement, formatted) - }) - .collect(); - if !matches.is_empty() { - return Some((stride, matches)); - } - } - None -} - -trait Named { - fn name(&self) -> &str; - fn format(&self) -> String; - fn prefix() -> char; -} - -impl Named for Function { - fn name(&self) -> &str { - &self.name +impl Candidate for MyCandidate { + fn display(&self) -> &str { + &self.1 } - fn format(&self) -> String { - format_func_with_args(self) - } - - fn prefix() -> char { - '#' + fn replacement(&self) -> &str { + &self.0 } } -impl Named for Variable { - fn name(&self) -> &str { - &self.repr - } - - fn format(&self) -> String { - format_var_name(&self.repr).to_string() - } - - fn prefix() -> char { - '$' - } +fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { + let create_item = |stride: usize, item: &Item| { + let replacement = item.name()[stride..].to_string(); + let formatted = item.format(); + MyCandidate(replacement, formatted) + }; + let create_output = |stride: usize, candidates: Vec| (stride, candidates); + find_items(line, items, create_item, create_output) } impl Completer for MyHelper<'_> { diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index ab81238..5250081 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -1,31 +1,38 @@ -use rustyline::hint::Hinter; +use rustyline::hint::{Hint, Hinter}; -use crate::utils::find_last; +use super::{ + common::{find_items, Named}, + MyHelper, +}; -use super::{MyHelper, MyHint}; +pub struct MyHint(String); + +impl Hint for MyHint { + fn display(&self) -> &str { + &self.0 + } + + fn completion(&self) -> Option<&str> { + Some(&self.0) + } +} + +pub fn find_hint(line: &str, items: &[Item]) -> Option { + let create_item = |stride: usize, item: &Item| MyHint(item.name()[stride..].to_string()); + let create_output = |_, hints: Vec| hints; + let hints = find_items(line, items, create_item, create_output); + hints.and_then(|hints| hints.into_iter().max_by_key(|hint| hint.0.len())) +} impl Hinter for MyHelper<'_> { type Hint = MyHint; fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { - if let Some(npos) = find_last('#', &line[..pos]) { - let line = &line[npos + 1..pos]; - let funcs = self.funcs.borrow(); - - if let Some(func) = funcs.iter().find(|f| f.name.starts_with(line)) { - let s = func.name[pos - npos - 1..].to_string(); - return Some(MyHint(s)); - } - } else if let Some(npos) = find_last('$', &line[..pos]) { - let line = &line[npos + 1..pos]; - let vars = self.vars.borrow(); - - if let Some(var) = vars.iter().find(|v| v.repr.starts_with(line)) { - let s = var.repr[pos - npos - 1..].to_string(); - return Some(MyHint(s)); - } - } - - None + let line = &line[..pos]; + + let funcs = self.funcs.borrow(); + let vars = self.vars.borrow(); + + find_hint(line, &funcs).or_else(|| find_hint(line, &vars)) } } diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index aca8eee..751c3e6 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -1,10 +1,11 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; -use rustyline::{completion::Candidate, config::Configurer, hint::Hint, Editor, Helper}; +use rustyline::{config::Configurer, Editor, Helper}; mod candidate; +mod common; mod completer; mod highlighter; mod hinter; @@ -38,27 +39,3 @@ pub struct MyHelper<'cell> { } impl Helper for MyHelper<'_> {} - -pub struct MyCandidate(String, String); - -impl Candidate for MyCandidate { - fn display(&self) -> &str { - &self.1 - } - - fn replacement(&self) -> &str { - &self.0 - } -} - -pub struct MyHint(String); - -impl Hint for MyHint { - fn display(&self) -> &str { - &self.0 - } - - fn completion(&self) -> Option<&str> { - Some(&self.0) - } -} diff --git a/src/cli/utils.rs b/src/cli/utils.rs index 1baf268..3eb0ebe 100644 --- a/src/cli/utils.rs +++ b/src/cli/utils.rs @@ -14,20 +14,3 @@ where } } } - -/// Find the position of the last instance of `c` -/// -/// ## Examples -/// -/// ``` -/// let s = "abc #foo bar"; -/// let pos = find_last('#', s).unwrap(); -/// assert_eq!(s.chars().nth(pos), '#'); -/// ``` -pub fn find_last(c: char, str: &str) -> Option { - str.chars() - .into_iter() - .rev() - .position(|ch| ch == c) - .map(|pos| str.chars().count() - pos - 1) -} From 411e9067baf820b2f43306baa68c4ac0d086b769 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:24:42 -0400 Subject: [PATCH 15/34] fix --- src/cli/editor/validator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs index 43599b1..dcf4331 100644 --- a/src/cli/editor/validator.rs +++ b/src/cli/editor/validator.rs @@ -1,4 +1,4 @@ -use rustmatheval::{model::EvaluationContext, tokenize}; + use rustyline::validate::{ValidationResult, Validator}; use super::MyHelper; @@ -6,7 +6,7 @@ use super::MyHelper; impl Validator for MyHelper<'_> { fn validate( &self, - ctx: &mut rustyline::validate::ValidationContext, + _ctx: &mut rustyline::validate::ValidationContext, ) -> rustyline::Result { // let line = ctx.input(); // let context = EvaluationContext::default(); From 066e8d61b7fb12a8bced84544c9d01c68fb37098 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:24:48 -0400 Subject: [PATCH 16/34] lint --- src/cli/editor/validator.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs index dcf4331..b4d0312 100644 --- a/src/cli/editor/validator.rs +++ b/src/cli/editor/validator.rs @@ -1,4 +1,3 @@ - use rustyline::validate::{ValidationResult, Validator}; use super::MyHelper; From 6028f1a53c70611cb1bd00eefb75cef7cbbf466d Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:25:31 -0400 Subject: [PATCH 17/34] remove file --- src/cli/editor/candidate.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/cli/editor/candidate.rs diff --git a/src/cli/editor/candidate.rs b/src/cli/editor/candidate.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/cli/editor/candidate.rs +++ /dev/null @@ -1 +0,0 @@ - From 7b1a83c0f739763c449a3abf2c3756a025a4e6dd Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:26:07 -0400 Subject: [PATCH 18/34] rename --- src/cli/editor/completer.rs | 2 +- src/cli/editor/{common.rs => finder.rs} | 0 src/cli/editor/hinter.rs | 2 +- src/cli/editor/mod.rs | 4 +--- 4 files changed, 3 insertions(+), 5 deletions(-) rename src/cli/editor/{common.rs => finder.rs} (100%) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 5a96bab..e563ba5 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,7 +1,7 @@ use rustyline::completion::{Candidate, Completer}; use super::{ - common::{find_items, Named}, + finder::{find_items, Named}, MyHelper, }; pub struct MyCandidate(String, String); diff --git a/src/cli/editor/common.rs b/src/cli/editor/finder.rs similarity index 100% rename from src/cli/editor/common.rs rename to src/cli/editor/finder.rs diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index 5250081..f153e61 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -1,7 +1,7 @@ use rustyline::hint::{Hint, Hinter}; use super::{ - common::{find_items, Named}, + finder::{find_items, Named}, MyHelper, }; diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index 751c3e6..bd998e2 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -3,9 +3,7 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; use rustyline::{config::Configurer, Editor, Helper}; -mod candidate; - -mod common; +mod finder; mod completer; mod highlighter; mod hinter; From 75f6209a7d13e22ad26210d62a5629e5271eb85c Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:36:13 -0400 Subject: [PATCH 19/34] partially centralize prefixes, fix completion bounds --- src/cli/editor/completer.rs | 6 ++++-- src/cli/editor/finder.rs | 22 ++++++++++++---------- src/cli/editor/hinter.rs | 5 +++-- src/lib/model/functions.rs | 2 ++ src/lib/model/variables.rs | 2 ++ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index e563ba5..d51254a 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,9 +1,11 @@ use rustyline::completion::{Candidate, Completer}; use super::{ - finder::{find_items, Named}, + finder::{find_items, Findable}, MyHelper, }; + +#[derive(Debug)] pub struct MyCandidate(String, String); impl Candidate for MyCandidate { @@ -16,7 +18,7 @@ impl Candidate for MyCandidate { } } -fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { +fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { let create_item = |stride: usize, item: &Item| { let replacement = item.name()[stride..].to_string(); let formatted = item.format(); diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 477241e..22df3ce 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -1,4 +1,4 @@ -use rustmatheval::model::{functions::Function, variables::Variable}; +use rustmatheval::model::{functions::{Function, PREFIX as FUNCTION_PREFIX}, variables::{Variable, PREFIX as VARIABLE_PREFIX}}; use crate::funcs::format_func_with_args; use crate::vars::format_var_name; @@ -27,12 +27,12 @@ pub(super) fn find_items( create_output: ToOutput, ) -> Option where - Item: Named, + Item: Findable, ToIntermediate: Fn(usize, &Item) -> Intermediate, ToOutput: FnOnce(usize, Vec) -> Output, { if let Some(pos) = find_last(Item::prefix(), line) { - // +1 because of `key` + // +1 because of prefix let line = &line[pos + 1..]; let stride = line.len() - pos; let matches: Vec = items @@ -41,19 +41,21 @@ where .map(|it| create_item(stride, it)) .collect(); if !matches.is_empty() { - return Some(create_output(stride, matches)); + // +1 because of prefix + return Some(create_output(stride + 1, matches)); } } None } -pub trait Named { +pub trait Findable { fn name(&self) -> &str; fn format(&self) -> String; fn prefix() -> char; + // fn replacement(stride: usize) } -impl Named for Function { +impl Findable for Function { fn name(&self) -> &str { &self.name } @@ -63,11 +65,11 @@ impl Named for Function { } fn prefix() -> char { - '#' + FUNCTION_PREFIX } } -impl Named for Variable { +impl Findable for Variable { fn name(&self) -> &str { &self.repr } @@ -77,6 +79,6 @@ impl Named for Variable { } fn prefix() -> char { - '$' + VARIABLE_PREFIX } -} +} \ No newline at end of file diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index f153e61..9991352 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -1,10 +1,11 @@ use rustyline::hint::{Hint, Hinter}; use super::{ - finder::{find_items, Named}, + finder::{find_items, Findable}, MyHelper, }; +#[derive(Debug)] pub struct MyHint(String); impl Hint for MyHint { @@ -17,7 +18,7 @@ impl Hint for MyHint { } } -pub fn find_hint(line: &str, items: &[Item]) -> Option { +pub fn find_hint(line: &str, items: &[Item]) -> Option { let create_item = |stride: usize, item: &Item| MyHint(item.name()[stride..].to_string()); let create_output = |_, hints: Vec| hints; let hints = find_items(line, items, create_item, create_output); diff --git a/src/lib/model/functions.rs b/src/lib/model/functions.rs index a651e36..3913ff2 100644 --- a/src/lib/model/functions.rs +++ b/src/lib/model/functions.rs @@ -8,6 +8,8 @@ use super::{ EvaluationContext, }; +pub const PREFIX: char = '#'; + #[derive(Debug, Clone, Copy, PartialEq)] pub enum Functions<'a> { Builtin(&'a Operator), diff --git a/src/lib/model/variables.rs b/src/lib/model/variables.rs index e9b9fbf..3795eb8 100644 --- a/src/lib/model/variables.rs +++ b/src/lib/model/variables.rs @@ -1,5 +1,7 @@ use super::representable::{get_by_repr, Searchable}; +pub const PREFIX: char = '$'; + #[derive(Clone, Debug, PartialEq)] /// Represents a variable, a value with a name pub struct Variable { From e3b7744584be99183229db795718cd8c71d15a0d Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:37:39 -0400 Subject: [PATCH 20/34] centralize prefixes --- src/cli/editor/finder.rs | 7 ++++--- src/lib/model/functions.rs | 2 +- src/lib/model/variables.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 22df3ce..30535cd 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -8,9 +8,10 @@ use crate::vars::format_var_name; /// ## Examples /// /// ``` -/// let s = "abc #foo bar"; -/// let pos = find_last('#', s).unwrap(); -/// assert_eq!(s.chars().nth(pos), '#'); +/// use rustmatheval::model::functions::PREFIX; +/// let s = format1("abc {}foo bar", PREFIX); +/// let pos = find_last(PREFIX, s).unwrap(); +/// assert_eq!(s.chars().nth(pos), PREFIX); /// ``` pub fn find_last(c: char, str: &str) -> Option { str.chars() diff --git a/src/lib/model/functions.rs b/src/lib/model/functions.rs index 3913ff2..7623352 100644 --- a/src/lib/model/functions.rs +++ b/src/lib/model/functions.rs @@ -63,7 +63,7 @@ impl Searchable for Function { impl Function { pub fn is(text: &str) -> bool { - text.starts_with('#') + text.starts_with(PREFIX) } pub fn next_function<'a>(text: &str, funcs: &'a [Self]) -> Option<(&'a Self, usize)> { get_by_repr(text, funcs) diff --git a/src/lib/model/variables.rs b/src/lib/model/variables.rs index 3795eb8..2cb0e27 100644 --- a/src/lib/model/variables.rs +++ b/src/lib/model/variables.rs @@ -31,7 +31,7 @@ impl Variable { /// Returns whether or not the given representation could reference a valid variable pub fn is(repr: &str) -> bool { - repr.starts_with('$') + repr.starts_with(PREFIX) } } From 0d4962d7570404e834df1c2ff91c2175f85cf13e Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 14:37:52 -0400 Subject: [PATCH 21/34] lint --- src/cli/editor/completer.rs | 5 ++++- src/cli/editor/finder.rs | 9 ++++++--- src/cli/editor/mod.rs | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index d51254a..352a7ea 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -18,7 +18,10 @@ impl Candidate for MyCandidate { } } -fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { +fn find_candidates( + line: &str, + items: &[Item], +) -> Option<(usize, Vec)> { let create_item = |stride: usize, item: &Item| { let replacement = item.name()[stride..].to_string(); let formatted = item.format(); diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 30535cd..487fbd9 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -1,4 +1,7 @@ -use rustmatheval::model::{functions::{Function, PREFIX as FUNCTION_PREFIX}, variables::{Variable, PREFIX as VARIABLE_PREFIX}}; +use rustmatheval::model::{ + functions::{Function, PREFIX as FUNCTION_PREFIX}, + variables::{Variable, PREFIX as VARIABLE_PREFIX}, +}; use crate::funcs::format_func_with_args; use crate::vars::format_var_name; @@ -53,7 +56,7 @@ pub trait Findable { fn name(&self) -> &str; fn format(&self) -> String; fn prefix() -> char; - // fn replacement(stride: usize) + // fn replacement(stride: usize) } impl Findable for Function { @@ -82,4 +85,4 @@ impl Findable for Variable { fn prefix() -> char { VARIABLE_PREFIX } -} \ No newline at end of file +} diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index bd998e2..f692ac6 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -3,8 +3,8 @@ use std::cell::RefCell; use rustmatheval::model::{functions::Function, variables::Variable}; use rustyline::{config::Configurer, Editor, Helper}; -mod finder; mod completer; +mod finder; mod highlighter; mod hinter; mod validator; From d64d5a8b0fd3757e1cde3c7fdbe287fef346b959 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 15:04:10 -0400 Subject: [PATCH 22/34] docs --- src/cli/editor/completer.rs | 4 ++-- src/cli/editor/finder.rs | 34 +++++++++++++++++++++++++++++++--- src/cli/editor/hinter.rs | 4 ++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 352a7ea..d4909ca 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -22,13 +22,13 @@ fn find_candidates( line: &str, items: &[Item], ) -> Option<(usize, Vec)> { - let create_item = |stride: usize, item: &Item| { + let create_intermediate = |stride: usize, item: &Item| { let replacement = item.name()[stride..].to_string(); let formatted = item.format(); MyCandidate(replacement, formatted) }; let create_output = |stride: usize, candidates: Vec| (stride, candidates); - find_items(line, items, create_item, create_output) + find_items(line, items, create_intermediate, create_output) } impl Completer for MyHelper<'_> { diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 487fbd9..0a18b2a 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -24,10 +24,38 @@ pub fn find_last(c: char, str: &str) -> Option { .map(|pos| str.chars().count() - pos - 1) } +/// Find all possibly completable `Item`s in `line` at the position closest to the end as indicated by [`Findable::prefix`] +/// and perform transformations on it using the two `Fn` parameters. +/// +/// ## Parameters +/// * `Item` - A [`Findable`] type to search for in `line` +/// * `Intermediate` - A type that is part of the desired result. +/// * `Output` - This type is used to construct the output of the function +/// * `ToIntermediate` - A `Fn` type capable of converting `Item`s to `Intermediate` +/// * `ToOutput` - A `Fn` type capable of converting `Intermediate`s to `Output` +/// +/// ## Arguments +/// * `line` - The string to find items within +/// * `items` - A slice of items, these are candidates for the search +/// * `create_intermediate` - A `Fn` that: +/// * accepts: +/// * `stride: usize`: The length of the matching string in `line` according to the prefix +/// * `item: &Item`: The matching item +/// * and produces an `Intermediate` +/// * `create_output` - a `FnOnce` that: +/// * accepts: +/// * `stride: usize` - "" +/// * `intermediates: Vec` - A list of intermediates +/// * and produces the final output of the function (which is then wrapped in an Option, see the Returns subheader) +/// +/// ## Returns +/// Returns `None` if there are no possible matches inside the input string. There are two reasons this could occur: +/// * There is no prefix in the string, and thus no matches are possible +/// * There is a prefix in the string, but none of the items could possibly complete the identifier pub(super) fn find_items( line: &str, items: &[Item], - create_item: ToIntermediate, + create_intermediate: ToIntermediate, create_output: ToOutput, ) -> Option where @@ -42,7 +70,7 @@ where let matches: Vec = items .iter() .filter(|it| it.name().starts_with(line)) - .map(|it| create_item(stride, it)) + .map(|it| create_intermediate(stride, it)) .collect(); if !matches.is_empty() { // +1 because of prefix @@ -52,11 +80,11 @@ where None } +/// Represents a type that can be found (using [`find_items`]) pub trait Findable { fn name(&self) -> &str; fn format(&self) -> String; fn prefix() -> char; - // fn replacement(stride: usize) } impl Findable for Function { diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index 9991352..f2e4651 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -19,9 +19,9 @@ impl Hint for MyHint { } pub fn find_hint(line: &str, items: &[Item]) -> Option { - let create_item = |stride: usize, item: &Item| MyHint(item.name()[stride..].to_string()); + let create_intermediate = |stride: usize, item: &Item| MyHint(item.name()[stride..].to_string()); let create_output = |_, hints: Vec| hints; - let hints = find_items(line, items, create_item, create_output); + let hints = find_items(line, items, create_intermediate, create_output); hints.and_then(|hints| hints.into_iter().max_by_key(|hint| hint.0.len())) } From e1e267a22f5a797c4a1f183b12704630101132e5 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 15:52:26 -0400 Subject: [PATCH 23/34] remove custom types where possible --- src/cli/editor/completer.rs | 30 ++++++++++-------------------- src/cli/editor/finder.rs | 14 ++++++++++---- src/cli/editor/hinter.rs | 25 ++++++------------------- 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index d4909ca..9e18a0c 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,38 +1,26 @@ -use rustyline::completion::{Candidate, Completer}; +use rustyline::completion::{Candidate, Completer, Pair}; use super::{ finder::{find_items, Findable}, MyHelper, }; -#[derive(Debug)] -pub struct MyCandidate(String, String); - -impl Candidate for MyCandidate { - fn display(&self) -> &str { - &self.1 - } - - fn replacement(&self) -> &str { - &self.0 - } -} - fn find_candidates( line: &str, items: &[Item], -) -> Option<(usize, Vec)> { +) -> Option<(usize, Vec)> { let create_intermediate = |stride: usize, item: &Item| { let replacement = item.name()[stride..].to_string(); - let formatted = item.format(); - MyCandidate(replacement, formatted) + let display = item.format(); + Pair { display, replacement } }; - let create_output = |stride: usize, candidates: Vec| (stride, candidates); - find_items(line, items, create_intermediate, create_output) + let create_output = |stride: usize, candidates: Vec| (stride, candidates); + let c = find_items(line, items, create_intermediate, create_output); + c } impl Completer for MyHelper<'_> { - type Candidate = MyCandidate; + type Candidate = Pair; fn complete( &self, @@ -58,6 +46,8 @@ impl Completer for MyHelper<'_> { fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { let end = line.pos(); + // let x: String = line.lines().collect(); + // dbg!(x, start, end, elected); line.replace(start..end, elected); } } diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 0a18b2a..837185c 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -64,16 +64,22 @@ where ToOutput: FnOnce(usize, Vec) -> Output, { if let Some(pos) = find_last(Item::prefix(), line) { - // +1 because of prefix + // dbg!(line, line.len(), pos, &line[pos + 1..].len()); + // let stride = line.len() - pos; + + // +1 removes prefix + // e.g. "#foobar" => "foobar" let line = &line[pos + 1..]; - let stride = line.len() - pos; + let stride = line.len(); + let matches: Vec = items .iter() - .filter(|it| it.name().starts_with(line)) + .filter(|it| it.name().starts_with(line) && it.name() != line) .map(|it| create_intermediate(stride, it)) .collect(); if !matches.is_empty() { - // +1 because of prefix + // +1 because the replacement length needs to include the prefix + // for some reason otherwise it's discarded on [`rustyline::completion::Completer::update`] return Some(create_output(stride + 1, matches)); } } diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index f2e4651..3c28ae9 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -1,32 +1,19 @@ -use rustyline::hint::{Hint, Hinter}; +use rustyline::hint::Hinter; use super::{ finder::{find_items, Findable}, MyHelper, }; -#[derive(Debug)] -pub struct MyHint(String); - -impl Hint for MyHint { - fn display(&self) -> &str { - &self.0 - } - - fn completion(&self) -> Option<&str> { - Some(&self.0) - } -} - -pub fn find_hint(line: &str, items: &[Item]) -> Option { - let create_intermediate = |stride: usize, item: &Item| MyHint(item.name()[stride..].to_string()); - let create_output = |_, hints: Vec| hints; +pub fn find_hint(line: &str, items: &[Item]) -> Option { + let create_intermediate = |stride: usize, item: &Item| item.name()[stride..].to_string(); + let create_output = |_, hints: Vec| hints; let hints = find_items(line, items, create_intermediate, create_output); - hints.and_then(|hints| hints.into_iter().max_by_key(|hint| hint.0.len())) + hints.and_then(|hints| hints.into_iter().max_by_key(String::len)) } impl Hinter for MyHelper<'_> { - type Hint = MyHint; + type Hint = String; fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option { let line = &line[..pos]; From a880ea230b33f3720d14d9790b05b2705cea9e32 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 15:52:39 -0400 Subject: [PATCH 24/34] lint --- src/cli/editor/completer.rs | 10 +++++----- src/cli/editor/finder.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 9e18a0c..7c7d3f5 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -5,14 +5,14 @@ use super::{ MyHelper, }; -fn find_candidates( - line: &str, - items: &[Item], -) -> Option<(usize, Vec)> { +fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { let create_intermediate = |stride: usize, item: &Item| { let replacement = item.name()[stride..].to_string(); let display = item.format(); - Pair { display, replacement } + Pair { + display, + replacement, + } }; let create_output = |stride: usize, candidates: Vec| (stride, candidates); let c = find_items(line, items, create_intermediate, create_output); diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 837185c..a3e1e6e 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -71,7 +71,7 @@ where // e.g. "#foobar" => "foobar" let line = &line[pos + 1..]; let stride = line.len(); - + let matches: Vec = items .iter() .filter(|it| it.name().starts_with(line) && it.name() != line) @@ -79,7 +79,7 @@ where .collect(); if !matches.is_empty() { // +1 because the replacement length needs to include the prefix - // for some reason otherwise it's discarded on [`rustyline::completion::Completer::update`] + // for some reason otherwise it's discarded on [`rustyline::completion::Completer::update`] return Some(create_output(stride + 1, matches)); } } From bc344a9e8a26f476a0ea2df8ed5202c0d0c3fefc Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 15:52:50 -0400 Subject: [PATCH 25/34] fix --- src/cli/editor/completer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 7c7d3f5..de89a26 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -1,4 +1,4 @@ -use rustyline::completion::{Candidate, Completer, Pair}; +use rustyline::completion::{Completer, Pair}; use super::{ finder::{find_items, Findable}, From cf28be7d26c83435da4310dd8ceb910f30f3abfc Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:31:16 -0400 Subject: [PATCH 26/34] fix and simplify completion / hint logic --- src/cli/editor/completer.rs | 14 +++++--------- src/cli/editor/finder.rs | 18 +++++------------- src/cli/editor/hinter.rs | 5 ++--- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index de89a26..340c93c 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -5,8 +5,8 @@ use super::{ MyHelper, }; -fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, Vec)> { - let create_intermediate = |stride: usize, item: &Item| { +fn find_candidates(line: &str, items: &[Item]) -> Option> { + let create_intermediate = |stride, item: &Item| { let replacement = item.name()[stride..].to_string(); let display = item.format(); Pair { @@ -14,9 +14,7 @@ fn find_candidates(line: &str, items: &[Item]) -> Option<(usize, replacement, } }; - let create_output = |stride: usize, candidates: Vec| (stride, candidates); - let c = find_items(line, items, create_intermediate, create_output); - c + find_items(line, items, create_intermediate) } impl Completer for MyHelper<'_> { @@ -34,9 +32,9 @@ impl Completer for MyHelper<'_> { let vars = self.vars.borrow(); let candidates = if let Some(candidates) = find_candidates(line, &funcs) { - candidates + (pos, candidates) } else if let Some(candidates) = find_candidates(line, &vars) { - candidates + (pos, candidates) } else { (0, vec![]) }; @@ -46,8 +44,6 @@ impl Completer for MyHelper<'_> { fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) { let end = line.pos(); - // let x: String = line.lines().collect(); - // dbg!(x, start, end, elected); line.replace(start..end, elected); } } diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index a3e1e6e..3d7ea59 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -39,12 +39,11 @@ pub fn find_last(c: char, str: &str) -> Option { /// * `items` - A slice of items, these are candidates for the search /// * `create_intermediate` - A `Fn` that: /// * accepts: -/// * `stride: usize`: The length of the matching string in `line` according to the prefix /// * `item: &Item`: The matching item /// * and produces an `Intermediate` /// * `create_output` - a `FnOnce` that: /// * accepts: -/// * `stride: usize` - "" +/// * `start: usize` - The positon immediately after the prefix /// * `intermediates: Vec` - A list of intermediates /// * and produces the final output of the function (which is then wrapped in an Option, see the Returns subheader) /// @@ -52,21 +51,16 @@ pub fn find_last(c: char, str: &str) -> Option { /// Returns `None` if there are no possible matches inside the input string. There are two reasons this could occur: /// * There is no prefix in the string, and thus no matches are possible /// * There is a prefix in the string, but none of the items could possibly complete the identifier -pub(super) fn find_items( +pub(super) fn find_items( line: &str, items: &[Item], - create_intermediate: ToIntermediate, - create_output: ToOutput, -) -> Option + create_intermediate: ToIntermediate +) -> Option> where Item: Findable, ToIntermediate: Fn(usize, &Item) -> Intermediate, - ToOutput: FnOnce(usize, Vec) -> Output, { if let Some(pos) = find_last(Item::prefix(), line) { - // dbg!(line, line.len(), pos, &line[pos + 1..].len()); - // let stride = line.len() - pos; - // +1 removes prefix // e.g. "#foobar" => "foobar" let line = &line[pos + 1..]; @@ -78,9 +72,7 @@ where .map(|it| create_intermediate(stride, it)) .collect(); if !matches.is_empty() { - // +1 because the replacement length needs to include the prefix - // for some reason otherwise it's discarded on [`rustyline::completion::Completer::update`] - return Some(create_output(stride + 1, matches)); + return Some(matches); } } None diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index 3c28ae9..69ae004 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -6,9 +6,8 @@ use super::{ }; pub fn find_hint(line: &str, items: &[Item]) -> Option { - let create_intermediate = |stride: usize, item: &Item| item.name()[stride..].to_string(); - let create_output = |_, hints: Vec| hints; - let hints = find_items(line, items, create_intermediate, create_output); + let create_intermediate = |stride, item: &Item| item.name()[stride..].to_string(); + let hints = find_items(line, items, create_intermediate); hints.and_then(|hints| hints.into_iter().max_by_key(String::len)) } From 3a0d0a6fe8c54b74e1bb9b18313197004bd656c5 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:32:45 -0400 Subject: [PATCH 27/34] docs --- src/cli/editor/finder.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 3d7ea59..4890b9b 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -29,28 +29,24 @@ pub fn find_last(c: char, str: &str) -> Option { /// /// ## Parameters /// * `Item` - A [`Findable`] type to search for in `line` -/// * `Intermediate` - A type that is part of the desired result. -/// * `Output` - This type is used to construct the output of the function +/// * `Intermediate` - The output type /// * `ToIntermediate` - A `Fn` type capable of converting `Item`s to `Intermediate` -/// * `ToOutput` - A `Fn` type capable of converting `Intermediate`s to `Output` /// /// ## Arguments /// * `line` - The string to find items within /// * `items` - A slice of items, these are candidates for the search /// * `create_intermediate` - A `Fn` that: /// * accepts: +/// * `stride: usize`: The stride of the matching section /// * `item: &Item`: The matching item /// * and produces an `Intermediate` -/// * `create_output` - a `FnOnce` that: -/// * accepts: -/// * `start: usize` - The positon immediately after the prefix -/// * `intermediates: Vec` - A list of intermediates -/// * and produces the final output of the function (which is then wrapped in an Option, see the Returns subheader) /// /// ## Returns -/// Returns `None` if there are no possible matches inside the input string. There are two reasons this could occur: +/// Returns `None` if there are no possible matches inside the input stringThere are two reasons this could occur: /// * There is no prefix in the string, and thus no matches are possible /// * There is a prefix in the string, but none of the items could possibly complete the identifier +/// +/// Otherwise: Returns a vector of intermediates pub(super) fn find_items( line: &str, items: &[Item], From 27b7dddc6993d5033b0e0a9ffc94c5dde15a38c7 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:35:08 -0400 Subject: [PATCH 28/34] simplify --- src/cli/editor/finder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 4890b9b..4b70118 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -64,7 +64,7 @@ where let matches: Vec = items .iter() - .filter(|it| it.name().starts_with(line) && it.name() != line) + .filter(|it| it.name().starts_with(line)) .map(|it| create_intermediate(stride, it)) .collect(); if !matches.is_empty() { From 08ead55ecf6992f119018a8d7f057bef53ef64c5 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:35:54 -0400 Subject: [PATCH 29/34] remove deadcode --- src/cli/editor/validator.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/cli/editor/validator.rs b/src/cli/editor/validator.rs index b4d0312..8ad5665 100644 --- a/src/cli/editor/validator.rs +++ b/src/cli/editor/validator.rs @@ -7,23 +7,10 @@ impl Validator for MyHelper<'_> { &self, _ctx: &mut rustyline::validate::ValidationContext, ) -> rustyline::Result { - // let line = ctx.input(); - // let context = EvaluationContext::default(); - - // // dbg!(line); - - // let (valid_, result) = if tokenize(line, &context).is_err() { - // (false, ValidationResult::Incomplete) - // } else { - // (true, ValidationResult::Valid(None)) - // }; - // *self.valid.borrow_mut() = valid_; - // rustyline::Result::Ok(result) Ok(rustyline::validate::ValidationResult::Valid(None)) } fn validate_while_typing(&self) -> bool { - // true false } } From 7e353e77b66421ed105aa039bf97e6e2820a587d Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:36:31 -0400 Subject: [PATCH 30/34] remove deadcode --- src/cli/editor/highlighter.rs | 9 ++------- src/cli/editor/mod.rs | 6 +----- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs index 8f528f0..c51a956 100644 --- a/src/cli/editor/highlighter.rs +++ b/src/cli/editor/highlighter.rs @@ -14,13 +14,8 @@ impl Highlighter for MyHelper<'_> { &'s self, prompt: &'p str, _default: bool, - ) -> std::borrow::Cow<'b, str> { - let valid = *self.valid.borrow(); - if valid { - Cow::Borrowed(prompt) - } else { - Cow::Owned(prompt.red().to_string()) - } + ) -> std::borrow::Cow<'b, str> { + Cow::Borrowed(prompt) } fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index f692ac6..c5a3751 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -18,8 +18,6 @@ pub fn editor<'a>( let helper = MyHelper { funcs, vars, - - valid: RefCell::new(true), }; editor.set_helper(Some(helper)); @@ -31,9 +29,7 @@ pub fn editor<'a>( pub struct MyHelper<'cell> { pub funcs: &'cell RefCell>, - pub vars: &'cell RefCell>, - - pub valid: RefCell, + pub vars: &'cell RefCell> } impl Helper for MyHelper<'_> {} From 6698d8126775b8fd56364c3353337a302fad5abc Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:36:40 -0400 Subject: [PATCH 31/34] lint --- src/cli/editor/finder.rs | 2 +- src/cli/editor/highlighter.rs | 2 +- src/cli/editor/mod.rs | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 4b70118..a3b9312 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -50,7 +50,7 @@ pub fn find_last(c: char, str: &str) -> Option { pub(super) fn find_items( line: &str, items: &[Item], - create_intermediate: ToIntermediate + create_intermediate: ToIntermediate, ) -> Option> where Item: Findable, diff --git a/src/cli/editor/highlighter.rs b/src/cli/editor/highlighter.rs index c51a956..890d70b 100644 --- a/src/cli/editor/highlighter.rs +++ b/src/cli/editor/highlighter.rs @@ -14,7 +14,7 @@ impl Highlighter for MyHelper<'_> { &'s self, prompt: &'p str, _default: bool, - ) -> std::borrow::Cow<'b, str> { + ) -> std::borrow::Cow<'b, str> { Cow::Borrowed(prompt) } diff --git a/src/cli/editor/mod.rs b/src/cli/editor/mod.rs index c5a3751..e108142 100644 --- a/src/cli/editor/mod.rs +++ b/src/cli/editor/mod.rs @@ -15,10 +15,7 @@ pub fn editor<'a>( ) -> Editor> { let mut editor = Editor::::new(); - let helper = MyHelper { - funcs, - vars, - }; + let helper = MyHelper { funcs, vars }; editor.set_helper(Some(helper)); @@ -29,7 +26,7 @@ pub fn editor<'a>( pub struct MyHelper<'cell> { pub funcs: &'cell RefCell>, - pub vars: &'cell RefCell> + pub vars: &'cell RefCell>, } impl Helper for MyHelper<'_> {} From 44e2101e6c4241c32759176cdd5d57f7bb36100f Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:49:35 -0400 Subject: [PATCH 32/34] don't have a cow, man! --- src/cli/editor/completer.rs | 2 +- src/cli/editor/finder.rs | 18 ++++++++++++++++++ src/cli/editor/hinter.rs | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/cli/editor/completer.rs b/src/cli/editor/completer.rs index 340c93c..a64d97b 100644 --- a/src/cli/editor/completer.rs +++ b/src/cli/editor/completer.rs @@ -7,7 +7,7 @@ use super::{ fn find_candidates(line: &str, items: &[Item]) -> Option> { let create_intermediate = |stride, item: &Item| { - let replacement = item.name()[stride..].to_string(); + let replacement = item.replacement()[stride..].to_string(); let display = item.format(); Pair { display, diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index a3b9312..55c371e 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -1,3 +1,5 @@ +use std::{borrow::Cow, fmt::format}; + use rustmatheval::model::{ functions::{Function, PREFIX as FUNCTION_PREFIX}, variables::{Variable, PREFIX as VARIABLE_PREFIX}, @@ -77,6 +79,7 @@ where /// Represents a type that can be found (using [`find_items`]) pub trait Findable { fn name(&self) -> &str; + fn replacement(&self) -> Cow<'_, str>; fn format(&self) -> String; fn prefix() -> char; } @@ -86,6 +89,17 @@ impl Findable for Function { &self.name } + fn replacement(&self) -> Cow<'_, str> { + let appendix = if self.arity() == 0 { + // If the function takes no arguments we can just open and close the parens + "()" + } else { + "(" + }; + let formatted = format!("{}{}", &self.name, appendix); + Cow::Owned(formatted) + } + fn format(&self) -> String { format_func_with_args(self) } @@ -100,6 +114,10 @@ impl Findable for Variable { &self.repr } + fn replacement(&self) -> Cow<'_, str> { + Cow::Borrowed(&self.repr) + } + fn format(&self) -> String { format_var_name(&self.repr).to_string() } diff --git a/src/cli/editor/hinter.rs b/src/cli/editor/hinter.rs index 69ae004..2652b39 100644 --- a/src/cli/editor/hinter.rs +++ b/src/cli/editor/hinter.rs @@ -6,7 +6,7 @@ use super::{ }; pub fn find_hint(line: &str, items: &[Item]) -> Option { - let create_intermediate = |stride, item: &Item| item.name()[stride..].to_string(); + let create_intermediate = |stride, item: &Item| item.replacement()[stride..].to_string(); let hints = find_items(line, items, create_intermediate); hints.and_then(|hints| hints.into_iter().max_by_key(String::len)) } From 0c8a3ee748a2fe460227300ee33e9cf106074ed1 Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:52:05 -0400 Subject: [PATCH 33/34] lint --- src/cli/editor/finder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/editor/finder.rs b/src/cli/editor/finder.rs index 55c371e..e4d9d5c 100644 --- a/src/cli/editor/finder.rs +++ b/src/cli/editor/finder.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::format}; +use std::borrow::Cow; use rustmatheval::model::{ functions::{Function, PREFIX as FUNCTION_PREFIX}, From 99400a0e11acfbb372fd67c0ee4119bdc07cdaca Mon Sep 17 00:00:00 2001 From: George Lewis Date: Mon, 14 Jun 2021 16:55:20 -0400 Subject: [PATCH 34/34] version bump: 1.5.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d6666c..834c788 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,7 +280,7 @@ dependencies = [ [[package]] name = "rustcalc" -version = "1.4.1" +version = "1.5.0" dependencies = [ "colored", "dirs", diff --git a/Cargo.toml b/Cargo.toml index b5cfc65..25154bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustcalc" -version = "1.4.1" +version = "1.5.0" authors = ["George-Lewis "] edition = "2018" description = "A command-line utility for evaluating mathematical statements"