forked from extern/nushell
e4226def16
This commit extracts five new crates: - nu-source, which contains the core source-code handling logic in Nu, including Text, Span, and also the pretty.rs-based debug logic - nu-parser, which is the parser and expander logic - nu-protocol, which is the bulk of the types and basic conveniences used by plugins - nu-errors, which contains ShellError, ParseError and error handling conveniences - nu-textview, which is the textview plugin extracted into a crate One of the major consequences of this refactor is that it's no longer possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so a lot of types became more concrete (Value became a concrete type instead of Spanned<Value>, for example). This also turned a number of inherent methods in the main nu crate into plain functions (impl Value {} became a bunch of functions in the `value` namespace in `crate::data::value`).
181 lines
5.8 KiB
Rust
181 lines
5.8 KiB
Rust
use crate::context::Context;
|
|
use ansi_term::Color;
|
|
use log::{log_enabled, trace};
|
|
use nu_parser::hir::syntax_shape::color_fallible_syntax;
|
|
use nu_parser::{FlatShape, PipelineShape, TokenNode, TokensIterator};
|
|
use nu_protocol::outln;
|
|
use nu_source::{nom_input, HasSpan, Spanned, Tag, Tagged, Text};
|
|
use rustyline::completion::Completer;
|
|
use rustyline::error::ReadlineError;
|
|
use rustyline::highlight::Highlighter;
|
|
use rustyline::hint::Hinter;
|
|
use std::borrow::Cow::{self, Owned};
|
|
|
|
pub(crate) struct Helper {
|
|
context: Context,
|
|
pub colored_prompt: String,
|
|
}
|
|
|
|
impl Helper {
|
|
pub(crate) fn new(context: Context) -> Helper {
|
|
Helper {
|
|
context,
|
|
colored_prompt: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Completer for Helper {
|
|
type Candidate = rustyline::completion::Pair;
|
|
fn complete(
|
|
&self,
|
|
line: &str,
|
|
pos: usize,
|
|
ctx: &rustyline::Context<'_>,
|
|
) -> Result<(usize, Vec<rustyline::completion::Pair>), ReadlineError> {
|
|
self.context.shell_manager.complete(line, pos, ctx)
|
|
}
|
|
}
|
|
|
|
impl Hinter for Helper {
|
|
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
|
self.context.shell_manager.hint(line, pos, ctx)
|
|
}
|
|
}
|
|
|
|
impl Highlighter for Helper {
|
|
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
|
|
&'s self,
|
|
prompt: &'p str,
|
|
default: bool,
|
|
) -> Cow<'b, str> {
|
|
use std::borrow::Cow::Borrowed;
|
|
|
|
if default {
|
|
Borrowed(&self.colored_prompt)
|
|
} else {
|
|
Borrowed(prompt)
|
|
}
|
|
}
|
|
|
|
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
|
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
|
|
}
|
|
|
|
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
|
let tokens = nu_parser::pipeline(nom_input(line));
|
|
|
|
match tokens {
|
|
Err(_) => Cow::Borrowed(line),
|
|
Ok((_rest, v)) => {
|
|
let mut out = String::new();
|
|
let pipeline = match v.as_pipeline() {
|
|
Err(_) => return Cow::Borrowed(line),
|
|
Ok(v) => v,
|
|
};
|
|
|
|
let tokens = vec![TokenNode::Pipeline(pipeline.clone())];
|
|
let mut tokens = TokensIterator::all(&tokens[..], Text::from(line), v.span());
|
|
|
|
let text = Text::from(line);
|
|
let expand_context = self.context.expand_context(&text);
|
|
|
|
#[cfg(not(coloring_in_tokens))]
|
|
let shapes = {
|
|
let mut shapes = vec![];
|
|
color_fallible_syntax(
|
|
&PipelineShape,
|
|
&mut tokens,
|
|
&expand_context,
|
|
&mut shapes,
|
|
)
|
|
.unwrap();
|
|
shapes
|
|
};
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
let shapes = {
|
|
// We just constructed a token list that only contains a pipeline, so it can't fail
|
|
color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context).unwrap();
|
|
tokens.with_color_tracer(|_, tracer| tracer.finish());
|
|
|
|
tokens.state().shapes()
|
|
};
|
|
|
|
trace!(target: "nu::color_syntax", "{:#?}", tokens.color_tracer());
|
|
|
|
if log_enabled!(target: "nu::color_syntax", log::Level::Debug) {
|
|
outln!("");
|
|
ptree::print_tree(&tokens.color_tracer().clone().print(Text::from(line)))
|
|
.unwrap();
|
|
outln!("");
|
|
}
|
|
|
|
for shape in shapes {
|
|
let styled = paint_flat_shape(&shape, line);
|
|
out.push_str(&styled);
|
|
}
|
|
|
|
Cow::Owned(out)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
fn vec_tag<T>(input: Vec<Tagged<T>>) -> Option<Tag> {
|
|
let mut iter = input.iter();
|
|
let first = iter.next()?.tag.clone();
|
|
let last = iter.last();
|
|
|
|
Some(match last {
|
|
None => first,
|
|
Some(last) => first.until(&last.tag),
|
|
})
|
|
}
|
|
|
|
fn paint_flat_shape(flat_shape: &Spanned<FlatShape>, line: &str) -> String {
|
|
let style = match &flat_shape.item {
|
|
FlatShape::OpenDelimiter(_) => Color::White.normal(),
|
|
FlatShape::CloseDelimiter(_) => Color::White.normal(),
|
|
FlatShape::ItVariable => Color::Purple.bold(),
|
|
FlatShape::Variable => Color::Purple.normal(),
|
|
FlatShape::Operator => Color::Yellow.normal(),
|
|
FlatShape::Dot => Color::White.normal(),
|
|
FlatShape::InternalCommand => Color::Cyan.bold(),
|
|
FlatShape::ExternalCommand => Color::Cyan.normal(),
|
|
FlatShape::ExternalWord => Color::Black.bold(),
|
|
FlatShape::BareMember => Color::Yellow.bold(),
|
|
FlatShape::StringMember => Color::Yellow.bold(),
|
|
FlatShape::String => Color::Green.normal(),
|
|
FlatShape::Path => Color::Cyan.normal(),
|
|
FlatShape::GlobPattern => Color::Cyan.bold(),
|
|
FlatShape::Word => Color::Green.normal(),
|
|
FlatShape::Pipe => Color::Purple.bold(),
|
|
FlatShape::Flag => Color::Black.bold(),
|
|
FlatShape::ShorthandFlag => Color::Black.bold(),
|
|
FlatShape::Int => Color::Purple.bold(),
|
|
FlatShape::Decimal => Color::Purple.bold(),
|
|
FlatShape::Whitespace => Color::White.normal(),
|
|
FlatShape::Error => Color::Red.bold(),
|
|
FlatShape::Size { number, unit } => {
|
|
let number = number.slice(line);
|
|
let unit = unit.slice(line);
|
|
return format!(
|
|
"{}{}",
|
|
Color::Purple.bold().paint(number),
|
|
Color::Cyan.bold().paint(unit)
|
|
);
|
|
}
|
|
};
|
|
|
|
let body = flat_shape.span.slice(line);
|
|
style.paint(body).to_string()
|
|
}
|
|
|
|
impl rustyline::Helper for Helper {}
|