diff --git a/src/parser.rs b/src/parser.rs index 7fdb19be66..a009cb0d7a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,7 +14,7 @@ use parser::PipelineParser; pub fn parse(input: &str, _registry: &dyn CommandRegistry) -> Result { let parser = PipelineParser::new(); - let tokens = Lexer::new(input); + let tokens = Lexer::new(input, false); match parser.parse(tokens) { Ok(val) => Ok(val), diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 7c056a84a7..90ff7095d6 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -375,14 +375,15 @@ pub enum Token { crate struct Lexer<'source> { lexer: logos::Lexer, first: bool, - // state: LexerState, + whitespace: bool, // state: LexerState } impl Lexer<'source> { - crate fn new(source: &str) -> Lexer<'_> { + crate fn new(source: &str, whitespace: bool) -> Lexer<'_> { Lexer { first: true, lexer: logos::Logos::lexer(source), + whitespace // state: LexerState::default(), } } @@ -400,7 +401,7 @@ impl Iterator for Lexer<'source> { TopToken::Error => { return Some(Err(lex_error(&self.lexer.range(), self.lexer.source))) } - TopToken::Whitespace => return self.next(), + TopToken::Whitespace if !self.whitespace => return self.next(), other => { return spanned(other.to_token()?, self.lexer.slice(), &self.lexer.range()) } @@ -415,7 +416,7 @@ impl Iterator for Lexer<'source> { match token { TopToken::Error => return Some(Err(lex_error(&range, self.lexer.source))), - TopToken::Whitespace => return self.next(), + TopToken::Whitespace if !self.whitespace => return self.next(), other => return spanned(other.to_token()?, slice, &range), } } @@ -429,7 +430,7 @@ impl Iterator for Lexer<'source> { AfterMemberDot::Error => { return Some(Err(lex_error(&range, self.lexer.source))) } - AfterMemberDot::Whitespace => self.next(), + AfterMemberDot::Whitespace if !self.whitespace => self.next(), other => return spanned(other.to_token()?, slice, &range), } } @@ -443,7 +444,7 @@ impl Iterator for Lexer<'source> { AfterVariableToken::Error => { return Some(Err(lex_error(&range, self.lexer.source))) } - AfterVariableToken::Whitespace => self.next(), + AfterVariableToken::Whitespace if !self.whitespace => self.next(), other => return spanned(other.to_token()?, slice, &range), } @@ -508,7 +509,7 @@ mod tests { use pretty_assertions::assert_eq; fn assert_lex(source: &str, tokens: &[TestToken<'_>]) { - let lex = Lexer::new(source); + let lex = Lexer::new(source, false); let mut current = 0; let expected_tokens: Vec = tokens diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 23fe3435eb..116a31d56a 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -1,15 +1,17 @@ use crate::shell::completer::NuCompleter; +use crate::parser::lexer::SpannedToken; use crate::prelude::*; +use ansi_term::Color; +use log::debug; use rustyline::completion::{self, Completer, FilenameCompleter}; use rustyline::error::ReadlineError; -use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; +use rustyline::highlight::Highlighter; use rustyline::hint::{Hinter, HistoryHinter}; use std::borrow::Cow::{self, Owned}; crate struct Helper { completer: NuCompleter, - highlighter: MatchingBracketHighlighter, hinter: HistoryHinter, } @@ -20,7 +22,6 @@ impl Helper { file_completer: FilenameCompleter::new(), commands, }, - highlighter: MatchingBracketHighlighter::new(), hinter: HistoryHinter {}, } } @@ -54,12 +55,78 @@ impl Highlighter for Helper { Owned("\x1b[1m".to_owned() + hint + "\x1b[m") } - fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { - self.highlighter.highlight(line, pos) + fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { + let tokens = crate::parser::lexer::Lexer::new(line, true); + let tokens: Result, _> = tokens.collect(); + + match tokens { + Err(_) => Cow::Borrowed(line), + Ok(v) => { + let mut out = String::new(); + let mut iter = v.iter(); + + let mut state = State::Command; + + loop { + match iter.next() { + None => return Cow::Owned(out), + Some((start, token, end)) => { + let (style, new_state) = token_style(&token, state); + + debug!("token={:?}", token); + debug!("style={:?}", style); + debug!("new_state={:?}", new_state); + + state = new_state; + let slice = &line[*start..*end]; + let styled = style.paint(slice); + out.push_str(&styled.to_string()); + } + } + } + } + } } - fn highlight_char(&self, line: &str, pos: usize) -> bool { - self.highlighter.highlight_char(line, pos) + fn highlight_char(&self, _line: &str, _pos: usize) -> bool { + true + } +} + +#[derive(Debug)] +enum State { + Command, + Flag, + Var, + Bare, + None, +} + +fn token_style( + token: &crate::parser::lexer::SpannedToken, + state: State, +) -> (ansi_term::Style, State) { + use crate::parser::lexer::Token::*; + + match (state, &token.token) { + (State::Command, Bare) => (Color::Cyan.bold(), State::None), + (State::Command, Whitespace) => (Color::White.normal(), State::Command), + + (State::Flag, Bare) => (Color::Black.bold(), State::None), + + (State::Var, Variable) => (Color::Yellow.bold(), State::None), + + (State::Bare, Dot) => (Color::Green.normal(), State::Bare), + (State::Bare, Member) => (Color::Green.normal(), State::Bare), + + (_, Dash) | (_, DashDash) => (Color::Black.bold(), State::Flag), + (_, Dollar) => (Color::Yellow.bold(), State::Var), + (_, Bare) => (Color::Green.normal(), State::Bare), + (_, Member) => (Color::Cyan.normal(), State::None), + (_, Num) => (Color::Purple.bold(), State::None), + (_, DQString) | (_, SQString) => (Color::Green.normal(), State::None), + (_, Pipe) => (Color::White.normal(), State::Command), + _ => (Color::White.normal(), State::None), } }