diff --git a/src/context.rs b/src/context.rs index b1fef92..484d61b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -19,7 +19,7 @@ pub struct Config { // with prettier unicode alternatives? // // Automatically disabled if enable_unicode is off. - //pub enable_substituion: bool, + pub enable_substituion: bool, // Should we print simple powers // as unicode superscript chars? @@ -38,7 +38,7 @@ impl Config { pub fn new() -> Config { Config{ term_color_type: 2, - //enable_substituion: true, + enable_substituion: true, //enable_unicode: true, enable_super_powers: true, enable_one_over_power: true diff --git a/src/entrypoint/unix/promptbuffer.rs b/src/entrypoint/unix/promptbuffer.rs index 2b711ab..7077bca 100644 --- a/src/entrypoint/unix/promptbuffer.rs +++ b/src/entrypoint/unix/promptbuffer.rs @@ -38,39 +38,23 @@ impl PromptBuffer { // Same as write_primpt, but pretends there is no cursor pub fn write_prompt_nocursor(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - // Draw prettyprinted expression - let (_, s) = substitute_cursor(context, &self.get_contents(), self.buffer.chars().count()); - - write!( - stdout, "\r{}{}==>{}{} {}", - style::Bold, - color::Fg(color::Blue), - color::Fg(color::Reset), - style::Reset, - s - )?; - - // If this string is shorter, clear the remaining old one. - if s.chars().count() < self.last_print_len { - write!( - stdout, "{}{}", - " ".repeat(self.last_print_len - s.chars().count()), - termion::cursor::Left((self.last_print_len - s.chars().count()) as u16) - )?; - } - - self.last_print_len = s.chars().count(); - stdout.flush()?; - return Ok(()); + let tmp = self.cursor; + self.cursor = 0; + let r = self.write_prompt(context, stdout); + self.cursor = tmp; + return r; } pub fn write_prompt(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { let l = self.buffer.chars().count(); let i = if l == 0 {0} else {l - self.cursor}; - // Draw prettyprinted expression - let (display_cursor, s) = substitute_cursor(context, &self.get_contents(), i); + //println!("{i} {}", self.cursor); + + // Draw prettyprinted expression + let (display_c, s) = substitute_cursor(context, &self.get_contents(), i); + write!( stdout, "\r{}{}==>{}{} {}", style::Bold, @@ -82,22 +66,18 @@ impl PromptBuffer { // If this string is shorter, clear the remaining old one. if s.chars().count() < self.last_print_len { - write!( - stdout, "{}{}", - " ".repeat(self.last_print_len - s.chars().count()), - termion::cursor::Left((self.last_print_len - s.chars().count()) as u16) - )?; - } - - - // Move cursor to correct position - if display_cursor != 0 { write!( stdout, "{}", - termion::cursor::Left(display_cursor as u16) + " ".repeat(self.last_print_len - s.chars().count()), )?; - stdout.flush()?; } + + + write!( + stdout, "\r{}", + termion::cursor::Right((display_c + 4) as u16) + )?; + stdout.flush()?; self.last_print_len = s.chars().count(); stdout.flush()?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 32c608d..56f4eef 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -21,8 +21,10 @@ pub fn parse( context: &Context, s: &String ) -> Result { - let expressions = stage::tokenize(context, s); - let (_, expressions) = stage::find_subs(expressions); + let mut expressions = stage::tokenize(context, s); + if context.config.enable_substituion { + (_, expressions) = stage::find_subs(expressions); + } let g = stage::groupify(context, expressions)?; let g = stage::treeify(context, g)?; @@ -34,6 +36,7 @@ pub fn parse_no_context(s: &String) -> Result String { + if !context.config.enable_substituion { return s.clone(); } let (_, s) = substitute_cursor(context, s, s.chars().count()); return s; } @@ -43,34 +46,45 @@ pub fn substitute_cursor( s: &String, // The string to substitute c: usize // Location of the cursor right now ) -> ( - usize, // Location of cursor in substituted string + usize, // New cursor String // String with substitutions ) { + + if !context.config.enable_substituion { return (c, s.clone()); } if s == "" { return (c, s.clone()) } + + let mut new_s = s.clone(); - let l = s.chars().count(); let expressions = stage::tokenize(context, s); let (mut subs, _) = stage::find_subs(expressions); - let mut new_c = l - c; + let mut new_c = c.clone(); while subs.len() > 0 { - let r = subs.pop_back().unwrap(); // Apply substitutions in reverse order + // r is the current substitution: (linelocation, string) + let r = subs.pop_back().unwrap(); if { // Don't substitute if our cursor is inside the substitution c >= r.0.pos && c < r.0.pos+r.0.len } { continue; } - if c < r.0.pos { - let ct = r.1.chars().count(); - if ct >= r.0.len { - if new_c >= ct - r.0.len { - new_c += ct - r.0.len - } - } else { - new_c -= r.0.len - ct + // If this substitution is before our cursor, + // we need to adjust our cursor's position. + if c > r.0.pos { + let c_o = r.0.len; // Old length + let c_n = r.1.chars().count(); // New length + + if c_n > c_o { + // Move cursor right by difference + new_c += c_n - c_o; + + } else if c_n < c_o { + // Move cursor left by difference + if new_c >= c_o - c_n { + new_c -= c_o - c_n; + } else { new_c = 0; } } } diff --git a/src/parser/stage/find_subs.rs b/src/parser/stage/find_subs.rs index 058cf54..865f407 100644 --- a/src/parser/stage/find_subs.rs +++ b/src/parser/stage/find_subs.rs @@ -6,6 +6,64 @@ use super::super::{ }; +fn sub_string(s: &str) -> Option<&'static str> { + let r = match s { + + /* Only found in operator tokens */ + + "*" => "×", + "/" => "÷", + "sqrt" => "√", + "rt" => "√", + + + + /* Only found in word tokens */ + + // Greek letters + "alpha" => "α", + "beta" => "β", + "gamma" => "γ", + "delta" => "δ", + "epsilon" => "ε", + "zeta" => "ζ", + "eta" => "η", + "theta" => "θ", + //"iota" => {Some("ι")}, // looks just like i + //"kappa" => {Some("κ")}, // looks just like k + "lambda" => "λ", + "mu" => "μ", + //"nu" => {Some("ν")}, // looks just like v + "xi" => "ξ", + //"omicron" => {Some("ο")}, // looks exactly like o + "pi" => "π", + "rho" => "ρ", + "sigma" => "σ", + "tau" => "τ", + //"upsilon" => {Some("υ")}, // looks just like u + "phi" => "φ", + "chi" => "χ", + //"psi" => {Some("ψ")}, Conflict with pound / square inch + "omega" => "ω", + + // Constants + "epsilon_zero" => "ε₀", + "eps_zero" => "ε₀", + "g_zero" => "g₀", + "mu_zero" => "μ₀", + "h_bar" => "ℏ", + + // Misc + "deg" => "°", + + _ => { return None; } + }; + return Some(r); +} + + + + pub fn find_subs( mut g: VecDeque, ) -> ( @@ -24,62 +82,19 @@ pub fn find_subs( while g.len() > 0 { let mut t = g.pop_front().unwrap(); + let target: Option<&str> = match &mut t { Token::Operator(_, s) => { - let target = match &s[..] { - "*" => {Some("×")}, - "/" => {Some("÷")}, - "sqrt" => {Some("√")}, - "rt" => {Some("√")}, - _ => {None} - }; + let target = sub_string(s); // Update token contents too. - // This makes sure that errors also contain the updated text. + // This makes errors and printouts use the updated string. if target.is_some() { *s = String::from(target.unwrap()); } target }, Token::Word(_, s) => { - let target = match &s[..] { - // Greek letters - "alpha" => {Some("α")}, - "beta" => {Some("β")}, - "gamma" => {Some("γ")}, - "delta" => {Some("δ")}, - "epsilon" => {Some("ε")}, - "zeta" => {Some("ζ")}, - "eta" => {Some("η")}, - "theta" => {Some("θ")}, - //"iota" => {Some("ι")}, - //"kappa" => {Some("κ")}, - "lambda" => {Some("λ")}, - "mu" => {Some("μ")}, - //"nu" => {Some("ν")}, - "xi" => {Some("ξ")}, - //"omicron" => {Some("ο")}, - "pi" => {Some("π")}, - "rho" => {Some("ρ")}, - "sigma" => {Some("σ")}, - "tau" => {Some("τ")}, - //"upsilon" => {Some("υ")}, - "phi" => {Some("φ")}, - "chi" => {Some("χ")}, - //"psi" => {Some("ψ")}, Conflict with pound / square inch - "omega" => {Some("ω")}, - - // Constants - "epsilon_zero" => {Some("ε₀")}, - "eps_zero" => {Some("ε₀")}, - "g_zero" => {Some("g₀")}, - "mu_zero" => {Some("μ₀")}, - "h_bar" => {Some("ℏ")}, - - // Misc - "deg" => {Some("°")} - _ => {None} - }; - + let target = sub_string(s); if target.is_some() { *s = String::from(target.unwrap()); } target },