2019-05-18 04:30:57 +02:00
|
|
|
use crate::shell::completer::NuCompleter;
|
2019-05-16 23:43:36 +02:00
|
|
|
|
2019-05-31 02:53:54 +02:00
|
|
|
use crate::parser::lexer::SpannedToken;
|
2019-05-26 08:54:41 +02:00
|
|
|
use crate::prelude::*;
|
2019-05-31 02:53:54 +02:00
|
|
|
use ansi_term::Color;
|
2019-06-01 19:00:42 +02:00
|
|
|
use log::trace;
|
2019-05-18 04:30:57 +02:00
|
|
|
use rustyline::completion::{self, Completer, FilenameCompleter};
|
2019-05-16 23:43:36 +02:00
|
|
|
use rustyline::error::ReadlineError;
|
2019-05-31 02:53:54 +02:00
|
|
|
use rustyline::highlight::Highlighter;
|
2019-05-16 23:43:36 +02:00
|
|
|
use rustyline::hint::{Hinter, HistoryHinter};
|
|
|
|
use std::borrow::Cow::{self, Owned};
|
|
|
|
|
|
|
|
crate struct Helper {
|
|
|
|
completer: NuCompleter,
|
|
|
|
hinter: HistoryHinter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Helper {
|
2019-05-26 08:54:41 +02:00
|
|
|
crate fn new(commands: indexmap::IndexMap<String, Arc<dyn Command>>) -> Helper {
|
2019-05-16 23:43:36 +02:00
|
|
|
Helper {
|
2019-05-18 04:30:57 +02:00
|
|
|
completer: NuCompleter {
|
|
|
|
file_completer: FilenameCompleter::new(),
|
2019-05-26 08:54:41 +02:00
|
|
|
commands,
|
2019-05-18 04:30:57 +02:00
|
|
|
},
|
2019-05-16 23:43:36 +02:00
|
|
|
hinter: HistoryHinter {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Completer for Helper {
|
2019-05-18 04:30:57 +02:00
|
|
|
type Candidate = completion::Pair;
|
2019-05-16 23:43:36 +02:00
|
|
|
|
|
|
|
fn complete(
|
|
|
|
&self,
|
|
|
|
line: &str,
|
|
|
|
pos: usize,
|
|
|
|
ctx: &rustyline::Context<'_>,
|
2019-05-18 04:30:57 +02:00
|
|
|
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
2019-05-16 23:43:36 +02:00
|
|
|
self.completer.complete(line, pos, ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Hinter for Helper {
|
|
|
|
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
|
|
|
self.hinter.hint(line, pos, ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Highlighter for Helper {
|
|
|
|
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
|
|
|
|
Owned("\x1b[32m".to_owned() + &prompt[0..prompt.len() - 2] + "\x1b[m> ")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
|
|
|
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
|
|
|
|
}
|
|
|
|
|
2019-05-31 02:53:54 +02:00
|
|
|
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
|
|
|
let tokens = crate::parser::lexer::Lexer::new(line, true);
|
|
|
|
let tokens: Result<Vec<(usize, SpannedToken, usize)>, _> = 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);
|
|
|
|
|
2019-06-01 19:00:42 +02:00
|
|
|
trace!("token={:?}", token);
|
|
|
|
trace!("style={:?}", style);
|
|
|
|
trace!("new_state={:?}", new_state);
|
2019-05-31 02:53:54 +02:00
|
|
|
|
|
|
|
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 {
|
|
|
|
true
|
2019-05-16 23:43:36 +02:00
|
|
|
}
|
2019-05-31 02:53:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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),
|
|
|
|
|
2019-05-31 18:33:46 +02:00
|
|
|
(State::Bare, PathDot) => (Color::Green.normal(), State::Bare),
|
2019-05-31 02:53:54 +02:00
|
|
|
(State::Bare, Member) => (Color::Green.normal(), State::Bare),
|
2019-05-16 23:43:36 +02:00
|
|
|
|
2019-05-31 02:53:54 +02:00
|
|
|
(_, 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),
|
2019-05-16 23:43:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl rustyline::Helper for Helper {}
|