mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 05:24:56 +02:00
Begin directory contrib docs and split commands (#3650)
* Begin directory contrib docs and split commands * Fix unused import warning
This commit is contained in:
@ -11,37 +11,37 @@ version = "0.32.1"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-completion = { version = "0.32.1", path = "../nu-completion" }
|
||||
nu-command = { version = "0.32.1", path = "../nu-command" }
|
||||
nu-data = { version = "0.32.1", path = "../nu-data" }
|
||||
nu-engine = { version = "0.32.1", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.32.1", path = "../nu-errors" }
|
||||
nu-json = { version = "0.32.1", path = "../nu-json" }
|
||||
nu-parser = { version = "0.32.1", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.32.1", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.32.1", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.32.1", path = "../nu-source" }
|
||||
nu-stream = { version = "0.32.1", path = "../nu-stream" }
|
||||
nu-table = { version = "0.32.1", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.32.1", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.32.1", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.32.1", path = "../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.32.1", path = "../nu-pretty-hex" }
|
||||
nu-completion = { version="0.32.1", path="../nu-completion" }
|
||||
nu-command = { version="0.32.1", path="../nu-command" }
|
||||
nu-data = { version="0.32.1", path="../nu-data" }
|
||||
nu-engine = { version="0.32.1", path="../nu-engine" }
|
||||
nu-errors = { version="0.32.1", path="../nu-errors" }
|
||||
nu-json = { version="0.32.1", path="../nu-json" }
|
||||
nu-parser = { version="0.32.1", path="../nu-parser" }
|
||||
nu-plugin = { version="0.32.1", path="../nu-plugin" }
|
||||
nu-protocol = { version="0.32.1", path="../nu-protocol" }
|
||||
nu-source = { version="0.32.1", path="../nu-source" }
|
||||
nu-stream = { version="0.32.1", path="../nu-stream" }
|
||||
nu-table = { version="0.32.1", path="../nu-table" }
|
||||
nu-test-support = { version="0.32.1", path="../nu-test-support" }
|
||||
nu-value-ext = { version="0.32.1", path="../nu-value-ext" }
|
||||
nu-ansi-term = { version="0.32.1", path="../nu-ansi-term" }
|
||||
nu-pretty-hex = { version="0.32.1", path="../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
arboard = { version="1.1.0", optional=true }
|
||||
async-recursion = "0.3.2"
|
||||
async-trait = "0.1.42"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||
bigdecimal = { version="0.2.0", features=["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "1.0.1"
|
||||
calamine = "0.17.0"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
chrono = { version="0.4.19", features=["serde"] }
|
||||
chrono-tz = "0.5.3"
|
||||
codespan-reporting = "0.11.0"
|
||||
csv = "1.1.5"
|
||||
ctrlc = { version = "3.1.7", optional = true }
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
derive-new = "0.5.8"
|
||||
directories-next = "2.0.0"
|
||||
dirs-next = "2.0.0"
|
||||
@ -51,25 +51,23 @@ eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.28"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||
futures = { version="0.3.12", features=["compat", "io-compat"] }
|
||||
futures-util = "0.3.12"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
indexmap = { version="1.6.1", features=["serde-1"] }
|
||||
log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
meval = "0.2.0"
|
||||
num-bigint = { version = "0.3.1", features = ["serde"] }
|
||||
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-bigint = { version="0.3.1", features=["serde"] }
|
||||
num-format = { version="0.4.0", features=["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
ptree = { version = "0.3.1", optional = true }
|
||||
ptree = { version="0.3.1", optional=true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.21.0"
|
||||
rand = "0.8.3"
|
||||
@ -77,8 +75,8 @@ rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version = "8.1.0", optional = true }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
rustyline = { version="8.1.0", optional=true }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.61"
|
||||
@ -90,18 +88,18 @@ strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
tempfile = "3.2.0"
|
||||
term = { version = "0.7.0", optional = true }
|
||||
term = { version="0.7.0", optional=true }
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.2"
|
||||
titlecase = "1.1.0"
|
||||
toml = "0.5.8"
|
||||
trash = { version = "1.3.0", optional = true }
|
||||
trash = { version="1.3.0", optional=true }
|
||||
unicode-segmentation = "1.7.1"
|
||||
url = "2.1.1"
|
||||
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
|
||||
which = { version = "4.0.2", optional = true }
|
||||
zip = { version = "0.5.9", optional = true }
|
||||
shadow-rs = { version = "0.5", default-features = false, optional = true }
|
||||
uuid_crate = { package="uuid", version="0.8.2", features=["v4"], optional=true }
|
||||
which = { version="4.0.2", optional=true }
|
||||
zip = { version="0.5.9", optional=true }
|
||||
shadow-rs = { version="0.5", default-features=false, optional=true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
|
4
crates/nu-cli/README.md
Normal file
4
crates/nu-cli/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
* nu-cli
|
||||
|
||||
This crate provides the fundamental needs when creating the Nushell interactive REPL. In it, you'll find features for interacting with the line editor (the piece which writes the prompt and takes input from the user), keybindings, handlers for the commandline arguments passed to the REPL as it starts up, and more.
|
||||
|
@ -5,7 +5,7 @@ mod options_parser;
|
||||
pub use options::{CliOptions, NuScript, Options};
|
||||
use options_parser::{NuParser, OptionsParser};
|
||||
|
||||
use nu_command::{commands::nu::Nu, utils::test_bins as binaries};
|
||||
use nu_command::{commands::NuSignature as Nu, utils::test_bins as binaries};
|
||||
use nu_engine::{get_full_help, EvaluationContext};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{Call, Expression, SpannedExpression, Synthetic};
|
||||
@ -58,7 +58,7 @@ impl App {
|
||||
if self.version() {
|
||||
let context = self.parser.context();
|
||||
|
||||
let stream = nu_command::commands::version::version(nu_engine::CommandArgs {
|
||||
let stream = nu_command::commands::version(nu_engine::CommandArgs {
|
||||
context: context.clone(),
|
||||
call_info: nu_engine::UnevaluatedCallInfo {
|
||||
args: Call::new(
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::Options;
|
||||
|
||||
use nu_command::commands::nu::{self, Nu};
|
||||
use nu_command::commands::{loglevels, testbins, NuSignature as Nu};
|
||||
use nu_command::commands::{Autoview, Pivot, Table, Version as NuVersion};
|
||||
use nu_engine::{whole_stream_command, EvaluationContext};
|
||||
use nu_errors::ShellError;
|
||||
@ -72,7 +72,7 @@ impl OptionsParser for NuParser {
|
||||
.map(|v| match k.as_ref() {
|
||||
"testbin" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if nu::testbins().iter().any(|n| name == *n) {
|
||||
if testbins().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
@ -90,7 +90,7 @@ impl OptionsParser for NuParser {
|
||||
}
|
||||
"loglevel" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if nu::loglevels().iter().any(|n| name == *n) {
|
||||
if loglevels().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
|
@ -1,5 +1,3 @@
|
||||
use super::Nu;
|
||||
|
||||
use crate::line_editor::configure_ctrl_c;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
|
||||
@ -150,7 +148,7 @@ pub fn cli(
|
||||
if !skip_welcome_message {
|
||||
println!(
|
||||
"Welcome to Nushell {} (type 'help' for more info)",
|
||||
Nu::version()
|
||||
nu_command::commands::core_version()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
|
||||
pub(crate) trait RenderView {
|
||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>;
|
||||
}
|
@ -1,8 +1,3 @@
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
#[macro_use]
|
||||
mod prelude;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate quickcheck;
|
||||
#[cfg(test)]
|
||||
@ -11,12 +6,11 @@ extern crate quickcheck_macros;
|
||||
|
||||
mod app;
|
||||
mod cli;
|
||||
mod format;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod keybinding;
|
||||
mod line_editor;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod shell;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub use crate::cli::cli;
|
||||
@ -25,8 +19,7 @@ pub use crate::app::App;
|
||||
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
|
||||
|
||||
pub use nu_command::{
|
||||
commands::default_context::create_default_context, commands::nu as Nu,
|
||||
commands::Version as NuVersion,
|
||||
commands::NuSignature as Nu, commands::Version as NuVersion, create_default_context,
|
||||
};
|
||||
pub use nu_data::config;
|
||||
pub use nu_data::dict::TaggedListBuilder;
|
||||
|
@ -1,8 +1,9 @@
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_errors::ShellError;
|
||||
use std::error::Error;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use nu_engine::script::LineResult;
|
||||
@ -31,7 +32,7 @@ pub fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>)
|
||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||
Err(ReadlineError::Eof) => LineResult::CtrlD,
|
||||
Err(err) => {
|
||||
outln!("Error: {:?}", err);
|
||||
eprintln!("Error: {:?}", err);
|
||||
LineResult::Break
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
#[macro_export]
|
||||
macro_rules! return_err {
|
||||
($expr:expr) => {
|
||||
match $expr {
|
||||
Err(_) => return,
|
||||
Ok(expr) => expr,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_out_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||
let objects = $expr.inspect(move |o| {
|
||||
trace!(
|
||||
target: $target,
|
||||
"{} = {}",
|
||||
$desc,
|
||||
match o {
|
||||
Err(err) => format!("{:?}", err),
|
||||
Ok(value) => value.display(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
nu_stream::OutputStream::new(objects)
|
||||
} else {
|
||||
$expr
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use nu_engine::Host;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_errors::ShellError;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_protocol::outln;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_value_ext::ValueExt;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use std::sync::atomic::Ordering;
|
@ -1,7 +1,212 @@
|
||||
#![allow(clippy::module_inception)]
|
||||
use nu_ansi_term::Color;
|
||||
use nu_completion::NuCompleter;
|
||||
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub(crate) mod helper;
|
||||
pub struct Helper {
|
||||
completer: NuCompleter,
|
||||
hinter: Option<rustyline::hint::HistoryHinter>,
|
||||
context: EvaluationContext,
|
||||
pub colored_prompt: String,
|
||||
validator: NuValidator,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub(crate) use helper::Helper;
|
||||
impl Helper {
|
||||
pub(crate) fn new(
|
||||
context: EvaluationContext,
|
||||
hinter: Option<rustyline::hint::HistoryHinter>,
|
||||
) -> Helper {
|
||||
Helper {
|
||||
completer: NuCompleter {},
|
||||
hinter,
|
||||
context,
|
||||
colored_prompt: String::new(),
|
||||
validator: NuValidator {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CompletionContext<'a>(&'a EvaluationContext);
|
||||
|
||||
impl<'a> nu_completion::CompletionContext for CompletionContext<'a> {
|
||||
fn signature_registry(&self) -> &dyn nu_parser::ParserScope {
|
||||
&self.0.scope
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompletionSuggestion(nu_completion::Suggestion);
|
||||
|
||||
impl rustyline::completion::Candidate for CompletionSuggestion {
|
||||
fn display(&self) -> &str {
|
||||
&self.0.display
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
&self.0.replacement
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::completion::Completer for Helper {
|
||||
type Candidate = CompletionSuggestion;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &rustyline::Context<'_>,
|
||||
) -> Result<(usize, Vec<Self::Candidate>), rustyline::error::ReadlineError> {
|
||||
let ctx = CompletionContext(&self.context);
|
||||
let (position, suggestions) = self.completer.complete(line, pos, &ctx);
|
||||
let suggestions = suggestions.into_iter().map(CompletionSuggestion).collect();
|
||||
Ok((position, suggestions))
|
||||
}
|
||||
|
||||
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
||||
let end = line.pos();
|
||||
line.replace(start..end, elected)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::hint::Hinter for Helper {
|
||||
type Hint = String;
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||
self.hinter.as_ref().and_then(|h| h.hint(line, pos, ctx))
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::highlight::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(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||
let cfg = &self.context.configs().lock();
|
||||
if let Some(palette) = &cfg.syntax_config {
|
||||
Painter::paint_string(line, &self.context.scope, palette)
|
||||
} else {
|
||||
Painter::paint_string(line, &self.context.scope, &DefaultPalette {})
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::validate::Validator for Helper {
|
||||
fn validate(
|
||||
&self,
|
||||
ctx: &mut rustyline::validate::ValidationContext,
|
||||
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||
self.validator.validate(ctx)
|
||||
}
|
||||
|
||||
fn validate_while_typing(&self) -> bool {
|
||||
self.validator.validate_while_typing()
|
||||
}
|
||||
}
|
||||
|
||||
struct NuValidator {}
|
||||
|
||||
impl rustyline::validate::Validator for NuValidator {
|
||||
fn validate(
|
||||
&self,
|
||||
ctx: &mut rustyline::validate::ValidationContext,
|
||||
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||
let src = ctx.input();
|
||||
|
||||
let (tokens, err) = nu_parser::lex(src, 0);
|
||||
if let Some(err) = err {
|
||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||
}
|
||||
}
|
||||
|
||||
let (_, err) = nu_parser::parse_block(tokens);
|
||||
|
||||
if let Some(err) = err {
|
||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(rustyline::validate::ValidationResult::Valid(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
})
|
||||
}
|
||||
|
||||
impl rustyline::Helper for Helper {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_engine::EvaluationContext;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn closing_quote_should_replaced() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 1);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn replacement_with_cursor_in_text() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 30);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,212 +0,0 @@
|
||||
use nu_ansi_term::Color;
|
||||
use nu_completion::NuCompleter;
|
||||
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
|
||||
pub struct Helper {
|
||||
completer: NuCompleter,
|
||||
hinter: Option<rustyline::hint::HistoryHinter>,
|
||||
context: EvaluationContext,
|
||||
pub colored_prompt: String,
|
||||
validator: NuValidator,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
pub(crate) fn new(
|
||||
context: EvaluationContext,
|
||||
hinter: Option<rustyline::hint::HistoryHinter>,
|
||||
) -> Helper {
|
||||
Helper {
|
||||
completer: NuCompleter {},
|
||||
hinter,
|
||||
context,
|
||||
colored_prompt: String::new(),
|
||||
validator: NuValidator {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CompletionContext<'a>(&'a EvaluationContext);
|
||||
|
||||
impl<'a> nu_completion::CompletionContext for CompletionContext<'a> {
|
||||
fn signature_registry(&self) -> &dyn nu_parser::ParserScope {
|
||||
&self.0.scope
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompletionSuggestion(nu_completion::Suggestion);
|
||||
|
||||
impl rustyline::completion::Candidate for CompletionSuggestion {
|
||||
fn display(&self) -> &str {
|
||||
&self.0.display
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
&self.0.replacement
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::completion::Completer for Helper {
|
||||
type Candidate = CompletionSuggestion;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &rustyline::Context<'_>,
|
||||
) -> Result<(usize, Vec<Self::Candidate>), rustyline::error::ReadlineError> {
|
||||
let ctx = CompletionContext(&self.context);
|
||||
let (position, suggestions) = self.completer.complete(line, pos, &ctx);
|
||||
let suggestions = suggestions.into_iter().map(CompletionSuggestion).collect();
|
||||
Ok((position, suggestions))
|
||||
}
|
||||
|
||||
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
||||
let end = line.pos();
|
||||
line.replace(start..end, elected)
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::hint::Hinter for Helper {
|
||||
type Hint = String;
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||
self.hinter.as_ref().and_then(|h| h.hint(line, pos, ctx))
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::highlight::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(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||
let cfg = &self.context.configs().lock();
|
||||
if let Some(palette) = &cfg.syntax_config {
|
||||
Painter::paint_string(line, &self.context.scope, palette)
|
||||
} else {
|
||||
Painter::paint_string(line, &self.context.scope, &DefaultPalette {})
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl rustyline::validate::Validator for Helper {
|
||||
fn validate(
|
||||
&self,
|
||||
ctx: &mut rustyline::validate::ValidationContext,
|
||||
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||
self.validator.validate(ctx)
|
||||
}
|
||||
|
||||
fn validate_while_typing(&self) -> bool {
|
||||
self.validator.validate_while_typing()
|
||||
}
|
||||
}
|
||||
|
||||
struct NuValidator {}
|
||||
|
||||
impl rustyline::validate::Validator for NuValidator {
|
||||
fn validate(
|
||||
&self,
|
||||
ctx: &mut rustyline::validate::ValidationContext,
|
||||
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||
let src = ctx.input();
|
||||
|
||||
let (tokens, err) = nu_parser::lex(src, 0);
|
||||
if let Some(err) = err {
|
||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||
}
|
||||
}
|
||||
|
||||
let (_, err) = nu_parser::parse_block(tokens);
|
||||
|
||||
if let Some(err) = err {
|
||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(rustyline::validate::ValidationResult::Valid(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
})
|
||||
}
|
||||
|
||||
impl rustyline::Helper for Helper {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_engine::EvaluationContext;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn closing_quote_should_replaced() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 1);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn replacement_with_cursor_in_text() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 30);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
pub(crate) mod deduction;
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user