Update rustyline to latest (#2565)

* Update rustyline to latest

* Go ahead and use rustyline for testing
This commit is contained in:
Jonathan Turner 2020-09-17 18:02:30 +12:00 committed by GitHub
parent 1dc8f3300e
commit 8453261211
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 372 additions and 313 deletions

View file

@ -71,7 +71,7 @@ steps:
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used
condition: eq(variables['style'], 'canary')
displayName: Check clippy lints
- bash: RUSTFLAGS="-D warnings" cargo test --all --no-default-features
- bash: RUSTFLAGS="-D warnings" cargo test --all --no-default-features --features=rustyline-support
condition: eq(variables['style'], 'minimal')
displayName: Run tests
- bash: RUSTFLAGS="-D warnings" cargo test --all --features=extra

24
Cargo.lock generated
View file

@ -987,7 +987,7 @@ version = "3.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b676fa23f995faf587496dcd1c80fead847ed58d2da52ac1caca9a72790dd2"
dependencies = [
"nix",
"nix 0.17.0",
"winapi 0.3.9",
]
@ -1834,7 +1834,7 @@ dependencies = [
"lazy_static 1.4.0",
"libc",
"mach",
"nix",
"nix 0.17.0",
"pin-utils",
"uom 0.28.0",
"winapi 0.3.9",
@ -1921,7 +1921,7 @@ dependencies = [
"heim-runtime",
"libc",
"macaddr",
"nix",
"nix 0.17.0",
]
[[package]]
@ -2796,6 +2796,18 @@ dependencies = [
"void",
]
[[package]]
name = "nix"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"libc",
]
[[package]]
name = "nodrop"
version = "0.1.14"
@ -4503,16 +4515,16 @@ dependencies = [
[[package]]
name = "rustyline"
version = "6.2.0"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777"
checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
dependencies = [
"cfg-if",
"dirs-next",
"libc",
"log 0.4.11",
"memchr",
"nix",
"nix 0.18.0",
"scopeguard",
"unicode-segmentation",
"unicode-width",

View file

@ -75,6 +75,7 @@ default = [
"ptree-support",
"term-support",
"uuid-support",
"rustyline-support",
"match",
"post",
"fetch",
@ -105,6 +106,7 @@ ctrlc-support = ["nu-cli/ctrlc"]
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
git-support = ["nu-cli/git2"]
ptree-support = ["nu-cli/ptree"]
rustyline-support = ["nu-cli/rustyline-support"]
term-support = ["nu-cli/term"]
trash-support = ["nu-cli/trash-support"]
uuid-support = ["nu-cli/uuid_crate"]

View file

@ -67,7 +67,7 @@ rand = "0.7.3"
regex = "1.3.9"
roxmltree = "0.13.0"
rust-embed = "5.6.0"
rustyline = "6.2.0"
rustyline = {version = "6.3.0", optional = true}
serde = {version = "1.0.115", features = ["derive"]}
serde-hjson = "0.9.1"
serde_bytes = "0.11.5"
@ -88,17 +88,17 @@ uuid_crate = { package = "uuid", version = "0.8.1", features = ["v4"], optional
which = {version = "4.0.2", optional = true}
zip = {version = "0.5.7", optional = true}
Inflector = "0.11"
clipboard = {version = "0.5.0", optional = true}
encoding_rs = "0.8.24"
quick-xml = "0.18.1"
rayon = "1.4.0"
trash = {version = "1.1.1", optional = true}
url = "2.1.1"
Inflector = "0.11"
[target.'cfg(unix)'.dependencies]
users = "0.10.0"
umask = "1.0.0"
users = "0.10.0"
# TODO this will be possible with new dependency resolver
# (currently on nightly behind -Zfeatures=itarget):
@ -114,12 +114,12 @@ version = "0.23.1"
[build-dependencies]
git2 = {version = "0.13.11", optional = true}
[dev-dependencies]
quickcheck = "0.9.2"
quickcheck_macros = "0.9.1"
[features]
clipboard-cli = ["clipboard"]
rustyline-support = ["rustyline"]
stable = []
trash-support = ["trash"]

View file

@ -1,9 +1,9 @@
use crate::commands::classified::block::run_block;
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
use crate::context::Context;
use crate::git::current_branch;
use crate::path::canonicalize;
use crate::prelude::*;
#[cfg(feature = "rustyline-support")]
use crate::shell::Helper;
use crate::EnvironmentSyncer;
use futures_codec::FramedRead;
@ -12,9 +12,14 @@ use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal,
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
use log::{debug, trace};
use rustyline::config::{ColorMode, CompletionType, Config};
use rustyline::error::ReadlineError;
use rustyline::{self, config::Configurer, At, Cmd, Editor, KeyPress, Movement, Word};
#[cfg(feature = "rustyline-support")]
use rustyline::{
self,
config::Configurer,
config::{ColorMode, CompletionType, Config},
error::ReadlineError,
At, Cmd, Editor, KeyPress, Movement, Word,
};
use std::error::Error;
use std::iter::Iterator;
use std::path::{Path, PathBuf};
@ -305,7 +310,21 @@ pub async fn run_vec_of_pipelines(
Ok(())
}
#[cfg(feature = "rustyline-support")]
fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>) -> LineResult {
match input {
Ok(s) => LineResult::Success(s),
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
Err(ReadlineError::Eof) => LineResult::Break,
Err(err) => {
outln!("Error: {:?}", err);
LineResult::Break
}
}
}
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
#[cfg(feature = "rustyline-support")]
pub async fn cli(mut context: Context) -> Result<(), Box<dyn Error>> {
let mut syncer = EnvironmentSyncer::new();
let configuration = syncer.get_config();
@ -417,6 +436,7 @@ pub async fn cli(mut context: Context) -> Result<(), Box<dyn Error>> {
}
}
} else {
use crate::git::current_branch;
format!(
"\x1b[32m{}{}\x1b[m> ",
cwd,
@ -444,7 +464,10 @@ pub async fn cli(mut context: Context) -> Result<(), Box<dyn Error>> {
initial_command = None;
}
let line = process_line(readline, &mut context, false, true).await;
let line = match convert_rustyline_result_to_string(readline) {
LineResult::Success(s) => process_line(&s, &mut context, false, true).await,
x => x,
};
// Check the config to see if we need to update the path
// TODO: make sure config is cached so we don't path this load every call
@ -579,7 +602,7 @@ pub async fn run_pipeline_standalone(
context: &mut Context,
exit_on_error: bool,
) -> Result<(), Box<dyn Error>> {
let line = process_line(Ok(pipeline), context, redirect_stdin, false).await;
let line = process_line(&pipeline, context, redirect_stdin, false).await;
match line {
LineResult::Success(line) => {
@ -617,6 +640,7 @@ pub async fn run_pipeline_standalone(
Ok(())
}
#[cfg(feature = "rustyline-support")]
fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(windows)]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
@ -657,6 +681,7 @@ fn default_rustyline_editor_configuration() -> Editor<Helper> {
rl
}
#[cfg(feature = "rustyline-support")]
fn configure_rustyline_editor(
rl: &mut Editor<Helper>,
config: &dyn nu_data::config::Conf,
@ -771,6 +796,7 @@ fn configure_rustyline_editor(
Ok(())
}
#[cfg(feature = "rustyline-support")]
fn nu_line_editor_helper(
context: &mut Context,
config: &dyn nu_data::config::Conf,
@ -779,6 +805,7 @@ fn nu_line_editor_helper(
crate::shell::Helper::new(context.clone(), hinter)
}
#[cfg(feature = "rustyline-support")]
fn rustyline_hinter(config: &dyn nu_data::config::Conf) -> Option<rustyline::hint::HistoryHinter> {
if let Some(line_editor_vars) = config.var("line_editor") {
for (idx, value) in line_editor_vars.row_entries() {
@ -839,15 +866,14 @@ pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result<String, She
/// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline
pub async fn process_line(
readline: Result<String, ReadlineError>,
line: &str,
ctx: &mut Context,
redirect_stdin: bool,
cli_mode: bool,
) -> LineResult {
match &readline {
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
Ok(line) => {
if line.trim() == "" {
LineResult::Success(line.to_string())
} else {
let line = chomp_newline(line);
ctx.raw_input = line.to_string();
@ -1035,13 +1061,6 @@ pub async fn process_line(
Err(err) => LineResult::Error(line.to_string(), err),
}
}
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
Err(ReadlineError::Eof) => LineResult::Break,
Err(err) => {
outln!("Error: {:?}", err);
LineResult::Break
}
}
}
pub fn print_err(err: ShellError, source: &Text) {

View file

@ -213,7 +213,7 @@ pub fn get_documentation(
long_desc.push_str(&format!("\n > {}\n", example.example));
} else {
let colored_example =
crate::shell::helper::Painter::paint_string(example.example, registry, &palette);
crate::shell::painter::Painter::paint_string(example.example, registry, &palette);
long_desc.push_str(&format!("\n > {}\n", colored_example));
}
}

View file

@ -41,11 +41,14 @@ pub fn nu(env: &IndexMap<String, String>, tag: impl Into<Tag>) -> Result<Value,
let config = nu_data::config::default_path()?;
nu_dict.insert_value("config-path", UntaggedValue::path(config).into_value(&tag));
#[cfg(feature = "rustyline-support")]
{
let keybinding_path = crate::keybinding::keybinding_path()?;
nu_dict.insert_value(
"keybinding-path",
UntaggedValue::path(keybinding_path).into_value(&tag),
);
}
let config: Box<dyn nu_data::config::Conf> = Box::new(nu_data::config::NuConfig::new());
let history = crate::commands::history::history_path(&config);

View file

@ -15,6 +15,7 @@ extern crate quickcheck_macros;
mod cli;
mod commands;
#[cfg(feature = "rustyline-support")]
mod completion;
mod context;
mod deserializer;
@ -23,7 +24,9 @@ mod env;
mod evaluate;
mod format;
mod futures;
#[cfg(feature = "rustyline-support")]
mod git;
#[cfg(feature = "rustyline-support")]
mod keybinding;
mod path;
mod plugin;
@ -34,8 +37,11 @@ pub mod utils;
#[cfg(test)]
mod examples;
#[cfg(feature = "rustyline-support")]
pub use crate::cli::cli;
pub use crate::cli::{
cli, create_default_context, parse_and_eval, process_line, register_plugins,
create_default_context, parse_and_eval, process_line, register_plugins,
run_pipeline_standalone, run_vec_of_pipelines, LineResult,
};
pub use crate::commands::command::{

View file

@ -1,12 +1,16 @@
#![allow(clippy::module_inception)]
#[cfg(feature = "rustyline-support")]
pub(crate) mod completer;
pub(crate) mod filesystem_shell;
pub(crate) mod help_shell;
#[cfg(feature = "rustyline-support")]
pub(crate) mod helper;
pub(crate) mod painter;
pub(crate) mod palette;
pub(crate) mod shell;
pub(crate) mod shell_manager;
pub(crate) mod value_shell;
#[cfg(feature = "rustyline-support")]
pub(crate) use helper::Helper;

View file

@ -3,7 +3,7 @@ use crate::completion::flag::FlagCompleter;
use crate::completion::path::{PathCompleter, PathSuggestion};
use crate::completion::{self, Completer, Suggestion};
use crate::context;
use std::borrow::Cow;
pub(crate) struct NuCompleter {}
impl NuCompleter {}
@ -109,8 +109,8 @@ fn select_directory_suggestions(completed_paths: Vec<PathSuggestion>) -> Vec<Pat
.collect()
}
fn requote(value: String) -> String {
let value = rustyline::completion::unescape(&value, Some('\\'));
fn requote(orig_value: String) -> String {
let value: Cow<str> = rustyline::completion::unescape(&orig_value, Some('\\'));
let mut quotes = vec!['"', '\'', '`'];
let mut should_quote = false;

View file

@ -1,14 +1,13 @@
use std::borrow::Cow::{self, Owned};
use ansi_term::{Color, Style};
use nu_parser::SignatureRegistry;
use nu_protocol::hir::FlatShape;
use nu_source::{Spanned, Tag, Tagged};
use nu_source::{Tag, Tagged};
use crate::completion;
use crate::context::Context;
use crate::shell::completer::NuCompleter;
use crate::shell::palette::{DefaultPalette, Palette};
use crate::shell::painter::Painter;
use crate::shell::palette::DefaultPalette;
pub struct Helper {
completer: NuCompleter,
@ -143,90 +142,4 @@ fn vec_tag<T>(input: Vec<Tagged<T>>) -> Option<Tag> {
})
}
pub struct Painter {
original: Vec<u8>,
styles: Vec<Style>,
}
impl Painter {
fn new(original: &str) -> Painter {
let bytes: Vec<u8> = original.bytes().collect();
let bytes_count = bytes.len();
Painter {
original: bytes,
styles: vec![Color::White.normal(); bytes_count],
}
}
pub fn paint_string<'l, P: Palette>(
line: &'l str,
registry: &dyn SignatureRegistry,
palette: &P,
) -> Cow<'l, str> {
let lite_block = nu_parser::lite_parse(line, 0);
match lite_block {
Err(_) => Cow::Borrowed(line),
Ok(lb) => {
let classified = nu_parser::classify_block(&lb, registry);
let shapes = nu_parser::shapes(&classified.block);
let mut painter = Painter::new(line);
for shape in shapes {
painter.paint_shape(&shape, palette);
}
Cow::Owned(painter.into_string())
}
}
}
fn paint_shape<P: Palette>(&mut self, shape: &Spanned<FlatShape>, palette: &P) {
palette
.styles_for_shape(shape)
.iter()
.for_each(|x| self.paint(x));
}
fn paint(&mut self, styled_span: &Spanned<Style>) {
for pos in styled_span.span.start()..styled_span.span.end() {
self.styles[pos] = styled_span.item;
}
}
fn into_string(self) -> String {
let mut idx_start = 0;
let mut idx_end = 1;
if self.original.is_empty() {
String::new()
} else {
let mut builder = String::new();
let mut current_style = self.styles[0];
while idx_end < self.styles.len() {
if self.styles[idx_end] != current_style {
// Emit, as we changed styles
let intermediate = String::from_utf8_lossy(&self.original[idx_start..idx_end]);
builder.push_str(&format!("{}", current_style.paint(intermediate)));
current_style = self.styles[idx_end];
idx_start = idx_end;
idx_end += 1;
} else {
idx_end += 1;
}
}
let intermediate = String::from_utf8_lossy(&self.original[idx_start..idx_end]);
builder.push_str(&format!("{}", current_style.paint(intermediate)));
builder
}
}
}
impl rustyline::Helper for Helper {}

View file

@ -0,0 +1,92 @@
use crate::shell::palette::Palette;
use ansi_term::{Color, Style};
use nu_parser::SignatureRegistry;
use nu_protocol::hir::FlatShape;
use nu_source::Spanned;
use std::borrow::Cow;
pub struct Painter {
original: Vec<u8>,
styles: Vec<Style>,
}
impl Painter {
fn new(original: &str) -> Painter {
let bytes: Vec<u8> = original.bytes().collect();
let bytes_count = bytes.len();
Painter {
original: bytes,
styles: vec![Color::White.normal(); bytes_count],
}
}
pub fn paint_string<'l, P: Palette>(
line: &'l str,
registry: &dyn SignatureRegistry,
palette: &P,
) -> Cow<'l, str> {
let lite_block = nu_parser::lite_parse(line, 0);
match lite_block {
Err(_) => Cow::Borrowed(line),
Ok(lb) => {
let classified = nu_parser::classify_block(&lb, registry);
let shapes = nu_parser::shapes(&classified.block);
let mut painter = Painter::new(line);
for shape in shapes {
painter.paint_shape(&shape, palette);
}
Cow::Owned(painter.into_string())
}
}
}
fn paint_shape<P: Palette>(&mut self, shape: &Spanned<FlatShape>, palette: &P) {
palette
.styles_for_shape(shape)
.iter()
.for_each(|x| self.paint(x));
}
fn paint(&mut self, styled_span: &Spanned<Style>) {
for pos in styled_span.span.start()..styled_span.span.end() {
self.styles[pos] = styled_span.item;
}
}
fn into_string(self) -> String {
let mut idx_start = 0;
let mut idx_end = 1;
if self.original.is_empty() {
String::new()
} else {
let mut builder = String::new();
let mut current_style = self.styles[0];
while idx_end < self.styles.len() {
if self.styles[idx_end] != current_style {
// Emit, as we changed styles
let intermediate = String::from_utf8_lossy(&self.original[idx_start..idx_end]);
builder.push_str(&format!("{}", current_style.paint(intermediate)));
current_style = self.styles[idx_end];
idx_start = idx_end;
idx_end += 1;
} else {
idx_end += 1;
}
}
let intermediate = String::from_utf8_lossy(&self.original[idx_start..idx_end]);
builder.push_str(&format!("{}", current_style.paint(intermediate)));
builder
}
}
}

View file

@ -166,8 +166,16 @@ fn main() -> Result<(), Box<dyn Error>> {
let _ = nu_cli::register_plugins(&mut context);
}
#[cfg(feature = "rustyline-support")]
{
futures::executor::block_on(nu_cli::cli(context))?;
}
#[cfg(not(feature = "rustyline-support"))]
{
println!("Nushell needs the 'rustyline-support' feature for CLI support");
}
}
}
Ok(())