Compare commits

...

23 Commits

Author SHA1 Message Date
4faaa5310e Bump to 0.29 (#3230)
* Bump to 0.29

* fix test
2021-03-30 22:35:21 +13:00
c448abd44e echo $scope.aliases | pivot to see all of your aliases (#3203)
* enable ability to see all aliases, pull in code from scope branch

* add in the alias tests

* add back in my changes to variables.rs after merging in andrasios changes from PR #3217
2021-03-29 21:27:51 +13:00
2517588d7d update date to-timezone usage (#3223) 2021-03-28 18:41:42 -05:00
8fc8fc89aa History, more test coverage improvements, and refactorings. (#3217)
Improvements overall to Nu. Also among the changes here, we can also be more confident towards incorporating `3041`. End to end tests for checking envs properly exported to externals is not added here (since it's in the other PR)

A few things added in this PR (probably forgetting some too)

* no writes happen to history during test runs.
* environment syncing end to end coverage added.
* clean up / refactorings few areas.
* testing API for finer control (can write tests passing more than one pipeline)
* can pass environment variables in tests that nu will inherit when running.

* No longer needed.

* no longer under a module. No need to use super.
2021-03-27 00:08:03 -05:00
b243b3ee1d fixed typo in help text (#3216) 2021-03-26 08:11:41 -05:00
28e08afada add ability to cd to ~/blah (#3210)
* add ability to cd to ~/blah. tested on windows.

* added dirs_next

* put change behind feature for linux-minimal/wasm

* clippy

* holy crap minimal, i'm about done with you!
2021-03-26 22:29:02 +13:00
7e184b58b2 Fix warnings for Rust 1.51 (#3214)
* Fix warnings for Rust 1.51

* More fixes

* More fixes
2021-03-26 21:26:57 +13:00
589fc0b8ad add support for timestamp-based time conversion by specifying timezone (#3207)
* add support for timestamp-based time conversion by specifing timezone or 'UTC/Local'

* [fix] fix the wrong test sample

* code formating

* code formating and import missing mod to test

* code formating again
2021-03-24 08:08:23 -05:00
f0c7c1b500 fix: prompt does not find external commands #3134 (#3189)
the initial setup-commands of nushell were executed without loading environment variables
this resulted in the PATH not being available at this point until an external command was run once
which resulted in env_vars being added
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;

Co-authored-by: alexhk <alexhk@protonmail.com>
2021-03-23 22:38:07 +13:00
840bd98e01 support forward slash for directory completion in Windows (#3201)
While the "main" separator in Windows is the backslash, it supports the
forward slash as a separator too.
Add support for this so that the behavior is similar to the way Windows
PowerShell handles the forward slash: it is recognized as a separator,
and when using <tab> for path completion the slash is reversed.
2021-03-23 16:20:01 +13:00
a5cdd22bfe Add basic support for md5 hashing strings and binary data (#3197) 2021-03-21 07:48:53 +13:00
0c7bcae9b1 Update contributor-book url (#3198) 2021-03-20 23:09:17 +13:00
ab666c170c added the ability to create multi-byte unicode chars like emoji (#3195) 2021-03-18 13:42:39 -05:00
d2213d18fa Playground infraestructure (tests, etc) additions. (#3179)
* Playground infraestructure (tests, etc) additions.

A few things to note:

* Nu can be started with a custom configuration file (`nu --config-file /path/to/sample_config.toml`). Useful for mocking the configuration on test runs.
* When given a custom configuration file Nu will save any changes to the file supplied appropiately.
* The `$nu.config-path` variable either shows the default configuration file (or the custom one, if given)
* We can now run end to end tests with finer grained control (currently, since this is baseline work, standard out) This will allow to check things like exit status, assert the contents with a format, etc)

* Remove (for another PR)
2021-03-15 02:26:30 -05:00
82b6300dcb fix: cargo test failed with --release (#3183) (#3184)
Signed-off-by: nibon7 <nibon7@163.com>
2021-03-15 16:59:04 +13:00
b69cda9e07 Add --signal option to kill command (#3077) (#3079) 2021-03-15 13:10:52 +13:00
56adc7c3c6 imp: bump rustyline to 8.0.0 (#3167)
* imp: bump rustyline to 8.0.0

* fix: rustyline 8 keybindings

* fix: commands count/length test

Co-authored-by: alexhk <alexhk@protonmail.com>
2021-03-14 15:13:31 +13:00
2ace20fade Make opening a directory list its contents (#3118)
* Make opening a directory enter it.

Not sure if this change is wanted, but I'm not sure what else opening a directory could mean.
And I find myself accidentally using `open <dir>` to mean `enter <dir>`

* Add example to open directory

* Open dir should list it's contents

* Update example description and fix style
2021-03-14 10:47:31 +13:00
c13fe83784 Rename count to length (#3166)
* update docs to refer to length instead of count

* rename count to length

* change all occurrences of 'count' to 'length' in tests

* format length command
2021-03-14 10:46:40 +13:00
6cf8df8685 Move script to nu engine (#3092)
* Move run_script to engine

* Add which dep and feature to engine

* Change unwrap to expect

* Add wasm specification

* Remove which from default, add specification correctly

* Add nu-platform-specifics

* Move is_external_cmd to platform_specifics

* Add is_external_cmd to host and use it instead of nu_platform directly

* Clean up if else logic in is_external_cmd

* Bump nu-platform-specifics version

* Pass context to print_err

* Commit cargo.lock

* Move print functions to own module inside nu-engine

* Hypocratic change to run windows-nightly again

* Add import for Ordering

* Move printing of error to host

* Move platform specific which functionality to basic host

* Allow no use of cmd_name

* Fix windows compile issue
2021-03-12 18:20:54 +13:00
86a89404be fix: unicode byte counting error #3150 (#3159)
Co-authored-by: hk <alexhaka10@protonmail.com>
2021-03-12 07:11:07 +13:00
0d305d7c3e Lines no longer treats a text buffer as a line (#3153) 2021-03-11 11:35:15 +13:00
ee5bd2b4b3 move from h1-client-rustls to hyper-client (#3154) 2021-03-10 15:45:53 -06:00
203 changed files with 3601 additions and 2246 deletions

View File

@ -2,7 +2,7 @@
Welcome to nushell!
*Note: for a more complete guide see [The nu contributor book](https://github.com/nushell/contributor-book)*
*Note: for a more complete guide see [The nu contributor book](https://www.nushell.sh/contributor-book/)*
For speedy contributions open it in Gitpod, nu will be pre-installed with the latest build in a VSCode like editor all from your browser.

1211
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
version = "0.28.0"
version = "0.29.0"
[workspace]
members = ["crates/*/"]
@ -18,35 +18,35 @@ members = ["crates/*/"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-cli = { version = "0.28.0", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.28.0", path = "./crates/nu-command" }
nu-data = { version = "0.28.0", path = "./crates/nu-data" }
nu-engine = { version = "0.28.0", path = "./crates/nu-engine" }
nu-errors = { version = "0.28.0", path = "./crates/nu-errors" }
nu-parser = { version = "0.28.0", path = "./crates/nu-parser" }
nu-plugin = { version = "0.28.0", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.28.0", path = "./crates/nu-protocol" }
nu-source = { version = "0.28.0", path = "./crates/nu-source" }
nu-value-ext = { version = "0.28.0", path = "./crates/nu-value-ext" }
nu-cli = { version = "0.29.0", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.29.0", path = "./crates/nu-command" }
nu-data = { version = "0.29.0", path = "./crates/nu-data" }
nu-engine = { version = "0.29.0", path = "./crates/nu-engine" }
nu-errors = { version = "0.29.0", path = "./crates/nu-errors" }
nu-parser = { version = "0.29.0", path = "./crates/nu-parser" }
nu-plugin = { version = "0.29.0", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.29.0", path = "./crates/nu-protocol" }
nu-source = { version = "0.29.0", path = "./crates/nu-source" }
nu-value-ext = { version = "0.29.0", path = "./crates/nu-value-ext" }
nu_plugin_binaryview = { version = "0.28.0", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.28.0", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.28.0", path = "./crates/nu_plugin_fetch", optional = true }
nu_plugin_from_bson = { version = "0.28.0", path = "./crates/nu_plugin_from_bson", optional = true }
nu_plugin_from_sqlite = { version = "0.28.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
nu_plugin_inc = { version = "0.28.0", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.28.0", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.28.0", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.28.0", path = "./crates/nu_plugin_ps", optional = true }
nu_plugin_s3 = { version = "0.28.0", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.28.0", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.28.0", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.28.0", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.28.0", path = "./crates/nu_plugin_textview", optional = true }
nu_plugin_to_bson = { version = "0.28.0", path = "./crates/nu_plugin_to_bson", optional = true }
nu_plugin_to_sqlite = { version = "0.28.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
nu_plugin_tree = { version = "0.28.0", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.28.0", path = "./crates/nu_plugin_xpath", optional = true }
nu_plugin_binaryview = { version = "0.29.0", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.29.0", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.29.0", path = "./crates/nu_plugin_fetch", optional = true }
nu_plugin_from_bson = { version = "0.29.0", path = "./crates/nu_plugin_from_bson", optional = true }
nu_plugin_from_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
nu_plugin_inc = { version = "0.29.0", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.29.0", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.29.0", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.29.0", path = "./crates/nu_plugin_ps", optional = true }
nu_plugin_s3 = { version = "0.29.0", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.29.0", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.29.0", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.29.0", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.29.0", path = "./crates/nu_plugin_textview", optional = true }
nu_plugin_to_bson = { version = "0.29.0", path = "./crates/nu_plugin_to_bson", optional = true }
nu_plugin_to_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
nu_plugin_tree = { version = "0.29.0", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.29.0", path = "./crates/nu_plugin_xpath", optional = true }
# Required to bootstrap the main binary
clap = "2.33.3"
@ -57,9 +57,10 @@ log = "0.4.14"
pretty_env_logger = "0.4.0"
[dev-dependencies]
nu-test-support = { version = "0.28.0", path = "./crates/nu-test-support" }
nu-test-support = { version = "0.29.0", path = "./crates/nu-test-support" }
dunce = "1.0.1"
serial_test = "0.5.1"
hamcrest2 = "0.3.0"
[build-dependencies]
@ -84,6 +85,7 @@ which-support = [
"nu-cli/which",
"nu-command/ichwh",
"nu-command/which",
"nu-engine/which",
]
default = [

View File

@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
edition = "2018"
license = "MIT"
name = "nu-ansi-term"
version = "0.28.0"
version = "0.29.0"
[lib]
doctest = false

View File

@ -15,7 +15,7 @@ fn main() {
let g = (col * 255 / WIDTH) as u8;
let b = 128;
print!("{}", Style::default().on(Color::RGB(r, g, b)).paint(" "));
print!("{}", Style::default().on(Color::Rgb(r, g, b)).paint(" "));
}
println!();

View File

@ -103,7 +103,7 @@ impl Color {
Color::Cyan => write!(f, "36"),
Color::White => write!(f, "37"),
Color::Fixed(num) => write!(f, "38;5;{}", &num),
Color::RGB(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
Color::DarkGray => write!(f, "90"),
Color::LightRed => write!(f, "91"),
Color::LightGreen => write!(f, "92"),
@ -128,7 +128,7 @@ impl Color {
Color::Cyan => write!(f, "46"),
Color::White => write!(f, "47"),
Color::Fixed(num) => write!(f, "48;5;{}", &num),
Color::RGB(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
Color::DarkGray => write!(f, "100"),
Color::LightRed => write!(f, "101"),
Color::LightGreen => write!(f, "102"),
@ -372,10 +372,10 @@ mod test {
test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
test!(rgb_on_rgb: Rgb(70,130,180).on(Rgb(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");

View File

@ -112,7 +112,7 @@ mod test {
test!(both: style().bold().italic() => "Style { bold, italic }");
test!(red: Red.normal() => "Style { fg(Red) }");
test!(redblue: Red.normal().on(RGB(3, 2, 4)) => "Style { fg(Red), on(RGB(3, 2, 4)) }");
test!(redblue: Red.normal().on(Rgb(3, 2, 4)) => "Style { fg(Red), on(Rgb(3, 2, 4)) }");
test!(everything:
Red.on(Blue).blink().bold().dimmed().hidden().italic().reverse().strikethrough().underline() =>

View File

@ -11,7 +11,7 @@ use std::ops::Deref;
/// display that string. `ANSIString` and `ANSIByteString` are aliases for
/// this type on `str` and `\[u8]`, respectively.
#[derive(PartialEq, Debug)]
pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized>
pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -30,12 +30,12 @@ where
/// let clone_string = plain_string.clone();
/// assert_eq!(clone_string, plain_string);
/// ```
impl<'a, S: 'a + ToOwned + ?Sized> Clone for ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
fn clone(&self) -> ANSIGenericString<'a, S> {
ANSIGenericString {
fn clone(&self) -> AnsiGenericString<'a, S> {
AnsiGenericString {
style: self.style,
string: self.string.clone(),
}
@ -85,26 +85,26 @@ where
/// let plain_string = ANSIString::from("a plain string");
/// assert_eq!(&*plain_string, "a plain string");
/// ```
pub type ANSIString<'a> = ANSIGenericString<'a, str>;
pub type AnsiString<'a> = AnsiGenericString<'a, str>;
/// An `ANSIByteString` represents a formatted series of bytes. Use
/// `ANSIByteString` when styling text with an unknown encoding.
pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>;
/// An `AnsiByteString` represents a formatted series of bytes. Use
/// `AnsiByteString` when styling text with an unknown encoding.
pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>;
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S>
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
fn from(input: I) -> ANSIGenericString<'a, S> {
ANSIGenericString {
fn from(input: I) -> AnsiGenericString<'a, S> {
AnsiGenericString {
string: input.into(),
style: Style::default(),
}
}
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -119,7 +119,7 @@ where
}
}
impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> Deref for AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -130,32 +130,32 @@ where
}
}
/// A set of `ANSIGenericString`s collected together, in order to be
/// A set of `AnsiGenericStrings`s collected together, in order to be
/// written with a minimum of control characters.
#[derive(Debug, PartialEq)]
pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [ANSIGenericString<'a, S>])
pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>])
where
<S as ToOwned>::Owned: fmt::Debug,
S: PartialEq;
/// A set of `ANSIString`s collected together, in order to be written with a
/// A set of `AnsiString`s collected together, in order to be written with a
/// minimum of control characters.
pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>;
pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>;
/// A function to construct an `ANSIStrings` instance.
/// A function to construct an `AnsiStrings` instance.
#[allow(non_snake_case)]
pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> {
ANSIGenericStrings(arg)
pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> {
AnsiGenericStrings(arg)
}
/// A set of `ANSIByteString`s collected together, in order to be
/// A set of `AnsiByteString`s collected together, in order to be
/// written with a minimum of control characters.
pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>;
pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>;
/// A function to construct an `ANSIByteStrings` instance.
#[allow(non_snake_case)]
pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> {
ANSIGenericStrings(arg)
pub fn ANSIByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> {
AnsiGenericStrings(arg)
}
// ---- paint functions ----
@ -163,12 +163,12 @@ pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a>
impl Style {
/// Paints the given text with this color, returning an ANSI string.
#[must_use]
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
ANSIGenericString {
AnsiGenericString {
string: input.into(),
style: self,
}
@ -185,12 +185,12 @@ impl Color {
/// println!("{}", Blue.paint("da ba dee"));
/// ```
#[must_use]
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
ANSIGenericString {
AnsiGenericString {
string: input.into(),
style: self.normal(),
}
@ -199,14 +199,14 @@ impl Color {
// ---- writers for individual ANSI strings ----
impl<'a> fmt::Display for ANSIString<'a> {
impl<'a> fmt::Display for AnsiString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let w: &mut dyn fmt::Write = f;
self.write_to_any(w)
}
}
impl<'a> ANSIByteString<'a> {
impl<'a> AnsiByteString<'a> {
/// Write an `ANSIByteString` to an `io::Write`. This writes the escape
/// sequences for the associated `Style` around the bytes.
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
@ -215,7 +215,7 @@ impl<'a> ANSIByteString<'a> {
}
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
&'a S: AsRef<[u8]>,
@ -229,14 +229,14 @@ where
// ---- writers for combined ANSI strings ----
impl<'a> fmt::Display for ANSIStrings<'a> {
impl<'a> fmt::Display for AnsiStrings<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f: &mut dyn fmt::Write = f;
self.write_to_any(f)
}
}
impl<'a> ANSIByteStrings<'a> {
impl<'a> AnsiByteStrings<'a> {
/// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal
/// escape sequences for the associated `Style`s around each set of
/// bytes.
@ -246,7 +246,7 @@ impl<'a> ANSIByteStrings<'a> {
}
}
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> ANSIGenericStrings<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
&'a S: AsRef<[u8]>,
@ -289,7 +289,7 @@ where
#[cfg(test)]
mod tests {
pub use super::super::ANSIStrings;
pub use super::super::AnsiStrings;
pub use crate::style::Color::*;
pub use crate::style::Style;
@ -297,7 +297,7 @@ mod tests {
fn no_control_codes_for_plain() {
let one = Style::default().paint("one");
let two = Style::default().paint("two");
let output = format!("{}", ANSIStrings(&[one, two]));
let output = format!("{}", AnsiStrings(&[one, two]));
assert_eq!(&*output, "onetwo");
}
}

View File

@ -365,7 +365,7 @@ pub enum Color {
Fixed(u8),
/// A 24-bit RGB color, as specified by ISO-8613-3.
RGB(u8, u8, u8),
Rgb(u8, u8, u8),
}
impl Color {

View File

@ -1,12 +1,12 @@
use crate::display::{ANSIString, ANSIStrings};
use crate::display::{AnsiString, AnsiStrings};
use std::ops::Deref;
/// Return a substring of the given ANSIStrings sequence, while keeping the formatting.
pub fn sub_string<'a>(
start: usize,
len: usize,
strs: &ANSIStrings<'a>,
) -> Vec<ANSIString<'static>> {
strs: &AnsiStrings<'a>,
) -> Vec<AnsiString<'static>> {
let mut vec = Vec::new();
let mut pos = start;
let mut len_rem = len;
@ -39,7 +39,7 @@ pub fn sub_string<'a>(
}
/// Return a concatenated copy of `strs` without the formatting, as an allocated `String`.
pub fn unstyle(strs: &ANSIStrings) -> String {
pub fn unstyle(strs: &AnsiStrings) -> String {
let mut s = String::new();
for i in strs.0.iter() {
@ -50,7 +50,7 @@ pub fn unstyle(strs: &ANSIStrings) -> String {
}
/// Return the unstyled length of ANSIStrings. This is equaivalent to `unstyle(strs).len()`.
pub fn unstyled_len(strs: &ANSIStrings) -> usize {
pub fn unstyled_len(strs: &AnsiStrings) -> usize {
let mut l = 0;
for i in strs.0.iter() {
l += i.deref().len();
@ -70,7 +70,7 @@ mod test {
Red.paint("-second"),
White.paint("-third"),
];
let a = ANSIStrings(&l);
let a = AnsiStrings(&l);
assert_eq!(unstyle(&a), "first-second-third");
assert_eq!(unstyled_len(&a), 18);

View File

@ -5,26 +5,26 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.28.0"
version = "0.29.0"
[lib]
doctest = false
[dependencies]
nu-command = { version = "0.28.0", path = "../nu-command" }
nu-data = { version = "0.28.0", path = "../nu-data" }
nu-engine = { version = "0.28.0", path = "../nu-engine" }
nu-errors = { version = "0.28.0", path = "../nu-errors" }
nu-json = { version = "0.28.0", path = "../nu-json" }
nu-parser = { version = "0.28.0", path = "../nu-parser" }
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
nu-source = { version = "0.28.0", path = "../nu-source" }
nu-stream = { version = "0.28.0", path = "../nu-stream" }
nu-table = { version = "0.28.0", path = "../nu-table" }
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
nu-command = { version = "0.29.0", path = "../nu-command" }
nu-data = { version = "0.29.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
@ -77,7 +77,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "6.3.0", optional = true }
rustyline = { version = "8.0.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"

View File

@ -1,12 +1,9 @@
use crate::line_editor::configure_ctrl_c;
use nu_command::commands::default_context::create_default_context;
#[allow(unused_imports)]
use nu_command::maybe_print_errors;
use nu_engine::run_block;
use nu_engine::EvaluationContext;
use nu_engine::{evaluation_context, run_block, script::run_script_standalone, EvaluationContext};
#[allow(unused_imports)]
pub(crate) use nu_command::script::{process_script, LineResult};
pub(crate) use nu_engine::script::{process_script, LineResult};
#[cfg(feature = "rustyline-support")]
use crate::line_editor::{
@ -16,13 +13,13 @@ use crate::line_editor::{
#[allow(unused_imports)]
use nu_data::config;
use nu_source::{Tag, Text};
use nu_data::config::{Conf, NuConfig};
use nu_source::{AnchorLocation, Tag, Text};
use nu_stream::InputStream;
use std::ffi::{OsStr, OsString};
#[allow(unused_imports)]
use std::sync::atomic::Ordering;
use nu_command::script::{print_err, run_script_standalone};
#[cfg(feature = "rustyline-support")]
use rustyline::{self, error::ReadlineError};
@ -36,6 +33,81 @@ use std::error::Error;
use std::iter::Iterator;
use std::path::PathBuf;
pub struct Options {
pub config: Option<OsString>,
pub history: Option<PathBuf>,
pub save_history: bool,
pub stdin: bool,
pub scripts: Vec<NuScript>,
}
impl Default for Options {
fn default() -> Self {
Self::new()
}
}
impl Options {
pub fn new() -> Self {
Self {
config: None,
history: None,
save_history: true,
stdin: false,
scripts: vec![],
}
}
pub fn history(&self, block: impl FnOnce(&std::path::Path)) {
if !self.save_history {
return;
}
if let Some(file) = &self.history {
block(&file)
}
}
}
pub struct NuScript {
pub filepath: Option<OsString>,
pub contents: String,
}
impl NuScript {
pub fn code<'a>(content: impl Iterator<Item = &'a str>) -> Result<Self, ShellError> {
let text = content
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
Ok(Self {
filepath: None,
contents: text,
})
}
pub fn get_code(&self) -> &str {
&self.contents
}
pub fn source_file(path: &OsStr) -> Result<Self, ShellError> {
use std::fs::File;
use std::io::Read;
let path = path.to_os_string();
let mut file = File::open(&path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(Self {
filepath: Some(path),
contents: buffer,
})
}
}
pub fn search_paths() -> Vec<std::path::PathBuf> {
use std::env;
@ -65,12 +137,9 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
search_paths
}
pub async fn run_script_file(
file_contents: String,
redirect_stdin: bool,
) -> Result<(), Box<dyn Error>> {
let mut syncer = EnvironmentSyncer::new();
pub async fn run_script_file(mut options: Options) -> Result<(), Box<dyn Error>> {
let mut context = create_default_context(false)?;
let mut syncer = create_environment_syncer(&context, &mut options);
let config = syncer.get_config();
context.configure(&config, |_, ctx| {
@ -79,7 +148,7 @@ pub async fn run_script_file(
syncer.sync_path_vars(ctx);
if let Err(reason) = syncer.autoenv(ctx) {
print_err(reason, &Text::from(""), ctx);
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = register_plugins(ctx);
@ -88,15 +157,62 @@ pub async fn run_script_file(
let _ = run_startup_commands(&mut context, &config).await;
run_script_standalone(file_contents, redirect_stdin, &context, true).await?;
let script = options
.scripts
.get(0)
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true).await?;
Ok(())
}
/// 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.
fn create_environment_syncer(
context: &EvaluationContext,
options: &mut Options,
) -> EnvironmentSyncer {
let configuration = match &options.config {
Some(config_file) => {
let location = Some(AnchorLocation::File(
config_file.to_string_lossy().to_string(),
));
let tag = Tag::unknown().anchored(location);
context.scope.add_var(
"config-path",
UntaggedValue::filepath(PathBuf::from(&config_file)).into_value(tag),
);
NuConfig::with(Some(config_file.into()))
}
None => NuConfig::new(),
};
let history_path = configuration.history_path();
options.history = Some(history_path.clone());
let location = Some(AnchorLocation::File(
history_path.to_string_lossy().to_string(),
));
let tag = Tag::unknown().anchored(location);
context.scope.add_var(
"history-path",
UntaggedValue::filepath(history_path).into_value(tag),
);
EnvironmentSyncer::with_config(Box::new(configuration))
}
#[cfg(feature = "rustyline-support")]
pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
let mut syncer = EnvironmentSyncer::new();
pub async fn cli(
mut context: EvaluationContext,
mut options: Options,
) -> Result<(), Box<dyn Error>> {
let mut syncer = create_environment_syncer(&context, &mut options);
let configuration = syncer.get_config();
let mut rl = default_rustyline_editor_configuration();
@ -107,7 +223,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
syncer.sync_path_vars(ctx);
if let Err(reason) = syncer.autoenv(ctx) {
print_err(reason, &Text::from(""), ctx);
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = configure_ctrl_c(ctx);
@ -134,8 +250,9 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
// Give ourselves a scope to work in
context.scope.enter_scope();
let history_path = nu_engine::history_path(&configuration);
let _ = rl.load_history(&history_path);
options.history(|file| {
let _ = rl.load_history(&file);
});
let mut session_text = String::new();
let mut line_start: usize = 0;
@ -171,6 +288,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
let prompt_line = prompt.as_string()?;
context.scope.enter_scope();
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
prompt_block.set_redirect(ExternalRedirection::Stdout);
@ -180,8 +298,6 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
} else {
// let env = context.get_env();
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;
context.scope.exit_scope();
@ -189,7 +305,10 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
Ok(result) => match result.collect_string(Tag::unknown()).await {
Ok(string_result) => {
let errors = context.get_errors();
maybe_print_errors(&context, Text::from(prompt_line));
evaluation_context::maybe_print_errors(
&context,
Text::from(prompt_line),
);
context.clear_errors();
if !errors.is_empty() {
@ -199,14 +318,14 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
}
Err(e) => {
crate::cli::print_err(e, &Text::from(prompt_line), &context);
context.host.lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
}
},
Err(e) => {
crate::cli::print_err(e, &Text::from(prompt_line), &context);
context.host.lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
@ -274,7 +393,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
if let Err(reason) = syncer.autoenv(ctx) {
print_err(reason, &Text::from(""), ctx);
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = configure_rustyline_editor(&mut rl, config);
@ -282,28 +401,33 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
match line {
LineResult::Success(line) => {
rl.add_history_entry(&line);
let _ = rl.save_history(&history_path);
maybe_print_errors(&context, Text::from(session_text.clone()));
options.history(|file| {
rl.add_history_entry(&line);
let _ = rl.save_history(&file);
});
evaluation_context::maybe_print_errors(&context, Text::from(session_text.clone()));
}
LineResult::ClearHistory => {
rl.clear_history();
let _ = rl.save_history(&history_path);
options.history(|file| {
rl.clear_history();
let _ = rl.save_history(&file);
});
}
LineResult::Error(line, err) => {
rl.add_history_entry(&line);
let _ = rl.save_history(&history_path);
LineResult::Error(line, reason) => {
options.history(|file| {
rl.add_history_entry(&line);
let _ = rl.save_history(&file);
});
print_err(err, &Text::from(session_text.clone()), &context);
maybe_print_errors(&context, Text::from(session_text.clone()));
context.with_host(|host| host.print_err(reason, &Text::from(session_text.clone())));
}
LineResult::CtrlC => {
let config_ctrlc_exit = config::config(Tag::unknown())?
.get("ctrlc_exit")
let config_ctrlc_exit = configuration
.var("ctrlc_exit")
.map(|s| s.value.is_true())
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
@ -312,7 +436,10 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
if ctrlcbreak {
let _ = rl.save_history(&history_path);
options.history(|file| {
let _ = rl.save_history(&file);
});
std::process::exit(0);
} else {
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
@ -336,7 +463,9 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
// we are ok if we can not save history
let _ = rl.save_history(&history_path);
options.history(|file| {
let _ = rl.save_history(&file);
});
Ok(())
}
@ -426,14 +555,14 @@ fn current_branch() -> String {
#[cfg(test)]
mod tests {
use nu_engine::basic_evaluation_context;
use nu_engine::EvaluationContext;
#[quickcheck]
fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() {
let context = basic_evaluation_context().unwrap();
let context = EvaluationContext::basic().unwrap();
let _ = nu_parser::classify_block(&lite_block, &context.scope);
}
true

View File

@ -15,7 +15,8 @@ pub struct PathSuggestion {
impl PathCompleter {
pub fn path_suggestions(&self, partial: &str, matcher: &dyn Matcher) -> Vec<PathSuggestion> {
let expanded = nu_parser::expand_ndots(partial);
let expanded = expanded.as_ref();
let expanded = expanded.replace(std::path::is_separator, &SEP.to_string());
let expanded: &str = expanded.as_ref();
let (base_dir_name, partial) = match expanded.rfind(SEP) {
Some(pos) => expanded.split_at(pos + SEP.len_utf8()),

View File

@ -163,8 +163,8 @@ mod tests {
use super::EnvironmentSyncer;
use indexmap::IndexMap;
use nu_data::config::tests::FakeConfig;
use nu_engine::basic_evaluation_context;
use nu_engine::Env;
use nu_engine::EvaluationContext;
use nu_errors::ShellError;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;
@ -179,7 +179,7 @@ mod tests {
#[test]
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
{
let mut ctx = basic_evaluation_context()?;
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
@ -282,7 +282,7 @@ mod tests {
#[test]
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
{
let mut ctx = basic_evaluation_context()?;
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
@ -381,7 +381,7 @@ mod tests {
#[test]
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
let mut ctx = basic_evaluation_context()?;
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
@ -457,7 +457,7 @@ mod tests {
#[test]
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
) -> Result<(), ShellError> {
let mut ctx = basic_evaluation_context()?;
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![
@ -544,7 +544,7 @@ mod tests {
#[test]
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
) -> Result<(), ShellError> {
let mut ctx = basic_evaluation_context()?;
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![

View File

@ -1,38 +1,64 @@
use rustyline::{KeyCode, Modifiers};
use serde::{Deserialize, Serialize};
fn convert_keypress(keypress: KeyPress) -> rustyline::KeyPress {
match keypress {
KeyPress::UnknownEscSeq => rustyline::KeyPress::UnknownEscSeq,
KeyPress::Backspace => rustyline::KeyPress::Backspace,
KeyPress::BackTab => rustyline::KeyPress::BackTab,
KeyPress::BracketedPasteStart => rustyline::KeyPress::BracketedPasteStart,
KeyPress::BracketedPasteEnd => rustyline::KeyPress::BracketedPasteEnd,
KeyPress::Char(c) => rustyline::KeyPress::Char(c),
KeyPress::ControlDown => rustyline::KeyPress::ControlDown,
KeyPress::ControlLeft => rustyline::KeyPress::ControlLeft,
KeyPress::ControlRight => rustyline::KeyPress::ControlRight,
KeyPress::ControlUp => rustyline::KeyPress::ControlUp,
KeyPress::Ctrl(c) => rustyline::KeyPress::Ctrl(c),
KeyPress::Delete => rustyline::KeyPress::Delete,
KeyPress::Down => rustyline::KeyPress::Down,
KeyPress::End => rustyline::KeyPress::End,
KeyPress::Enter => rustyline::KeyPress::Enter,
KeyPress::Esc => rustyline::KeyPress::Esc,
KeyPress::F(u) => rustyline::KeyPress::F(u),
KeyPress::Home => rustyline::KeyPress::Home,
KeyPress::Insert => rustyline::KeyPress::Insert,
KeyPress::Left => rustyline::KeyPress::Left,
KeyPress::Meta(c) => rustyline::KeyPress::Meta(c),
KeyPress::Null => rustyline::KeyPress::Null,
KeyPress::PageDown => rustyline::KeyPress::PageDown,
KeyPress::PageUp => rustyline::KeyPress::PageUp,
KeyPress::Right => rustyline::KeyPress::Right,
KeyPress::ShiftDown => rustyline::KeyPress::ShiftDown,
KeyPress::ShiftLeft => rustyline::KeyPress::ShiftLeft,
KeyPress::ShiftRight => rustyline::KeyPress::ShiftRight,
KeyPress::ShiftUp => rustyline::KeyPress::ShiftUp,
KeyPress::Tab => rustyline::KeyPress::Tab,
KeyPress::Up => rustyline::KeyPress::Up,
pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent {
match key_event {
KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None),
KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None),
KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None),
KeyEvent::BracketedPasteStart => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None)
}
KeyEvent::BracketedPasteEnd => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None)
}
KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None),
KeyEvent::ControlDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL))
}
KeyEvent::ControlLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL))
}
KeyEvent::ControlRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL))
}
KeyEvent::ControlUp => {
convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL))
}
KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c),
KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None),
KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None),
KeyEvent::End => convert_to_rl_keyevent(rustyline::KeyCode::End, None),
KeyEvent::Enter => convert_to_rl_keyevent(rustyline::KeyCode::Enter, None),
KeyEvent::Esc => convert_to_rl_keyevent(rustyline::KeyCode::Esc, None),
KeyEvent::F(u) => convert_to_rl_keyevent(rustyline::KeyCode::F(u), None),
KeyEvent::Home => convert_to_rl_keyevent(rustyline::KeyCode::Home, None),
KeyEvent::Insert => convert_to_rl_keyevent(rustyline::KeyCode::Insert, None),
KeyEvent::Left => convert_to_rl_keyevent(rustyline::KeyCode::Left, None),
KeyEvent::Meta(c) => rustyline::KeyEvent::new(c, Modifiers::NONE),
KeyEvent::Null => convert_to_rl_keyevent(rustyline::KeyCode::Null, None),
KeyEvent::PageDown => convert_to_rl_keyevent(rustyline::KeyCode::PageDown, None),
KeyEvent::PageUp => convert_to_rl_keyevent(rustyline::KeyCode::PageUp, None),
KeyEvent::Right => convert_to_rl_keyevent(rustyline::KeyCode::Right, None),
KeyEvent::ShiftDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftUp => convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::SHIFT)),
KeyEvent::Tab => convert_to_rl_keyevent(rustyline::KeyCode::Tab, None),
KeyEvent::Up => convert_to_rl_keyevent(rustyline::KeyCode::Up, None),
}
}
fn convert_to_rl_keyevent(key_event: KeyCode, modifier: Option<Modifiers>) -> rustyline::KeyEvent {
rustyline::KeyEvent {
0: key_event,
1: modifier.unwrap_or(Modifiers::NONE),
}
}
@ -97,7 +123,9 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
match cmd {
Cmd::Abort => rustyline::Cmd::Abort,
Cmd::AcceptLine => rustyline::Cmd::AcceptLine,
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine,
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine {
accept_in_the_middle: false,
},
Cmd::BeginningOfHistory => rustyline::Cmd::BeginningOfHistory,
Cmd::CapitalizeWord => rustyline::Cmd::CapitalizeWord,
Cmd::ClearScreen => rustyline::Cmd::ClearScreen,
@ -140,18 +168,18 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
}
}
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyPress, rustyline::Cmd) {
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) {
(
convert_keypress(keybinding.key),
convert_keyevent(keybinding.key),
convert_cmd(keybinding.binding),
)
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum KeyPress {
pub enum KeyEvent {
/// Unsupported escape sequence (on unix platform)
UnknownEscSeq,
/// ⌫ or `KeyPress::Ctrl('H')`
/// ⌫ or `KeyEvent::Ctrl('H')`
Backspace,
/// ⇤ (usually Shift-Tab)
BackTab,
@ -177,9 +205,9 @@ pub enum KeyPress {
Down,
/// ⇲
End,
/// ↵ or `KeyPress::Ctrl('M')`
/// ↵ or `KeyEvent::Ctrl('M')`
Enter,
/// Escape or `KeyPress::Ctrl('[')`
/// Escape or `KeyEvent::Ctrl('[')`
Esc,
/// Function key
F(u8),
@ -191,7 +219,7 @@ pub enum KeyPress {
Left,
/// Escape-char or Alt-char
Meta(char),
/// `KeyPress::Char('\0')`
/// `KeyEvent::Char('\0')`
Null,
/// ⇟
PageDown,
@ -207,7 +235,7 @@ pub enum KeyPress {
ShiftRight,
/// Shift-↑
ShiftUp,
/// ⇥ or `KeyPress::Ctrl('I')`
/// ⇥ or `KeyEvent::Ctrl('I')`
Tab,
/// ↑ arrow key
Up,
@ -399,7 +427,7 @@ pub type RepeatCount = usize;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Keybinding {
key: KeyPress,
key: KeyEvent,
binding: Cmd,
}

View File

@ -28,6 +28,7 @@ pub mod types;
pub use crate::cli::cli;
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
pub use crate::cli::{NuScript, Options};
pub use crate::env::environment_syncer::EnvironmentSyncer;
pub use nu_command::commands::default_context::create_default_context;

View File

@ -5,7 +5,10 @@ use std::error::Error;
use crate::prelude::*;
#[allow(unused_imports)]
use nu_command::script::LineResult;
use nu_engine::script::LineResult;
#[cfg(feature = "rustyline-support")]
use crate::keybinding::{convert_keyevent, KeyEvent};
#[cfg(feature = "rustyline-support")]
use crate::shell::Helper;
@ -16,7 +19,7 @@ use rustyline::{
config::Configurer,
config::{ColorMode, CompletionType, Config},
error::ReadlineError,
At, Cmd, Editor, KeyPress, Movement, Word,
At, Cmd, Editor, Movement, Word,
};
#[cfg(feature = "rustyline-support")]
@ -40,22 +43,27 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(not(windows))]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
let config = Config::builder().color_mode(ColorMode::Forced).build();
let config = Config::builder()
.check_cursor_position(true)
.color_mode(ColorMode::Forced)
.build();
let mut rl: Editor<_> = Editor::with_config(config);
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
rl.bind_sequence(
KeyPress::ControlLeft,
convert_keyevent(KeyEvent::ControlLeft),
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
);
rl.bind_sequence(
KeyPress::ControlRight,
convert_keyevent(KeyEvent::ControlRight),
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
);
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
rl.bind_sequence(
convert_keyevent(KeyEvent::BracketedPasteStart),
rustyline::Cmd::Noop,
);
// Let's set the defaults up front and then override them later if the user indicates
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
rl.set_max_history_size(100);

View File

@ -57,6 +57,7 @@ impl rustyline::completion::Completer for Helper {
}
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))
}
@ -148,7 +149,6 @@ impl rustyline::Helper for Helper {}
#[cfg(test)]
mod tests {
use super::*;
use nu_engine::basic_evaluation_context;
use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer;
@ -162,7 +162,7 @@ mod tests {
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1);
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);
@ -182,7 +182,7 @@ mod tests {
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30);
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);

View File

@ -5,25 +5,25 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.28.0"
version = "0.29.0"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.28.0", path = "../nu-data" }
nu-engine = { version = "0.28.0", path = "../nu-engine" }
nu-errors = { version = "0.28.0", path = "../nu-errors" }
nu-json = { version = "0.28.0", path = "../nu-json" }
nu-parser = { version = "0.28.0", path = "../nu-parser" }
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
nu-source = { version = "0.28.0", path = "../nu-source" }
nu-stream = { version = "0.28.0", path = "../nu-stream" }
nu-table = { version = "0.28.0", path = "../nu-table" }
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
nu-data = { version = "0.29.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
@ -62,6 +62,7 @@ indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
log = "0.4.14"
md5 = "0.7.0"
meval = "0.2.0"
minus = { version = "3.3.0", optional = true, features = ["async_std_lib", "search"] }
num-bigint = { version = "0.3.1", features = ["serde"] }
@ -78,7 +79,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "7.1.0", optional = true }
rustyline = { version = "8.0.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"
@ -124,6 +125,7 @@ shadow-rs = "0.5"
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
hamcrest2 = "0.3.0"
[features]
clipboard-cli = ["arboard"]

View File

@ -20,11 +20,9 @@ pub(crate) mod chart;
pub(crate) mod classified;
#[cfg(feature = "clipboard-cli")]
pub(crate) mod clip;
pub mod command;
pub(crate) mod compact;
pub(crate) mod config;
pub(crate) mod constants;
pub(crate) mod count;
pub(crate) mod cp;
pub(crate) mod date;
pub(crate) mod debug;
@ -73,6 +71,7 @@ pub(crate) mod insert;
pub(crate) mod into_int;
pub(crate) mod keep;
pub(crate) mod last;
pub(crate) mod length;
pub(crate) mod let_;
pub(crate) mod let_env;
pub(crate) mod lines;
@ -153,9 +152,8 @@ pub(crate) use char_::Char;
pub(crate) use chart::Chart;
pub(crate) use compact::Compact;
pub(crate) use config::{
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
Config, ConfigClear, ConfigGet, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
};
pub(crate) use count::Count;
pub(crate) use cp::Cpy;
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
pub(crate) use debug::Debug;
@ -186,25 +184,25 @@ pub(crate) use first::First;
pub(crate) use flatten::Command as Flatten;
pub(crate) use format::{FileSize, Format};
pub(crate) use from::From;
pub(crate) use from_csv::FromCSV;
pub(crate) use from_eml::FromEML;
pub(crate) use from_csv::FromCsv;
pub(crate) use from_eml::FromEml;
pub(crate) use from_ics::FromIcs;
pub(crate) use from_ini::FromINI;
pub(crate) use from_json::FromJSON;
pub(crate) use from_ods::FromODS;
pub(crate) use from_ssv::FromSSV;
pub(crate) use from_toml::FromTOML;
pub(crate) use from_tsv::FromTSV;
pub(crate) use from_url::FromURL;
pub(crate) use from_ini::FromIni;
pub(crate) use from_json::FromJson;
pub(crate) use from_ods::FromOds;
pub(crate) use from_ssv::FromSsv;
pub(crate) use from_toml::FromToml;
pub(crate) use from_tsv::FromTsv;
pub(crate) use from_url::FromUrl;
pub(crate) use from_vcf::FromVcf;
pub(crate) use from_xlsx::FromXLSX;
pub(crate) use from_xml::FromXML;
pub(crate) use from_yaml::FromYAML;
pub(crate) use from_yaml::FromYML;
pub(crate) use from_xlsx::FromXlsx;
pub(crate) use from_xml::FromXml;
pub(crate) use from_yaml::FromYaml;
pub(crate) use from_yaml::FromYml;
pub(crate) use get::Command as Get;
pub(crate) use group_by::Command as GroupBy;
pub(crate) use group_by_date::GroupByDate;
pub(crate) use hash_::{Hash, HashBase64};
pub(crate) use hash_::{Hash, HashBase64, HashMd5};
pub(crate) use headers::Headers;
pub(crate) use help::Help;
pub(crate) use histogram::Histogram;
@ -213,6 +211,7 @@ pub(crate) use insert::Command as Insert;
pub(crate) use into_int::IntoInt;
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
pub(crate) use last::Last;
pub(crate) use length::Length;
pub(crate) use let_::Let;
pub(crate) use let_env::LetEnv;
pub(crate) use lines::Lines;
@ -273,15 +272,15 @@ pub(crate) use table::Table;
pub(crate) use tags::Tags;
pub(crate) use termsize::TermSize;
pub(crate) use to::To;
pub(crate) use to_csv::ToCSV;
pub(crate) use to_html::ToHTML;
pub(crate) use to_json::ToJSON;
pub(crate) use to_csv::ToCsv;
pub(crate) use to_html::ToHtml;
pub(crate) use to_json::ToJson;
pub(crate) use to_md::Command as ToMarkdown;
pub(crate) use to_toml::ToTOML;
pub(crate) use to_tsv::ToTSV;
pub(crate) use to_url::ToURL;
pub(crate) use to_xml::ToXML;
pub(crate) use to_yaml::ToYAML;
pub(crate) use to_toml::ToToml;
pub(crate) use to_tsv::ToTsv;
pub(crate) use to_url::ToUrl;
pub(crate) use to_xml::ToXml;
pub(crate) use to_yaml::ToYaml;
pub(crate) use touch::Touch;
pub(crate) use uniq::Uniq;
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};

View File

@ -9,6 +9,7 @@ pub struct Char;
#[derive(Deserialize)]
struct CharArgs {
name: Tagged<String>,
rest: Vec<Tagged<String>>,
unicode: bool,
}
@ -25,6 +26,7 @@ impl WholeStreamCommand for Char {
SyntaxShape::Any,
"the name of the character to output",
)
.rest(SyntaxShape::String, "multiple unicode bytes")
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
}
@ -53,24 +55,60 @@ impl WholeStreamCommand for Char {
example: r#"char -u 1f378"#,
result: Some(vec![Value::from("\u{1f378}")]),
},
Example {
description: "Output multi-byte unicode character",
example: r#"char -u 1F468 200D 1F466 200D 1F466"#,
result: Some(vec![Value::from(
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
)]),
},
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (CharArgs { name, unicode }, _) = args.process().await?;
let (
CharArgs {
name,
rest,
unicode,
},
_,
) = args.process().await?;
if unicode {
let decoded_char = string_to_unicode_char(&name.item);
if let Some(output) = decoded_char {
if !rest.is_empty() {
// Setup a new buffer to put all the unicode bytes in
let mut multi_byte = String::new();
// Get the first byte
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
// Get the rest of the bytes
for byte_part in rest {
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
}
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
UntaggedValue::string(multi_byte).into_value(name.tag),
)))
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
name.tag(),
))
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
if let Ok(ch) = decoded_char {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(ch).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
name.tag(),
))
}
}
} else {
let special_character = str_to_character(&name.item);
@ -89,10 +127,20 @@ impl WholeStreamCommand for Char {
}
}
fn string_to_unicode_char(s: &str) -> Option<char> {
u32::from_str_radix(s, 16)
fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
let decoded_char = u32::from_str_radix(s, 16)
.ok()
.and_then(std::char::from_u32)
.and_then(std::char::from_u32);
if let Some(ch) = decoded_char {
Ok(ch)
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
t,
))
}
}
fn str_to_character(s: &str) -> Option<String> {

View File

@ -28,7 +28,7 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name);
if !did_find_command(&command.name) {
if !context.host.lock().is_external_cmd(&command.name) {
return Err(ShellError::labeled_error(
"Command not found",
"command not found",
@ -443,35 +443,6 @@ fn spawn(
}
}
pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
#[cfg(not(feature = "which"))]
{
// we can't perform this check, so just assume it can be found
true
}
#[cfg(all(feature = "which", unix))]
{
which::which(name).is_ok()
}
#[cfg(all(feature = "which", windows))]
{
if which::which(name).is_ok() {
true
} else {
// Reference: https://ss64.com/nt/syntax-internal.html
let cmd_builtins = [
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
"rmdir", "start", "time", "title", "type", "ver", "verify", "vol",
];
cmd_builtins.contains(&name)
}
}
}
fn expand_tilde<SI: ?Sized, P, HD>(input: &SI, home_dir: HD) -> std::borrow::Cow<str>
where
SI: AsRef<str>,
@ -538,7 +509,7 @@ mod tests {
#[cfg(feature = "which")]
use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_engine::basic_evaluation_context;
use nu_engine::EvaluationContext;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")]
@ -563,7 +534,7 @@ mod tests {
let input = InputStream::empty();
let mut ctx =
basic_evaluation_context().expect("There was a problem creating a basic context.");
EvaluationContext::basic().expect("There was a problem creating a basic context.");
assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
@ -577,7 +548,7 @@ mod tests {
// async fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false)
// .await?
// .expect("There was a problem running the external command.");

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
pub struct SubCommand;
@ -35,13 +35,19 @@ impl WholeStreamCommand for SubCommand {
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(name_span, &path)?;
result.clear();
config::write(&result, &None)?;
config::write(&result, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::CommandArgs;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_stream::OutputStream;
pub struct Command;
@ -22,9 +22,16 @@ impl WholeStreamCommand for Command {
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let name = args.call_info.name_tag;
let result = nu_data::config::read(name_span, &None)?;
let name = args.call_info.name_tag.clone();
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let result = nu_data::config::read(&name, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -1,13 +1,15 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct GetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
}
#[async_trait]
@ -42,14 +44,21 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (GetArgs { path }, _) = args.process().await?;
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { column_path }, _) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let result = UntaggedValue::row(nu_data::config::read(&name_tag, &None)?).into_value(&name_tag);
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let value = crate::commands::get::get_column_path(&path, &result)?;
let result = UntaggedValue::row(nu_data::config::read(&name, &path)?).into_value(&name);
let value = crate::commands::get::get_column_path(&column_path, &result)?;
Ok(match value {
Value {

View File

@ -1,51 +0,0 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
use std::path::PathBuf;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct LoadArgs {
load: Tagged<PathBuf>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config load"
}
fn signature(&self) -> Signature {
Signature::build("config load").required(
"load",
SyntaxShape::FilePath,
"Path to load the config from",
)
}
fn usage(&self) -> &str {
"Loads the config from the path given"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
}
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let name_span = args.call_info.name_tag.clone();
let (LoadArgs { load }, _) = args.process().await?;
let configuration = load.item().clone();
let result = nu_data::config::read(name_span, &Some(configuration))?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
}

View File

@ -1,7 +1,6 @@
pub mod clear;
pub mod command;
pub mod get;
pub mod load;
pub mod path;
pub mod remove;
pub mod set;
@ -10,7 +9,6 @@ pub mod set_into;
pub use clear::SubCommand as ConfigClear;
pub use command::Command as Config;
pub use get::SubCommand as ConfigGet;
pub use load::SubCommand as ConfigLoad;
pub use path::SubCommand as ConfigPath;
pub use remove::SubCommand as ConfigRemove;
pub use set::SubCommand as ConfigSet;

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
pub struct SubCommand;
@ -33,9 +33,18 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
let path = config::default_path()?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::FilePath(path)).into_value(args.call_info.name_tag),
match args.scope.get_var("config-path") {
Some(
path
@
Value {
value: UntaggedValue::Primitive(Primitive::FilePath(_)),
..
},
) => path,
_ => UntaggedValue::Primitive(Primitive::FilePath(nu_data::config::default_path()?))
.into_value(args.call_info.name_tag),
},
)))
}

View File

@ -1,13 +1,13 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct RemoveArgs {
pub struct Arguments {
remove: Tagged<String>,
}
@ -44,15 +44,24 @@ impl WholeStreamCommand for SubCommand {
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let (RemoveArgs { remove }, _) = args.process().await?;
let scope = args.scope.clone();
let (Arguments { remove }, _) = args.process().await?;
let mut result = nu_data::config::read(name_span, &None)?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(name_span, &path)?;
let key = remove.to_string();
if result.contains_key(&key) {
result.swap_remove(&key);
config::write(&result, &None)?;
config::write(&result, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(remove.tag()),
)])

View File

@ -1,13 +1,15 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
value: Value,
}
@ -58,13 +60,26 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (SetArgs { path, mut value }, _) = args.process().await?;
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (
Arguments {
column_path,
mut value,
},
_,
) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let raw_entries = nu_data::config::read(&name_tag, &None)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name_tag);
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let raw_entries = nu_data::config::read(&name, &path)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name);
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
@ -72,15 +87,15 @@ pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
match configuration.forgiving_insert_data_at_column_path(&path, value) {
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
config::write(&changes.entries, &None)?;
config::write(&changes.entries, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name_tag),
UntaggedValue::Row(changes).into_value(name),
)))
}
Ok(_) => Ok(OutputStream::empty()),

View File

@ -1,13 +1,13 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetIntoArgs {
pub struct Arguments {
set_into: Tagged<String>,
}
@ -43,17 +43,19 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { set_into: v }, input) = args.process().await?;
let (SetIntoArgs { set_into: v }, input) = args.process().await?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
// In the original code, this is set to `Some` if the `load flag is set`
let configuration = None;
let mut result = nu_data::config::read(&name, &path)?;
let rows: Vec<Value> = input.collect().await;
let key = v.to_string();
@ -70,7 +72,7 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value.clone());
config::write(&result, &configuration)?;
config::write(&result, &path)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
@ -81,7 +83,7 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value);
config::write(&result, &configuration)?;
config::write(&result, &path)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -27,10 +27,11 @@ impl WholeStreamCommand for Date {
}
fn usage(&self) -> &str {
"Convert a date to a given time zone.
Use `date list-timezone` to list all supported time zones.
"
"Convert a date to a given time zone."
}
fn extra_usage(&self) -> &str {
"Use 'date list-timezone' to list all supported time zones."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {

View File

@ -1,10 +1,9 @@
use crate::prelude::*;
use nu_engine::basic_evaluation_context;
use nu_engine::whole_stream_command;
use nu_engine::EvaluationContext;
use std::error::Error;
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
let context = basic_evaluation_context()?;
let context = EvaluationContext::basic()?;
{
use crate::commands::*;
@ -29,7 +28,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(ConfigSet),
whole_stream_command(ConfigSetInto),
whole_stream_command(ConfigClear),
whole_stream_command(ConfigLoad),
whole_stream_command(ConfigRemove),
whole_stream_command(ConfigPath),
whole_stream_command(Help),
@ -57,7 +55,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Sleep),
// Statistics
whole_stream_command(Size),
whole_stream_command(Count),
whole_stream_command(Length),
whole_stream_command(Benchmark),
// Metadata
whole_stream_command(Tags),
@ -75,6 +73,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
// Text manipulation
whole_stream_command(Hash),
whole_stream_command(HashBase64),
whole_stream_command(HashMd5),
whole_stream_command(Split),
whole_stream_command(SplitColumn),
whole_stream_command(SplitRow),
@ -190,30 +189,30 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(MathCeil),
// File format output
whole_stream_command(To),
whole_stream_command(ToCSV),
whole_stream_command(ToHTML),
whole_stream_command(ToJSON),
whole_stream_command(ToCsv),
whole_stream_command(ToHtml),
whole_stream_command(ToJson),
whole_stream_command(ToMarkdown),
whole_stream_command(ToTOML),
whole_stream_command(ToTSV),
whole_stream_command(ToURL),
whole_stream_command(ToYAML),
whole_stream_command(ToXML),
whole_stream_command(ToToml),
whole_stream_command(ToTsv),
whole_stream_command(ToUrl),
whole_stream_command(ToYaml),
whole_stream_command(ToXml),
// File format input
whole_stream_command(From),
whole_stream_command(FromCSV),
whole_stream_command(FromEML),
whole_stream_command(FromTSV),
whole_stream_command(FromSSV),
whole_stream_command(FromINI),
whole_stream_command(FromJSON),
whole_stream_command(FromODS),
whole_stream_command(FromTOML),
whole_stream_command(FromURL),
whole_stream_command(FromXLSX),
whole_stream_command(FromXML),
whole_stream_command(FromYAML),
whole_stream_command(FromYML),
whole_stream_command(FromCsv),
whole_stream_command(FromEml),
whole_stream_command(FromTsv),
whole_stream_command(FromSsv),
whole_stream_command(FromIni),
whole_stream_command(FromJson),
whole_stream_command(FromOds),
whole_stream_command(FromToml),
whole_stream_command(FromUrl),
whole_stream_command(FromXlsx),
whole_stream_command(FromXml),
whole_stream_command(FromYaml),
whole_stream_command(FromYml),
whole_stream_command(FromIcs),
whole_stream_command(FromVcf),
// "Private" commands (not intended to be accessed directly)

View File

@ -4,16 +4,16 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
pub struct FromCSV;
pub struct FromCsv;
#[derive(Deserialize)]
pub struct FromCSVArgs {
pub struct FromCsvArgs {
noheaders: bool,
separator: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for FromCSV {
impl WholeStreamCommand for FromCsv {
fn name(&self) -> &str {
"from csv"
}
@ -71,7 +71,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (
FromCSVArgs {
FromCsvArgs {
noheaders,
separator,
},
@ -105,13 +105,13 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromCSV;
use super::FromCsv;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromCSV {})
test_examples(FromCsv {})
}
}

View File

@ -6,18 +6,18 @@ use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
use nu_source::Tagged;
pub struct FromEML;
pub struct FromEml;
const DEFAULT_BODY_PREVIEW: usize = 50;
#[derive(Deserialize, Clone)]
pub struct FromEMLArgs {
pub struct FromEmlArgs {
#[serde(rename(deserialize = "preview-body"))]
preview_body: Option<Tagged<usize>>,
}
#[async_trait]
impl WholeStreamCommand for FromEML {
impl WholeStreamCommand for FromEml {
fn name(&self) -> &str {
"from eml"
}
@ -75,7 +75,7 @@ fn headerfieldvalue_to_value(tag: &Tag, value: &HeaderFieldValue) -> UntaggedVal
async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (eml_args, input): (FromEMLArgs, _) = args.process().await?;
let (eml_args, input): (FromEmlArgs, _) = args.process().await?;
let value = input.collect_string(tag.clone()).await?;
let body_preview = eml_args
@ -121,13 +121,13 @@ async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromEML;
use super::FromEml;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromEML {})
test_examples(FromEml {})
}
}

View File

@ -4,10 +4,10 @@ use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
use std::collections::HashMap;
pub struct FromINI;
pub struct FromIni;
#[async_trait]
impl WholeStreamCommand for FromINI {
impl WholeStreamCommand for FromIni {
fn name(&self) -> &str {
"from ini"
}
@ -86,13 +86,13 @@ async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromINI;
use super::FromIni;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromINI {})
test_examples(FromIni {})
}
}

View File

@ -3,15 +3,15 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromJSON;
pub struct FromJson;
#[derive(Deserialize)]
pub struct FromJSONArgs {
pub struct FromJsonArgs {
objects: bool,
}
#[async_trait]
impl WholeStreamCommand for FromJSON {
impl WholeStreamCommand for FromJson {
fn name(&self) -> &str {
"from json"
}
@ -71,7 +71,7 @@ pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Res
async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (FromJSONArgs { objects }, input) = args.process().await?;
let (FromJsonArgs { objects }, input) = args.process().await?;
let concat_string = input.collect_string(name_tag.clone()).await?;
let string_clone: Vec<_> = concat_string.item.lines().map(|x| x.to_string()).collect();
@ -135,13 +135,13 @@ async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromJSON;
use super::FromJson;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromJSON {})
test_examples(FromJson {})
}
}

View File

@ -6,15 +6,15 @@ use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;
pub struct FromODS;
pub struct FromOds;
#[derive(Deserialize)]
pub struct FromODSArgs {
pub struct FromOdsArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromODS {
impl WholeStreamCommand for FromOds {
fn name(&self) -> &str {
"from ods"
}
@ -41,7 +41,7 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
let span = tag.span;
let (
FromODSArgs {
FromOdsArgs {
noheaders: _noheaders,
},
input,
@ -93,13 +93,13 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromODS;
use super::FromOds;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromODS {})
test_examples(FromOds {})
}
}

View File

@ -6,10 +6,10 @@ use nu_protocol::{
};
use nu_source::Tagged;
pub struct FromSSV;
pub struct FromSsv;
#[derive(Deserialize)]
pub struct FromSSVArgs {
pub struct FromSsvArgs {
noheaders: bool,
#[serde(rename(deserialize = "aligned-columns"))]
aligned_columns: bool,
@ -21,7 +21,7 @@ const STRING_REPRESENTATION: &str = "from ssv";
const DEFAULT_MINIMUM_SPACES: usize = 2;
#[async_trait]
impl WholeStreamCommand for FromSSV {
impl WholeStreamCommand for FromSsv {
fn name(&self) -> &str {
STRING_REPRESENTATION
}
@ -250,7 +250,7 @@ fn from_ssv_string_to_value(
async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (
FromSSVArgs {
FromSsvArgs {
noheaders,
aligned_columns,
minimum_spaces,
@ -489,9 +489,9 @@ mod tests {
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use super::FromSSV;
use super::FromSsv;
use crate::examples::test as test_examples;
test_examples(FromSSV {})
test_examples(FromSsv {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromTOML;
pub struct FromToml;
#[async_trait]
impl WholeStreamCommand for FromTOML {
impl WholeStreamCommand for FromToml {
fn name(&self) -> &str {
"from toml"
}
@ -92,13 +92,13 @@ pub async fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromTOML;
use super::FromToml;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromTOML {})
test_examples(FromToml {})
}
}

View File

@ -4,15 +4,15 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::Signature;
pub struct FromTSV;
pub struct FromTsv;
#[derive(Deserialize)]
pub struct FromTSVArgs {
pub struct FromTsvArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromTSV {
impl WholeStreamCommand for FromTsv {
fn name(&self) -> &str {
"from tsv"
}
@ -36,20 +36,20 @@ impl WholeStreamCommand for FromTSV {
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (FromTSVArgs { noheaders }, input) = args.process().await?;
let (FromTsvArgs { noheaders }, input) = args.process().await?;
from_delimited_data(noheaders, '\t', "TSV", input, name).await
}
#[cfg(test)]
mod tests {
use super::FromTSV;
use super::FromTsv;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromTSV {})
test_examples(FromTsv {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
pub struct FromURL;
pub struct FromUrl;
#[async_trait]
impl WholeStreamCommand for FromURL {
impl WholeStreamCommand for FromUrl {
fn name(&self) -> &str {
"from url"
}
@ -55,13 +55,13 @@ async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromURL;
use super::FromUrl;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromURL {})
test_examples(FromUrl {})
}
}

View File

@ -6,15 +6,15 @@ use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;
pub struct FromXLSX;
pub struct FromXlsx;
#[derive(Deserialize)]
pub struct FromXLSXArgs {
pub struct FromXlsxArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromXLSX {
impl WholeStreamCommand for FromXlsx {
fn name(&self) -> &str {
"from xlsx"
}
@ -40,7 +40,7 @@ async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let span = tag.span;
let (
FromXLSXArgs {
FromXlsxArgs {
noheaders: _noheaders,
},
input,
@ -93,13 +93,13 @@ async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromXLSX;
use super::FromXlsx;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromXLSX {})
test_examples(FromXlsx {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromXML;
pub struct FromXml;
#[async_trait]
impl WholeStreamCommand for FromXML {
impl WholeStreamCommand for FromXml {
fn name(&self) -> &str {
"from xml"
}
@ -298,9 +298,9 @@ mod tests {
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use super::FromXML;
use super::FromXml;
use crate::examples::test as test_examples;
test_examples(FromXML {})
test_examples(FromXml {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromYAML;
pub struct FromYaml;
#[async_trait]
impl WholeStreamCommand for FromYAML {
impl WholeStreamCommand for FromYaml {
fn name(&self) -> &str {
"from yaml"
}
@ -24,10 +24,10 @@ impl WholeStreamCommand for FromYAML {
}
}
pub struct FromYML;
pub struct FromYml;
#[async_trait]
impl WholeStreamCommand for FromYML {
impl WholeStreamCommand for FromYml {
fn name(&self) -> &str {
"from yml"
}
@ -169,7 +169,7 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromYAML {})
test_examples(FromYaml {})
}
#[test]

View File

@ -0,0 +1,133 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tag;
#[derive(Deserialize)]
pub struct Arguments {
pub rest: Vec<ColumnPath>,
}
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"hash md5"
}
fn signature(&self) -> Signature {
Signature::build("hash md5").rest(
SyntaxShape::ColumnPath,
"optionally md5 encode data by column paths",
)
}
fn usage(&self) -> &str {
"md5 encode a value"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "md5 encode a string",
example: "echo 'abcdefghijklmnopqrstuvwxyz' | hash md5",
result: Some(vec![UntaggedValue::string(
"c3fcd3d76192e4007dfb496cca67e13b",
)
.into_untagged_value()]),
},
Example {
description: "md5 encode a file",
example: "open ./nu_0_24_1_windows.zip | hash md5",
result: Some(vec![UntaggedValue::string(
"dcf30f2836a1a99fc55cf72e28272606",
)
.into_untagged_value()]),
},
]
}
}
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process().await?;
let column_paths: Vec<_> = rest;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
)?;
}
ReturnSuccess::value(ret)
}
})
.to_output_stream())
}
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
let md5_digest = md5::compute(s.as_bytes());
Ok(UntaggedValue::string(&format!("{:x}", md5_digest)).into_value(tag))
}
UntaggedValue::Primitive(Primitive::Binary(bytes)) => {
let md5_digest = md5::compute(bytes);
Ok(UntaggedValue::string(&format!("{:x}", md5_digest)).into_value(tag))
}
other => {
let got = format!("got {}", other.type_name());
Err(ShellError::labeled_error(
"value is not supported for hashing as md5",
got,
tag.into().span,
))
}
}
}
#[cfg(test)]
mod tests {
use super::action;
use nu_protocol::{Primitive, UntaggedValue};
use nu_source::Tag;
use nu_test_support::value::string;
#[test]
fn md5_encode_string() {
let word = string("abcdefghijklmnopqrstuvwxyz");
let expected =
UntaggedValue::string("c3fcd3d76192e4007dfb496cca67e13b").into_untagged_value();
let actual = action(&word, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn md5_encode_bytes() {
let bytes = vec![0xC0, 0xFF, 0xEE];
let binary = UntaggedValue::Primitive(Primitive::Binary(bytes)).into_untagged_value();
let expected =
UntaggedValue::string("5f80e231382769b0102b1164cf722d83").into_untagged_value();
let actual = action(&binary, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -1,5 +1,7 @@
mod base64_;
mod command;
mod md5_;
pub use base64_::SubCommand as HashBase64;
pub use command::Command as Hash;
pub use md5_::SubCommand as HashMd5;

View File

@ -29,7 +29,7 @@ impl WholeStreamCommand for Headers {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create headers for a raw string",
description: "Create headers from a raw string",
example: r#"echo "a b c|1 2 3" | split row "|" | split column " " | headers"#,
result: None,
},

View File

@ -1,6 +1,5 @@
use crate::prelude::*;
use nu_data::config::{Conf, NuConfig};
use nu_engine::history_path;
use nu_data::config::{path::history as history_path, NuConfig};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
@ -34,7 +33,7 @@ impl WholeStreamCommand for History {
}
async fn history(args: CommandArgs) -> Result<OutputStream, ShellError> {
let config: Box<dyn Conf> = Box::new(NuConfig::new());
let config = NuConfig::new();
let tag = args.call_info.name_tag.clone();
let (Arguments { clear }, _) = args.process().await?;

View File

@ -13,6 +13,7 @@ pub struct KillArgs {
pub rest: Vec<Tagged<u64>>,
pub force: Tagged<bool>,
pub quiet: Tagged<bool>,
pub signal: Option<Tagged<u32>>,
}
#[async_trait]
@ -22,7 +23,7 @@ impl WholeStreamCommand for Kill {
}
fn signature(&self) -> Signature {
Signature::build("kill")
let signature = Signature::build("kill")
.required(
"pid",
SyntaxShape::Int,
@ -30,7 +31,18 @@ impl WholeStreamCommand for Kill {
)
.rest(SyntaxShape::Int, "rest of processes to kill")
.switch("force", "forcefully kill the process", Some('f'))
.switch("quiet", "won't print anything to the console", Some('q'))
.switch("quiet", "won't print anything to the console", Some('q'));
if cfg!(windows) {
return signature;
}
signature.named(
"signal",
SyntaxShape::Int,
"signal decimal number to be sent instead of the default 15 (unsupported on Windows)",
Some('s'),
)
}
fn usage(&self) -> &str {
@ -53,6 +65,11 @@ impl WholeStreamCommand for Kill {
example: "kill --force 12345",
result: None,
},
Example {
description: "Send INT signal",
example: "kill -s 2 12345",
result: None,
},
]
}
}
@ -64,6 +81,7 @@ async fn kill(args: CommandArgs) -> Result<OutputStream, ShellError> {
rest,
force,
quiet,
signal,
},
..,
) = args.process().await?;
@ -89,7 +107,18 @@ async fn kill(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut cmd = Command::new("kill");
if *force {
if let Some(signal_value) = signal {
return Err(ShellError::labeled_error_with_secondary(
"mixing force and signal options is not supported",
"signal option",
signal_value.tag(),
"force option",
force.tag(),
));
}
cmd.arg("-9");
} else if let Some(signal_value) = signal {
cmd.arg(format!("-{}", signal_value.item().to_string()));
}
cmd.arg(pid.item().to_string());

View File

@ -4,21 +4,21 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue, Value};
pub struct Count;
pub struct Length;
#[derive(Deserialize)]
pub struct CountArgs {
pub struct LengthArgs {
column: bool,
}
#[async_trait]
impl WholeStreamCommand for Count {
impl WholeStreamCommand for Length {
fn name(&self) -> &str {
"count"
"length"
}
fn signature(&self) -> Signature {
Signature::build("count").switch(
Signature::build("length").switch(
"column",
"Calculate number of columns in table",
Some('c'),
@ -31,10 +31,10 @@ impl WholeStreamCommand for Count {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (CountArgs { column }, input) = args.process().await?;
let (LengthArgs { column }, input) = args.process().await?;
let rows: Vec<Value> = input.collect().await;
let count = if column {
let length = if column {
if rows.is_empty() {
0
} else {
@ -42,8 +42,8 @@ impl WholeStreamCommand for Count {
UntaggedValue::Row(dictionary) => dictionary.length(),
_ => {
return Err(ShellError::labeled_error(
"Cannot obtain column count",
"cannot obtain column count",
"Cannot obtain column length",
"cannot obtain column length",
tag,
));
}
@ -53,19 +53,21 @@ impl WholeStreamCommand for Count {
rows.len()
};
Ok(OutputStream::one(UntaggedValue::int(count).into_value(tag)))
Ok(OutputStream::one(
UntaggedValue::int(length).into_value(tag),
))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Count the number of entries in a list",
example: "echo [1 2 3 4 5] | count",
example: "echo [1 2 3 4 5] | length",
result: Some(vec![UntaggedValue::int(5).into()]),
},
Example {
description: "Count the number of columns in the calendar table",
example: "cal | count -c",
example: "cal | length -c",
result: None,
},
]
@ -74,13 +76,13 @@ impl WholeStreamCommand for Count {
#[cfg(test)]
mod tests {
use super::Count;
use super::Length;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Count {})
test_examples(Length {})
}
}

View File

@ -56,59 +56,70 @@ async fn lines(args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(args
.input
.chain(eos)
.map(move |item| {
.filter_map(move |item| {
let leftover_string = leftover_string.clone();
async move {
match item {
Value {
value: UntaggedValue::Primitive(Primitive::String(st)),
..
} => {
let mut leftover_string = leftover_string.lock();
match item {
Value {
value: UntaggedValue::Primitive(Primitive::String(st)),
..
} => {
let mut leftover_string = leftover_string.lock();
let mut buffer = leftover_string.clone();
buffer.push_str(&st);
let lo_lines = (&*leftover_string).lines().map(|x| x.to_string());
let st_lines = st.lines().map(|x| x.to_string());
let mut lines: Vec<String> = lo_lines.chain(st_lines).collect();
let mut lines: Vec<String> =
buffer.lines().map(|x| x.to_string()).collect();
leftover_string.clear();
leftover_string.clear();
if !ends_with_line_ending(&st) {
if let Some(last) = lines.pop() {
leftover_string.push_str(&last);
if !ends_with_line_ending(&st) {
if let Some(last) = lines.pop() {
leftover_string.push_str(&last);
}
}
if !lines.is_empty() {
let success_lines: Vec<_> = lines
.iter()
.map(|x| {
ReturnSuccess::value(
UntaggedValue::string(x).into_untagged_value(),
)
})
.collect();
Some(futures::stream::iter(success_lines))
} else {
None
}
}
let success_lines: Vec<_> = lines
.iter()
.map(|x| {
ReturnSuccess::value(UntaggedValue::string(x).into_untagged_value())
})
.collect();
futures::stream::iter(success_lines)
}
Value {
value: UntaggedValue::Primitive(Primitive::EndOfStream),
..
} => {
let st = (&*leftover_string).lock().clone();
if !st.is_empty() {
futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::string(st).into_untagged_value(),
)])
} else {
futures::stream::iter(vec![])
Value {
value: UntaggedValue::Primitive(Primitive::EndOfStream),
..
} => {
let st = (&*leftover_string).lock().clone();
if !st.is_empty() {
Some(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::string(st).into_untagged_value(),
)]))
} else {
None
}
}
Value {
tag: value_span, ..
} => Some(futures::stream::iter(vec![Err(
ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
value_span,
),
)])),
}
Value {
tag: value_span, ..
} => futures::stream::iter(vec![Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
value_span,
))]),
}
})
.flatten()

View File

@ -74,6 +74,11 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
example: "open file.csv --encoding iso-8859-1 | from csv",
result: None,
},
Example {
description: "Lists the contents of a directory (identical to `ls ../projectB`)",
example: "open ../projectB",
result: None,
},
]
}
}
@ -99,6 +104,8 @@ async fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
let scope = args.scope.clone();
let cwd = PathBuf::from(args.shell_manager.path());
let shell_manager = args.shell_manager.clone();
let name = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone();
let (
OpenArgs {
@ -109,6 +116,17 @@ async fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
_,
) = args.process().await?;
if path.is_dir() {
let args = nu_engine::shell::LsArgs {
path: Some(path),
all: false,
long: false,
short_names: false,
du: false,
};
return shell_manager.ls(args, name, ctrl_c);
}
// TODO: Remove once Streams are supported everywhere!
// As a short term workaround for getting AutoConvert and Bat functionality (Those don't currently support Streams)

View File

@ -143,11 +143,11 @@ async fn maybe_autocd_dir<'a>(
// - the command name ends in a path separator, or
// - it's not a command on the path and no arguments were given.
let name = &cmd.name;
let path_name = if name.ends_with(std::path::MAIN_SEPARATOR)
let path_name = if name.ends_with(std::path::is_separator)
|| (cmd.args.is_empty()
&& PathBuf::from(name).is_dir()
&& dunce::canonicalize(name).is_ok()
&& !crate::commands::classified::external::did_find_command(&name))
&& !ctx.host.lock().is_external_cmd(&name))
{
Some(name)
} else {

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_engine::{script, WholeStreamCommand};
use nu_errors::ShellError;
use nu_parser::expand_path;
@ -50,7 +50,7 @@ pub async fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
let contents = std::fs::read_to_string(expand_path(&filename.item).into_owned());
match contents {
Ok(contents) => {
let result = crate::script::run_script_standalone(contents, true, &ctx, false).await;
let result = script::run_script_standalone(contents, true, &ctx, false).await;
if let Err(err) = result {
ctx.error(err.into());

View File

@ -8,14 +8,48 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
use chrono::{DateTime, FixedOffset, LocalResult, Offset, TimeZone};
use chrono::{DateTime, FixedOffset, Local, LocalResult, Offset, TimeZone, Utc};
#[derive(Deserialize)]
struct Arguments {
timezone: Option<Tagged<String>>,
offset: Option<Tagged<i16>>,
format: Option<Tagged<String>>,
rest: Vec<ColumnPath>,
}
// In case it may be confused with chrono::TimeZone
#[derive(Clone)]
enum Zone {
Utc,
Local,
East(u8),
West(u8),
Error, // we want the nullshell to cast it instead of rust
}
impl Zone {
fn new(i: i16) -> Self {
if i.abs() <= 12 {
// guanranteed here
if i >= 0 {
Self::East(i as u8) // won't go out of range
} else {
Self::West(-i as u8) // same here
}
} else {
Self::Error // Out of range
}
}
fn from_string(s: String) -> Self {
match s.to_lowercase().as_str() {
"utc" | "u" => Self::Utc,
"local" | "l" => Self::Local,
_ => Self::Error,
}
}
}
pub struct SubCommand;
#[async_trait]
@ -26,6 +60,18 @@ impl WholeStreamCommand for SubCommand {
fn signature(&self) -> Signature {
Signature::build("str to-datetime")
.named(
"timezone",
SyntaxShape::String,
"Specify timezone if the input is timestamp, like 'UTC/u' or 'LOCAL/l'",
Some('z'),
)
.named(
"offset",
SyntaxShape::Int,
"Specify timezone by offset if the input is timestamp, like '+8', '-4', prior than timezone",
Some('o'),
)
.named(
"format",
SyntaxShape::String,
@ -63,6 +109,17 @@ impl WholeStreamCommand for SubCommand {
example: "echo '20200904_163918+0000' | str to-datetime -f '%Y%m%d_%H%M%S%z'",
result: None,
},
Example {
description: "Convert to datetime using a specified timezone",
example: "echo '1614434140' | str to-datetime -z 'UTC'",
result: None,
},
Example {
description:
"Convert to datetime using a specified timezone offset (between -12 and 12)",
example: "echo '1614434140' | str to-datetime -o '+9'",
result: None,
},
]
}
}
@ -71,11 +128,42 @@ impl WholeStreamCommand for SubCommand {
struct DatetimeFormat(String);
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { format, rest }, input) = args.process().await?;
let (
Arguments {
timezone,
offset,
format,
rest,
},
input,
) = args.process().await?;
let column_paths: Vec<_> = rest;
let options = if let Some(Tagged { item: fmt, .. }) = format {
// if zone-offset is specified, then zone will be neglected
let zone_options = if let Some(Tagged {
item: zone_offset,
tag: _tag,
}) = offset
{
Some(Tagged {
item: Zone::new(zone_offset),
tag: _tag,
})
} else if let Some(Tagged {
item: zone,
tag: _tag,
}) = timezone
{
Some(Tagged {
item: Zone::from_string(zone),
tag: _tag,
})
} else {
None
};
let format_options = if let Some(Tagged { item: fmt, .. }) = format {
Some(DatetimeFormat(fmt))
} else {
None
@ -84,16 +172,17 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
ReturnSuccess::value(action(&v, &zone_options, &format_options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let options = options.clone();
let zone_options = zone_options.clone();
let format_options = format_options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
Box::new(move |old| action(old, &zone_options, &format_options, old.tag())),
)?;
}
@ -105,12 +194,41 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
options: &Option<DatetimeFormat>,
timezone: &Option<Tagged<Zone>>,
dateformat: &Option<DatetimeFormat>,
tag: impl Into<Tag>,
) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
let out = match options {
let ts = s.parse::<i64>();
// if timezone if specified, first check if the input is a timestamp.
if let Some(tz) = timezone {
if let Ok(t) = ts {
const HOUR: i32 = 3600;
let stampout = match tz.item {
Zone::Utc => UntaggedValue::date(Utc.timestamp(t, 0)),
Zone::Local => UntaggedValue::date(Local.timestamp(t, 0)),
Zone::East(i) => {
let eastoffset = FixedOffset::east((i as i32) * HOUR);
UntaggedValue::date(eastoffset.timestamp(t, 0))
}
Zone::West(i) => {
let westoffset = FixedOffset::west((i as i32) * HOUR);
UntaggedValue::date(westoffset.timestamp(t, 0))
}
Zone::Error => {
return Err(ShellError::labeled_error(
"could not continue to convert timestamp",
"given timezone or offset is invalid",
tz.tag().span,
));
}
};
return Ok(stampout.into_value(tag));
}
};
// if it's not, continue and negelect the timezone option.
let out = match dateformat {
Some(dt) => match DateTime::parse_from_str(s, &dt.0) {
Ok(d) => UntaggedValue::date(d),
Err(reason) => {
@ -165,9 +283,9 @@ fn action(
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, DatetimeFormat, SubCommand};
use super::{action, DatetimeFormat, SubCommand, Zone};
use nu_protocol::{Primitive, UntaggedValue};
use nu_source::Tag;
use nu_source::{Tag, Tagged};
use nu_test_support::value::string;
#[test]
@ -183,7 +301,7 @@ mod tests {
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &fmt_options, Tag::unknown()).unwrap();
let actual = action(&date_str, &None, &fmt_options, Tag::unknown()).unwrap();
match actual.value {
UntaggedValue::Primitive(Primitive::Date(_)) => {}
@ -194,7 +312,35 @@ mod tests {
#[test]
fn takes_iso8601_date_format() {
let date_str = string("2020-08-04T16:39:18+00:00");
let actual = action(&date_str, &None, Tag::unknown()).unwrap();
let actual = action(&date_str, &None, &None, Tag::unknown()).unwrap();
match actual.value {
UntaggedValue::Primitive(Primitive::Date(_)) => {}
_ => panic!("Didn't convert to date"),
}
}
#[test]
fn takes_timestamp_offset() {
let date_str = string("1614434140");
let timezone_option = Some(Tagged {
item: Zone::East(8),
tag: Tag::unknown(),
});
let actual = action(&date_str, &timezone_option, &None, Tag::unknown()).unwrap();
match actual.value {
UntaggedValue::Primitive(Primitive::Date(_)) => {}
_ => panic!("Didn't convert to date"),
}
}
#[test]
fn takes_timestamp() {
let date_str = string("1614434140");
let timezone_option = Some(Tagged {
item: Zone::Local,
tag: Tag::unknown(),
});
let actual = action(&date_str, &timezone_option, &None, Tag::unknown()).unwrap();
match actual.value {
UntaggedValue::Primitive(Primitive::Date(_)) => {}
_ => panic!("Didn't convert to date"),
@ -207,7 +353,7 @@ mod tests {
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &fmt_options, Tag::unknown());
let actual = action(&date_str, &None, &fmt_options, Tag::unknown());
assert!(actual.is_err());
}

View File

@ -7,7 +7,9 @@ use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_table::{draw_table, Alignment, StyledString, TextStyle};
use std::collections::HashMap;
use std::sync::atomic::Ordering;
use std::time::Instant;
#[cfg(feature = "table-pager")]
use {
futures::future::join,

View File

@ -4,16 +4,16 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
pub struct ToCSV;
pub struct ToCsv;
#[derive(Deserialize)]
pub struct ToCSVArgs {
pub struct ToCsvArgs {
noheaders: bool,
separator: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for ToCSV {
impl WholeStreamCommand for ToCsv {
fn name(&self) -> &str {
"to csv"
}
@ -45,7 +45,7 @@ impl WholeStreamCommand for ToCSV {
async fn to_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (
ToCSVArgs {
ToCsvArgs {
separator,
noheaders,
},
@ -80,12 +80,12 @@ async fn to_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToCSV;
use super::ToCsv;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToCSV {})
test_examples(ToCsv {})
}
}

View File

@ -79,10 +79,10 @@ impl Default for HtmlTheme {
#[folder = "assets/"]
struct Assets;
pub struct ToHTML;
pub struct ToHtml;
#[derive(Deserialize)]
pub struct ToHTMLArgs {
pub struct ToHtmlArgs {
html_color: bool,
no_color: bool,
dark: bool,
@ -92,7 +92,7 @@ pub struct ToHTMLArgs {
}
#[async_trait]
impl WholeStreamCommand for ToHTML {
impl WholeStreamCommand for ToHtml {
fn name(&self) -> &str {
"to html"
}
@ -271,7 +271,7 @@ fn get_list_of_theme_names() -> Vec<String> {
async fn to_html(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (
ToHTMLArgs {
ToHtmlArgs {
html_color,
no_color,
dark,
@ -758,6 +758,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToHTML {})
test_examples(ToHtml {})
}
}

View File

@ -7,15 +7,15 @@ use nu_protocol::{
use serde::Serialize;
use serde_json::json;
pub struct ToJSON;
pub struct ToJson;
#[derive(Deserialize)]
pub struct ToJSONArgs {
pub struct ToJsonArgs {
pretty: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for ToJSON {
impl WholeStreamCommand for ToJson {
fn name(&self) -> &str {
"to json"
}
@ -160,7 +160,7 @@ fn json_list(input: &[Value]) -> Result<Vec<serde_json::Value>, ShellError> {
async fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (ToJSONArgs { pretty }, input) = args.process().await?;
let (ToJsonArgs { pretty }, input) = args.process().await?;
let name_span = name_tag.span;
let input: Vec<Value> = input.collect().await;
@ -256,12 +256,12 @@ async fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToJSON;
use super::ToJson;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToJSON {})
test_examples(ToJson {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UnspannedPathMember, UntaggedValue, Value};
pub struct ToTOML;
pub struct ToToml;
#[async_trait]
impl WholeStreamCommand for ToTOML {
impl WholeStreamCommand for ToToml {
fn name(&self) -> &str {
"to toml"
}
@ -195,7 +195,7 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToTOML {})
test_examples(ToToml {})
}
#[test]

View File

@ -4,15 +4,15 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::Signature;
pub struct ToTSV;
pub struct ToTsv;
#[derive(Deserialize)]
pub struct ToTSVArgs {
pub struct ToTsvArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for ToTSV {
impl WholeStreamCommand for ToTsv {
fn name(&self) -> &str {
"to tsv"
}
@ -36,7 +36,7 @@ impl WholeStreamCommand for ToTSV {
async fn to_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (ToTSVArgs { noheaders }, input) = args.process().await?;
let (ToTsvArgs { noheaders }, input) = args.process().await?;
to_delimited_data(noheaders, '\t', "TSV", input, name).await
}
@ -44,12 +44,12 @@ async fn to_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToTSV;
use super::ToTsv;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToTSV {})
test_examples(ToTsv {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value};
pub struct ToURL;
pub struct ToUrl;
#[async_trait]
impl WholeStreamCommand for ToURL {
impl WholeStreamCommand for ToUrl {
fn name(&self) -> &str {
"to url"
}
@ -76,12 +76,12 @@ async fn to_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToURL;
use super::ToUrl;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToURL {})
test_examples(ToUrl {})
}
}

View File

@ -8,15 +8,15 @@ use std::collections::HashSet;
use std::io::Cursor;
use std::io::Write;
pub struct ToXML;
pub struct ToXml;
#[derive(Deserialize)]
pub struct ToXMLArgs {
pub struct ToXmlArgs {
pretty: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for ToXML {
impl WholeStreamCommand for ToXml {
fn name(&self) -> &str {
"to xml"
}
@ -135,7 +135,7 @@ pub fn write_xml_events<W: Write>(
async fn to_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let name_span = name_tag.span;
let (ToXMLArgs { pretty }, input) = args.process().await?;
let (ToXmlArgs { pretty }, input) = args.process().await?;
let input: Vec<Value> = input.collect().await;
let to_process_input = match input.len() {
@ -189,12 +189,12 @@ async fn to_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToXML;
use super::ToXml;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToXML {})
test_examples(ToXml {})
}
}

View File

@ -3,10 +3,10 @@ use nu_engine::WholeStreamCommand;
use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UnspannedPathMember, UntaggedValue, Value};
pub struct ToYAML;
pub struct ToYaml;
#[async_trait]
impl WholeStreamCommand for ToYAML {
impl WholeStreamCommand for ToYaml {
fn name(&self) -> &str {
"to yaml"
}
@ -164,12 +164,12 @@ async fn to_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToYAML;
use super::ToYaml;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ToYAML {})
test_examples(ToYaml {})
}
}

View File

@ -6,14 +6,12 @@ mod stub_generate;
use double_echo::Command as DoubleEcho;
use double_ls::Command as DoubleLs;
use stub_generate::{mock_path, Command as StubOpen};
use nu_engine::basic_evaluation_context;
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::hir::ClassifiedBlock;
use nu_protocol::{ShellTypeName, Value};
use nu_source::AnchorLocation;
use stub_generate::{mock_path, Command as StubOpen};
use crate::commands::{
Append, BuildString, Each, Echo, First, Get, Keep, Last, Let, Nth, Select, StrCollect, Wrap,
@ -26,7 +24,7 @@ use futures::executor::block_on;
pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = basic_evaluation_context()?;
let base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
// Command Doubles
@ -92,7 +90,7 @@ pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = basic_evaluation_context()?;
let base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
whole_stream_command(Echo {}),
@ -149,7 +147,7 @@ pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let base_context = basic_evaluation_context()?;
let base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
// Minimal restricted commands to aid in testing

View File

@ -8,15 +8,11 @@ extern crate indexmap;
mod prelude;
pub mod commands;
mod futures;
pub mod maybe_print_errors;
pub mod script;
pub mod utils;
#[cfg(test)]
mod examples;
pub use crate::maybe_print_errors::maybe_print_errors;
pub use nu_data::config;
pub use nu_data::dict::TaggedListBuilder;
pub use nu_data::primitive;

View File

@ -1,17 +0,0 @@
use nu_engine::EvaluationContext;
use nu_source::Text;
pub fn maybe_print_errors(context: &EvaluationContext, source: Text) -> bool {
let errors = context.current_errors.clone();
let mut errors = errors.lock();
if errors.len() > 0 {
let error = errors[0].clone();
*errors = vec![];
crate::script::print_err(error, &source, context);
true
} else {
false
}
}

View File

@ -46,7 +46,6 @@ macro_rules! trace_out_stream {
}};
}
pub(crate) use crate::commands::command::RunnableContext;
pub(crate) use async_trait::async_trait;
pub(crate) use bigdecimal::BigDecimal;
pub(crate) use futures::{Stream, StreamExt};
@ -58,11 +57,12 @@ pub(crate) use nu_engine::EvaluationContext;
pub(crate) use nu_engine::Example;
pub(crate) use nu_engine::Host;
pub(crate) use nu_engine::RawCommandArgs;
pub(crate) use nu_engine::RunnableContext;
pub(crate) use nu_engine::ShellManager;
pub(crate) use nu_engine::{get_full_help, CommandArgs, Scope, WholeStreamCommand};
pub(crate) use nu_parser::ParserScope;
pub(crate) use nu_protocol::{out, row};
pub(crate) use nu_source::{AnchorLocation, PrettyDebug, Span, SpannedItem, Tag, TaggedItem, Text};
pub(crate) use nu_source::{AnchorLocation, PrettyDebug, Span, SpannedItem, Tag, TaggedItem};
pub(crate) use nu_stream::ToInputStream;
pub(crate) use nu_stream::{InputStream, Interruptible, OutputStream};
pub(crate) use nu_value_ext::ValueExt;
@ -71,7 +71,7 @@ pub(crate) use num_traits::cast::ToPrimitive;
pub(crate) use serde::Deserialize;
pub(crate) use std::collections::VecDeque;
pub(crate) use std::future::Future;
pub(crate) use std::sync::atomic::{AtomicBool, Ordering};
pub(crate) use std::sync::atomic::AtomicBool;
pub(crate) use std::sync::Arc;
#[allow(clippy::wrong_self_convention)]

View File

@ -17,6 +17,21 @@ pub fn nonu() {
args().iter().skip(1).for_each(|arg| print!("{}", arg));
}
pub fn repeater() {
let mut stdout = io::stdout();
let args = args();
let mut args = args.iter().skip(1);
let letter = args.next().expect("needs a character to iterate");
let count = args.next().expect("need the number of times to iterate");
let count: u64 = count.parse().expect("can't convert count to number");
for _ in 0..count {
let _ = write!(stdout, "{}", letter);
}
let _ = stdout.flush();
}
pub fn iecho() {
// println! panics if stdout gets closed, whereas writeln gives us an error
let mut stdout = io::stdout();

View File

@ -1,29 +1,21 @@
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
use nu_test_support::pipeline as input;
use nu_test_support::playground::{says, Playground};
use hamcrest2::assert_that;
use hamcrest2::prelude::*;
#[test]
fn adds_a_row_to_the_end() {
Playground::setup("append_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
Andrés N. Robalino
Jonathan Turner
Yehuda Katz
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| lines
Playground::setup("append_test_1", |_, nu| {
assert_that!(
nu.pipeline(&input(
r#"
echo [ "Andrés N. Robalino", "Jonathan Turner", "Yehuda Katz" ]
| append "pollo loco"
| nth 3
"#
));
assert_eq!(actual.out, "pollo loco");
"#
)),
says().to_stdout("pollo loco")
);
})
}

View File

@ -33,7 +33,7 @@ fn cal_friday_the_thirteenths_in_2015() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal --full-year 2015 | default friday 0 | where friday == 13 | count
cal --full-year 2015 | default friday 0 | where friday == 13 | length
"#
));
@ -45,7 +45,7 @@ fn cal_rows_in_2020() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal --full-year 2020 | count
cal --full-year 2020 | length
"#
));

View File

@ -25,7 +25,7 @@ fn discards_rows_where_given_column_is_empty() {
open los_tres_amigos.json
| get amigos
| compact rusty_luck
| count
| length
"#
));
@ -41,7 +41,7 @@ fn discards_empty_rows_by_default() {
echo "[1,2,3,14,null]"
| from json
| compact
| count
| length
"#
));

View File

@ -26,7 +26,7 @@ fn adds_row_data_if_column_missing() {
| get amigos
| default rusty_luck 1
| where rusty_luck == 1
| count
| length
"#
));

View File

@ -13,7 +13,7 @@ fn columns() {
]
| drop column
| get
| count
| length
"#)
);
@ -62,7 +62,7 @@ fn rows() {
#[test]
fn more_rows_than_table_has() {
let actual = nu!(cwd: ".", "date | drop 50 | count");
let actual = nu!(cwd: ".", "date | drop 50 | length");
assert_eq!(actual.out, "0");
}

View File

@ -13,7 +13,7 @@ fn reports_emptiness() {
| get are_empty
| empty? check
| where check
| count
| length
"#
));

View File

@ -17,7 +17,7 @@ fn gets_first_rows_by_amount() {
r#"
ls
| first 3
| count
| length
"#
));
@ -40,7 +40,7 @@ fn gets_all_rows_if_amount_higher_than_all_rows() {
r#"
ls
| first 99
| count
| length
"#
));
@ -58,7 +58,7 @@ fn gets_first_row_when_no_amount_given() {
r#"
ls
| first
| count
| length
"#
));

View File

@ -71,7 +71,7 @@ fn flatten_row_column_explicitly() {
let actual = nu!(
cwd: dirs.test(),
"open katz.json | flatten people | where name == Andres | count"
"open katz.json | flatten people | where name == Andres | length"
);
assert_eq!(actual.out, "1");
@ -105,7 +105,7 @@ fn flatten_row_columns_having_same_column_names_flats_separately() {
let actual = nu!(
cwd: dirs.test(),
"open katz.json | flatten | flatten people city | get city_name | count"
"open katz.json | flatten | flatten people city | get city_name | length"
);
assert_eq!(actual.out, "4");
@ -139,7 +139,7 @@ fn flatten_table_columns_explicitly() {
let actual = nu!(
cwd: dirs.test(),
"open katz.json | flatten city | where people.name == Katz | count"
"open katz.json | flatten city | where people.name == Katz | length"
);
assert_eq!(actual.out, "2");

View File

@ -89,7 +89,7 @@ fn column_paths_are_either_double_quoted_or_regular_unquoted_words_separated_by_
r#"
open sample.toml
| get package."9999"
| count
| length
"#
));
@ -151,24 +151,12 @@ fn errors_fetching_by_column_not_present() {
"#
));
assert!(
actual.err.contains("Unknown column"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("There isn't a column named 'taco'"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("Perhaps you meant 'taconushell'?"),
format!("actual: {:?}", actual.err)
);
assert!(
actual
.err
.contains("Columns available: pizzanushell, taconushell"),
format!("actual: {:?}", actual.err)
);
assert!(actual.err.contains("Unknown column"),);
assert!(actual.err.contains("There isn't a column named 'taco'"),);
assert!(actual.err.contains("Perhaps you meant 'taconushell'?"),);
assert!(actual
.err
.contains("Columns available: pizzanushell, taconushell"),);
})
}
@ -191,20 +179,11 @@ fn errors_fetching_by_column_using_a_number() {
"#
));
assert!(
actual.err.contains("No rows available"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("A row at '0' can't be indexed."),
format!("actual: {:?}", actual.err)
);
assert!(
actual
.err
.contains("Appears to contain columns. Columns available: 0"),
format!("actual: {:?}", actual.err)
)
assert!(actual.err.contains("No rows available"),);
assert!(actual.err.contains("A row at '0' can't be indexed."),);
assert!(actual
.err
.contains("Appears to contain columns. Columns available: 0"),)
})
}
#[test]
@ -226,18 +205,9 @@ fn errors_fetching_by_index_out_of_bounds() {
"#
));
assert!(
actual.err.contains("Row not found"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("There isn't a row indexed at 3"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("The table only has 3 rows (0 to 2)"),
format!("actual: {:?}", actual.err)
)
assert!(actual.err.contains("Row not found"),);
assert!(actual.err.contains("There isn't a row indexed at 3"),);
assert!(actual.err.contains("The table only has 3 rows (0 to 2)"),)
})
}

View File

@ -21,7 +21,7 @@ fn groups() {
open los_tres_caballeros.csv
| group-by rusty_at
| get "10/11/2013"
| count
| length
"#
));

View File

@ -83,3 +83,16 @@ fn error_use_both_flags() {
.err
.contains("only one of --decode and --encode flags can be used"));
}
#[test]
fn md5_works_with_file() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
open sample.db | hash md5
"#
)
);
assert_eq!(actual.out, "4de97601d232c427977ef11db396c951");
}

View File

@ -1,11 +1,11 @@
use nu_test_support::{nu, pipeline};
#[test]
fn help_commands_count() {
fn help_commands_length() {
let actual = nu!(
cwd: ".", pipeline(
r#"
help commands | count
help commands | length
"#
));
@ -16,11 +16,11 @@ fn help_commands_count() {
}
#[test]
fn help_generate_docs_count() {
fn help_generate_docs_length() {
let actual = nu!(
cwd: ".", pipeline(
r#"
help generate_docs | flatten | count
help generate_docs | flatten | length
"#
));

View File

@ -27,7 +27,7 @@ fn gets_last_rows_by_amount() {
r#"
ls
| last 3
| count
| length
"#
));
@ -45,7 +45,7 @@ fn gets_last_row_when_no_amount_given() {
r#"
ls
| last
| count
| length
"#
));
@ -58,7 +58,7 @@ fn requests_more_rows_than_table_has() {
let actual = nu!(
cwd: ".", pipeline(
r#"
date | last 50 | count
date | last 50 | length
"#
));

View File

@ -1,11 +1,11 @@
use nu_test_support::{nu, pipeline};
#[test]
fn count_columns_in_cal_table() {
fn length_columns_in_cal_table() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal | count -c
cal | length -c
"#
));
@ -13,11 +13,11 @@ fn count_columns_in_cal_table() {
}
#[test]
fn count_columns_no_rows() {
fn length_columns_no_rows() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo [] | count -c
echo [] | length -c
"#
));

View File

@ -42,9 +42,9 @@ fn lines_multi_value_split() {
open sample-simple.json
| get first second
| lines
| count
| length
"#
));
assert_eq!(actual.out, "6");
assert_eq!(actual.out, "5");
}

View File

@ -1,5 +1,5 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::{Dirs, Playground};
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
@ -15,7 +15,7 @@ fn lists_regular_files() {
cwd: dirs.test(), pipeline(
r#"
ls
| count
| length
"#
));
@ -37,7 +37,7 @@ fn lists_regular_files_using_asterisk_wildcard() {
cwd: dirs.test(), pipeline(
r#"
ls *.txt
| count
| length
"#
));
@ -59,7 +59,7 @@ fn lists_regular_files_using_question_mark_wildcard() {
cwd: dirs.test(), pipeline(
r#"
ls *.??.txt
| count
| length
"#
));
@ -88,7 +88,7 @@ fn lists_all_files_in_directories_from_stream() {
r#"
echo dir_a dir_b
| each { ls $it }
| count
| length
"#
));
@ -105,7 +105,7 @@ fn does_not_fail_if_glob_matches_empty_directory() {
cwd: dirs.test(), pipeline(
r#"
ls dir_a
| count
| length
"#
));
@ -142,7 +142,7 @@ fn list_files_from_two_parents_up_using_multiple_dots() {
let actual = nu!(
cwd: dirs.test().join("foo/bar"),
r#"
ls ... | count
ls ... | length
"#
);
@ -165,7 +165,7 @@ fn lists_hidden_file_when_explicitly_specified() {
cwd: dirs.test(), pipeline(
r#"
ls .testdotfile
| count
| length
"#
));
@ -199,7 +199,7 @@ fn lists_all_hidden_files_when_glob_contains_dot() {
cwd: dirs.test(), pipeline(
r#"
ls **/.*
| count
| length
"#
));
@ -236,7 +236,7 @@ fn lists_all_hidden_files_when_glob_does_not_contain_dot() {
cwd: dirs.test(), pipeline(
r#"
ls **/*
| count
| length
"#
));
@ -259,7 +259,7 @@ fn lists_files_including_starting_with_dot() {
cwd: dirs.test(), pipeline(
r#"
ls -a
| count
| length
"#
));
@ -269,61 +269,57 @@ fn lists_files_including_starting_with_dot() {
#[test]
fn list_all_columns() {
Playground::setup(
"ls_test_all_columns",
|dirs: Dirs, sandbox: &mut Playground| {
sandbox.with_files(vec![
EmptyFile("Leonardo.yaml"),
EmptyFile("Raphael.json"),
EmptyFile("Donatello.xml"),
EmptyFile("Michelangelo.txt"),
]);
// Normal Operation
let actual = nu!(
cwd: dirs.test(),
"ls | get | to md"
);
let expected = ["name", "type", "size", "modified"].join("");
assert_eq!(actual.out, expected, "column names are incorrect for ls");
// Long
let actual = nu!(
cwd: dirs.test(),
"ls -l | get | to md"
);
let expected = {
#[cfg(unix)]
{
[
"name",
"type",
"target",
"num_links",
"inode",
"readonly",
"mode",
"uid",
"group",
"size",
"created",
"accessed",
"modified",
]
.join("")
}
Playground::setup("ls_test_all_columns", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("Leonardo.yaml"),
EmptyFile("Raphael.json"),
EmptyFile("Donatello.xml"),
EmptyFile("Michelangelo.txt"),
]);
// Normal Operation
let actual = nu!(
cwd: dirs.test(),
"ls | get | to md"
);
let expected = ["name", "type", "size", "modified"].join("");
assert_eq!(actual.out, expected, "column names are incorrect for ls");
// Long
let actual = nu!(
cwd: dirs.test(),
"ls -l | get | to md"
);
let expected = {
#[cfg(unix)]
{
[
"name",
"type",
"target",
"num_links",
"inode",
"readonly",
"mode",
"uid",
"group",
"size",
"created",
"accessed",
"modified",
]
.join("")
}
#[cfg(windows)]
{
[
"name", "type", "target", "readonly", "size", "created", "accessed",
"modified",
]
.join("")
}
};
assert_eq!(
actual.out, expected,
"column names are incorrect for ls long"
);
},
);
#[cfg(windows)]
{
[
"name", "type", "target", "readonly", "size", "created", "accessed", "modified",
]
.join("")
}
};
assert_eq!(
actual.out, expected,
"column names are incorrect for ls long"
);
});
}

View File

@ -70,7 +70,7 @@ fn show_created_paths() {
pipeline(
r#"
mkdir -s dir_1 dir_2 dir_3
| count
| length
"#
));

View File

@ -1,11 +1,7 @@
mod append;
mod autoenv;
mod autoenv_trust;
mod autoenv_untrust;
mod cal;
mod cd;
mod compact;
mod count;
mod cp;
mod def;
mod default;
@ -28,6 +24,7 @@ mod insert;
mod into_int;
mod keep;
mod last;
mod length;
mod lines;
mod ls;
mod math;

View File

@ -28,7 +28,7 @@ fn selects_many_rows() {
ls
| get name
| nth 1 0
| count
| length
"#
));

View File

@ -1,3 +1,4 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
@ -230,3 +231,24 @@ fn errors_if_file_not_found() {
expected
);
}
#[test]
fn open_dir_is_ls() {
Playground::setup("open_dir", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jonathan.txt"),
EmptyFile("andres.txt"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open .
| length
"#
));
assert_eq!(actual.out, "3");
})
}

View File

@ -5,7 +5,7 @@ fn rolls_4_roll() {
let actual = nu!(
cwd: ".", pipeline(
r#"
random dice -d 4 -s 10 | count
random dice -d 4 -s 10 | length
"#
));

View File

@ -36,7 +36,7 @@ fn selects_some_rows() {
ls
| get name
| range 1..2
| count
| length
"#
));

View File

@ -23,7 +23,7 @@ fn changes_the_column_name() {
| wrap name
| rename mosqueteros
| get mosqueteros
| count
| length
"#
));
@ -53,7 +53,7 @@ fn keeps_remaining_original_names_given_less_new_names_than_total_original_names
| default hit "arepa!"
| rename mosqueteros
| get hit
| count
| length
"#
));
@ -83,13 +83,7 @@ fn errors_if_no_columns_present() {
"#
));
assert!(
actual.err.contains("no column names available"),
format!("actual: {:?}", actual.err)
);
assert!(
actual.err.contains("can't rename"),
format!("actual: {:?}", actual.err)
);
assert!(actual.err.contains("no column names available"));
assert!(actual.err.contains("can't rename"));
})
}

View File

@ -76,7 +76,7 @@ fn allows_if_given_unknown_column_name_is_missing() {
[Yehuda Katz 10/11/2013 A]
]
| select rrusty_at first_name
| count
| length
"#
));

View File

@ -22,7 +22,7 @@ fn splits() {
| group-by rusty_at
| split-by type
| get A."10/11/2013"
| count
| length
"#
));

View File

@ -19,7 +19,7 @@ fn to_row() {
| lines
| str trim
| split row ","
| count
| length
"#
));

View File

@ -22,7 +22,7 @@ fn removes_duplicate_rows() {
r#"
open los_tres_caballeros.csv
| uniq
| count
| length
"#
));
@ -52,7 +52,7 @@ fn uniq_values() {
open los_tres_caballeros.csv
| select type
| uniq
| count
| length
"#
));
@ -117,7 +117,7 @@ fn nested_json_structures() {
r#"
open nested_json_structures.json
| uniq
| count
| length
"#
));
@ -133,7 +133,7 @@ fn uniq_when_keys_out_of_order() {
echo '[{"a": "a", "b": [1,2,3]},{"b": [1,2,3], "a": "a"}]'
| from json
| uniq
| count
| length
"#
));

Some files were not shown because too many files have changed in this diff Show More