mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
parent
bb50f1eb14
commit
c231dd32cd
29 changed files with 759 additions and 224 deletions
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -443,20 +443,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.10.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_cursor 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_cursor 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_input 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_input 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_screen 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_screen 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_style 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_style 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_terminal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_terminal 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_cursor"
|
name = "crossterm_cursor"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -466,10 +466,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_input"
|
name = "crossterm_input"
|
||||||
version = "0.3.8"
|
version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_screen 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_screen 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -478,7 +478,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_screen"
|
name = "crossterm_screen"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -488,7 +488,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_style"
|
name = "crossterm_style"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -498,10 +498,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_terminal"
|
name = "crossterm_terminal"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_cursor 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_cursor 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1804,7 +1804,7 @@ dependencies = [
|
||||||
"chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossterm 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossterm 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"derive-new 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"derive-new 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3623,12 +3623,12 @@ dependencies = [
|
||||||
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
|
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
|
||||||
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
||||||
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||||
"checksum crossterm 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d94cd758100e3728e5b9a8b9e2d7f21d6f5babf571770514a9cba5448485df18"
|
"checksum crossterm 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9abce7d7c50e9823ea0c0dbeb8f16d7e247af06d75b4c6244ea0a0998b3a6f35"
|
||||||
"checksum crossterm_cursor 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10235efee04a9d6cb3e98a46714da3b30bf4ed6210c02ab3bab33cdf10f74e63"
|
"checksum crossterm_cursor 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fb4bfd085f17d83e6cd2943f0150d3b4331e465de8dba1750d1966192faf63dc"
|
||||||
"checksum crossterm_input 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a49e74609fe693d994a41b729054dbfb41d2c5fa14d8457113bdfeab28973315"
|
"checksum crossterm_input 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c6dd255ca05a596bae31ec392fdb67a829509bb767213f00f37c6b62814db663"
|
||||||
"checksum crossterm_screen 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "957112221da964bd743451559a62def9b58392747a4676ae8cb2a0fd181d8337"
|
"checksum crossterm_screen 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf294484fc34c22d514c41afc0b97ce74e10ea54d6eb5fe4806d1e1ac0f7b76"
|
||||||
"checksum crossterm_style 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5462cb56aa9572c5e3c1911213da2f9eb23f636253e932e73e7e2e97eef7ddda"
|
"checksum crossterm_style 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b950f8262e29a446a8a976e0290b67a9067ddc9620f9fb37961d2377f0d8c09"
|
||||||
"checksum crossterm_terminal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0a100ca73011f81ddab21c7ffc0b57ac0a3e459fb3874520e41d522321c102"
|
"checksum crossterm_terminal 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "db8546b519e0c26aa1f43a4a4ea45ccb41eaca74b9a753ea1788f9ad90212636"
|
||||||
"checksum crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f874a71b2040c730669ddff805c9bc2a1a2f6de9d7f6aab2ae8d29ccbf8a0617"
|
"checksum crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f874a71b2040c730669ddff805c9bc2a1a2f6de9d7f6aab2ae8d29ccbf8a0617"
|
||||||
"checksum crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b055e7cc627c452e6a9b977022f48a2db6f0ff73df446ca970f95eef9c381d45"
|
"checksum crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b055e7cc627c452e6a9b977022f48a2db6f0ff73df446ca970f95eef9c381d45"
|
||||||
"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
|
"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -33,13 +33,13 @@ futures-sink-preview = "=0.3.0-alpha.17"
|
||||||
futures_codec = "0.2.5"
|
futures_codec = "0.2.5"
|
||||||
term = "0.5.2"
|
term = "0.5.2"
|
||||||
bytes = "0.4.12"
|
bytes = "0.4.12"
|
||||||
log = "0.4.7"
|
log = "0.4.8"
|
||||||
pretty_env_logger = "0.3.0"
|
pretty_env_logger = "0.3.0"
|
||||||
serde = "1.0.97"
|
serde = "1.0.98"
|
||||||
serde_json = "1.0.40"
|
serde_json = "1.0.40"
|
||||||
serde-hjson = "0.9.0"
|
serde-hjson = "0.9.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
serde_derive = "1.0.97"
|
serde_derive = "1.0.98"
|
||||||
serde_bytes = "0.11.1"
|
serde_bytes = "0.11.1"
|
||||||
getset = "0.0.7"
|
getset = "0.0.7"
|
||||||
logos = "0.10.0-rc2"
|
logos = "0.10.0-rc2"
|
||||||
|
@ -73,9 +73,9 @@ regex = "1.2.0"
|
||||||
pretty-hex = "0.1.0"
|
pretty-hex = "0.1.0"
|
||||||
neso = "0.5.0"
|
neso = "0.5.0"
|
||||||
rawkey = "0.1.2"
|
rawkey = "0.1.2"
|
||||||
crossterm = "0.10.1"
|
crossterm = "0.10.2"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
image = "0.22.0"
|
image = "0.22.1"
|
||||||
semver = "0.9.0"
|
semver = "0.9.0"
|
||||||
uuid = {version = "0.7.4", features = [ "v4", "serde" ]}
|
uuid = {version = "0.7.4", features = [ "v4", "serde" ]}
|
||||||
syntect = "3.2.0"
|
syntect = "3.2.0"
|
||||||
|
|
17
README.md
17
README.md
|
@ -79,7 +79,7 @@ We can pipeline this into a command that gets the contents of one of the columns
|
||||||
-------------+----------------------------+---------+---------+------+---------
|
-------------+----------------------------+---------+---------+------+---------
|
||||||
authors | description | edition | license | name | version
|
authors | description | edition | license | name | version
|
||||||
-------------+----------------------------+---------+---------+------+---------
|
-------------+----------------------------+---------+---------+------+---------
|
||||||
[list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.1.2
|
[list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.1.3
|
||||||
-------------+----------------------------+---------+---------+------+---------
|
-------------+----------------------------+---------+---------+------+---------
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -87,11 +87,18 @@ Finally, we can use commands outside of Nu once we have the data we want:
|
||||||
|
|
||||||
```
|
```
|
||||||
/home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it
|
/home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it
|
||||||
0.1.2
|
0.1.3
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we use the variable `$it` to refer to the value being piped to the external command.
|
Here we use the variable `$it` to refer to the value being piped to the external command.
|
||||||
|
|
||||||
|
## Shells
|
||||||
|
|
||||||
|
By default, Nu will work inside of a single directory and allow you to navigate around your filesystem. Sometimes, you're working in multiple directories at the same time. For this, Nu offers a way of adding additional working directories that you can jump between.
|
||||||
|
|
||||||
|
To do so, use the `enter` command, which will allow you create a new shell and enter it at the specified path. You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells. Once you're done with a shell, you can `exit` it and remove it from the ring buffer.
|
||||||
|
|
||||||
|
Finally, to get a list of all the current shells, you can use the `shells` command.
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
||||||
|
@ -127,7 +134,11 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
||||||
| sys | View information about the current system |
|
| sys | View information about the current system |
|
||||||
| open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') |
|
| open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') |
|
||||||
| rm {file or directory} | Remove a file, (for removing directory append '--recursive') |
|
| rm {file or directory} | Remove a file, (for removing directory append '--recursive') |
|
||||||
| exit | Exit the shell |
|
| exit (--now) | Exit the current shell (or all shells) |
|
||||||
|
| enter (path) | Create a new shell and begin at this path |
|
||||||
|
| p | Go to previous shell |
|
||||||
|
| n | Go to next shell |
|
||||||
|
| shells | Display the list of current shells |
|
||||||
|
|
||||||
## Filters on tables (structured data)
|
## Filters on tables (structured data)
|
||||||
| command | description |
|
| command | description |
|
||||||
|
|
24
src/cli.rs
24
src/cli.rs
|
@ -161,9 +161,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
command("from-xml", Box::new(from_xml::from_xml)),
|
command("from-xml", Box::new(from_xml::from_xml)),
|
||||||
command("from-yaml", Box::new(from_yaml::from_yaml)),
|
command("from-yaml", Box::new(from_yaml::from_yaml)),
|
||||||
command("get", Box::new(get::get)),
|
command("get", Box::new(get::get)),
|
||||||
command("exit", Box::new(exit::exit)),
|
command("enter", Box::new(enter::enter)),
|
||||||
|
command("n", Box::new(next::next)),
|
||||||
|
command("p", Box::new(prev::prev)),
|
||||||
command("lines", Box::new(lines::lines)),
|
command("lines", Box::new(lines::lines)),
|
||||||
command("pick", Box::new(pick::pick)),
|
command("pick", Box::new(pick::pick)),
|
||||||
|
command("shells", Box::new(shells::shells)),
|
||||||
command("split-column", Box::new(split_column::split_column)),
|
command("split-column", Box::new(split_column::split_column)),
|
||||||
command("split-row", Box::new(split_row::split_row)),
|
command("split-row", Box::new(split_row::split_row)),
|
||||||
command("lines", Box::new(lines::lines)),
|
command("lines", Box::new(lines::lines)),
|
||||||
|
@ -182,29 +185,30 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
Arc::new(Date),
|
Arc::new(Date),
|
||||||
Arc::new(Where),
|
Arc::new(Where),
|
||||||
Arc::new(Config),
|
Arc::new(Config),
|
||||||
|
Arc::new(Exit),
|
||||||
Arc::new(SkipWhile),
|
Arc::new(SkipWhile),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
context.add_sinks(vec![
|
context.add_sinks(vec![
|
||||||
sink("autoview", Box::new(autoview::autoview)),
|
sink("autoview", Box::new(autoview::autoview)),
|
||||||
sink("clip", Box::new(clip::clip)),
|
sink("clip", Box::new(clip::clip)),
|
||||||
sink("save", Box::new(save::save)),
|
|
||||||
sink("table", Box::new(table::table)),
|
sink("table", Box::new(table::table)),
|
||||||
sink("vtable", Box::new(vtable::vtable)),
|
sink("vtable", Box::new(vtable::vtable)),
|
||||||
|
Arc::new(Save),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
let _ = load_plugins(&mut context);
|
let _ = load_plugins(&mut context);
|
||||||
|
|
||||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||||
let h = crate::shell::Helper::new(context.clone_commands());
|
//let h = crate::shell::Helper::new(context.clone_commands());
|
||||||
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
|
let mut rl: Editor<_> = Editor::with_config(config);
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
let _ = ansi_term::enable_ansi_support();
|
let _ = ansi_term::enable_ansi_support();
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.set_helper(Some(h));
|
//rl.set_helper(Some(h));
|
||||||
let _ = rl.load_history("history.txt");
|
let _ = rl.load_history("history.txt");
|
||||||
|
|
||||||
let ctrl_c = Arc::new(AtomicBool::new(false));
|
let ctrl_c = Arc::new(AtomicBool::new(false));
|
||||||
|
@ -220,10 +224,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cwd = {
|
let cwd = context.shell_manager.path();
|
||||||
let env = context.env.lock().unwrap();
|
|
||||||
env.path().display().to_string()
|
rl.set_helper(Some(crate::shell::Helper::new(
|
||||||
};
|
context.shell_manager.clone(),
|
||||||
|
)));
|
||||||
|
|
||||||
let readline = rl.readline(&format!(
|
let readline = rl.readline(&format!(
|
||||||
"{}{}> ",
|
"{}{}> ",
|
||||||
cwd,
|
cwd,
|
||||||
|
|
|
@ -10,6 +10,7 @@ crate mod command;
|
||||||
crate mod config;
|
crate mod config;
|
||||||
crate mod cp;
|
crate mod cp;
|
||||||
crate mod date;
|
crate mod date;
|
||||||
|
crate mod enter;
|
||||||
crate mod exit;
|
crate mod exit;
|
||||||
crate mod first;
|
crate mod first;
|
||||||
crate mod from_csv;
|
crate mod from_csv;
|
||||||
|
@ -21,13 +22,16 @@ crate mod from_yaml;
|
||||||
crate mod get;
|
crate mod get;
|
||||||
crate mod lines;
|
crate mod lines;
|
||||||
crate mod ls;
|
crate mod ls;
|
||||||
|
crate mod next;
|
||||||
crate mod open;
|
crate mod open;
|
||||||
crate mod pick;
|
crate mod pick;
|
||||||
crate mod plugin;
|
crate mod plugin;
|
||||||
|
crate mod prev;
|
||||||
crate mod ps;
|
crate mod ps;
|
||||||
crate mod reject;
|
crate mod reject;
|
||||||
crate mod rm;
|
crate mod rm;
|
||||||
crate mod save;
|
crate mod save;
|
||||||
|
crate mod shells;
|
||||||
crate mod size;
|
crate mod size;
|
||||||
crate mod skip_while;
|
crate mod skip_while;
|
||||||
crate mod sort_by;
|
crate mod sort_by;
|
||||||
|
@ -48,7 +52,9 @@ crate use command::command;
|
||||||
crate use config::Config;
|
crate use config::Config;
|
||||||
crate use cp::Copycp;
|
crate use cp::Copycp;
|
||||||
crate use date::Date;
|
crate use date::Date;
|
||||||
|
crate use exit::Exit;
|
||||||
crate use open::Open;
|
crate use open::Open;
|
||||||
crate use rm::Remove;
|
crate use rm::Remove;
|
||||||
|
crate use save::Save;
|
||||||
crate use skip_while::SkipWhile;
|
crate use skip_while::SkipWhile;
|
||||||
crate use where_::Where;
|
crate use where_::Where;
|
||||||
|
|
|
@ -1,52 +1,6 @@
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let env = args.env.lock().unwrap();
|
args.shell_manager.cd(args.call_info, args.input)
|
||||||
let cwd = env.path().to_path_buf();
|
|
||||||
|
|
||||||
let path = match args.nth(0) {
|
|
||||||
None => match dirs::home_dir() {
|
|
||||||
Some(o) => o,
|
|
||||||
_ => {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Can not change to home directory",
|
|
||||||
"can not go to home",
|
|
||||||
args.call_info.name_span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(v) => {
|
|
||||||
let target = v.as_string()?;
|
|
||||||
match dunce::canonicalize(cwd.join(target).as_path()) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Can not change to directory",
|
|
||||||
"directory not found",
|
|
||||||
v.span().clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut stream = VecDeque::new();
|
|
||||||
match env::set_current_dir(&path) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_) => {
|
|
||||||
if args.len() > 0 {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Can not change to directory",
|
|
||||||
"directory not found",
|
|
||||||
args.nth(0).unwrap().span().clone(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::string("Can not change to directory"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.push_back(ReturnSuccess::change_cwd(path));
|
|
||||||
Ok(stream.into())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,12 +145,62 @@ impl InternalCommand {
|
||||||
match item? {
|
match item? {
|
||||||
ReturnSuccess::Action(action) => match action {
|
ReturnSuccess::Action(action) => match action {
|
||||||
CommandAction::ChangePath(path) => {
|
CommandAction::ChangePath(path) => {
|
||||||
context.env.lock().unwrap().path = path;
|
context.shell_manager.set_path(path);
|
||||||
}
|
}
|
||||||
CommandAction::AddSpanSource(uuid, span_source) => {
|
CommandAction::AddSpanSource(uuid, span_source) => {
|
||||||
context.add_span_source(uuid, span_source);
|
context.add_span_source(uuid, span_source);
|
||||||
}
|
}
|
||||||
CommandAction::Exit => std::process::exit(0),
|
CommandAction::Exit => std::process::exit(0),
|
||||||
|
CommandAction::EnterShell(location) => {
|
||||||
|
let path = std::path::Path::new(&location);
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
// If it's a directory, add a new filesystem shell
|
||||||
|
context
|
||||||
|
.shell_manager
|
||||||
|
.push(Box::new(FilesystemShell::with_location(location)?));
|
||||||
|
} else {
|
||||||
|
// If it's a file, attempt to open the file as a value and enter it
|
||||||
|
let cwd = context.shell_manager.path();
|
||||||
|
|
||||||
|
let full_path = std::path::PathBuf::from(cwd);
|
||||||
|
|
||||||
|
let (file_extension, contents, contents_tag, _) =
|
||||||
|
crate::commands::open::fetch(
|
||||||
|
&full_path,
|
||||||
|
&location,
|
||||||
|
Span::unknown(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match contents {
|
||||||
|
Value::Primitive(Primitive::String(string)) => {
|
||||||
|
let value = crate::commands::open::parse_as_value(
|
||||||
|
file_extension,
|
||||||
|
string,
|
||||||
|
contents_tag,
|
||||||
|
Span::unknown(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.shell_manager.push(Box::new(ValueShell::new(value)));
|
||||||
|
}
|
||||||
|
value => context
|
||||||
|
.shell_manager
|
||||||
|
.push(Box::new(ValueShell::new(value.tagged(Tag::unknown())))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommandAction::PreviousShell => {
|
||||||
|
context.shell_manager.prev();
|
||||||
|
}
|
||||||
|
CommandAction::NextShell => {
|
||||||
|
context.shell_manager.next();
|
||||||
|
}
|
||||||
|
CommandAction::LeaveShell => {
|
||||||
|
context.shell_manager.pop();
|
||||||
|
if context.shell_manager.is_empty() {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ReturnSuccess::Value(v) => {
|
ReturnSuccess::Value(v) => {
|
||||||
|
@ -298,7 +348,7 @@ impl ExternalCommand {
|
||||||
|
|
||||||
process = Exec::shell(new_arg_string);
|
process = Exec::shell(new_arg_string);
|
||||||
}
|
}
|
||||||
process = process.cwd(context.env.lock().unwrap().path());
|
process = process.cwd(context.shell_manager.path());
|
||||||
|
|
||||||
let mut process = match stream_next {
|
let mut process = match stream_next {
|
||||||
StreamNext::Last => process,
|
StreamNext::Last => process,
|
||||||
|
|
|
@ -6,7 +6,6 @@ use crate::parser::registry::{self, Args};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
@ -20,7 +19,7 @@ pub struct CallInfo {
|
||||||
#[get = "crate"]
|
#[get = "crate"]
|
||||||
pub struct CommandArgs {
|
pub struct CommandArgs {
|
||||||
pub host: Arc<Mutex<dyn Host + Send>>,
|
pub host: Arc<Mutex<dyn Host + Send>>,
|
||||||
pub env: Arc<Mutex<Environment>>,
|
pub shell_manager: ShellManager,
|
||||||
pub call_info: CallInfo,
|
pub call_info: CallInfo,
|
||||||
pub input: InputStream,
|
pub input: InputStream,
|
||||||
}
|
}
|
||||||
|
@ -60,9 +59,13 @@ pub struct SinkCommandArgs {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum CommandAction {
|
pub enum CommandAction {
|
||||||
ChangePath(PathBuf),
|
ChangePath(String),
|
||||||
AddSpanSource(Uuid, SpanSource),
|
AddSpanSource(Uuid, SpanSource),
|
||||||
Exit,
|
Exit,
|
||||||
|
EnterShell(String),
|
||||||
|
PreviousShell,
|
||||||
|
NextShell,
|
||||||
|
LeaveShell,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -80,7 +83,7 @@ impl From<Tagged<Value>> for ReturnValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReturnSuccess {
|
impl ReturnSuccess {
|
||||||
pub fn change_cwd(path: PathBuf) -> ReturnValue {
|
pub fn change_cwd(path: String) -> ReturnValue {
|
||||||
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
|
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::parser::hir::SyntaxType;
|
||||||
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
|
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub struct Copycp;
|
pub struct Copycp;
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ impl Command for Copycp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cp(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn cp(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let mut source = args.env.lock().unwrap().path().to_path_buf();
|
let mut source = PathBuf::from(args.shell_manager.path());
|
||||||
let mut destination = args.env.lock().unwrap().path().to_path_buf();
|
let mut destination = PathBuf::from(args.shell_manager.path());
|
||||||
|
|
||||||
let mut dst = String::new();
|
let mut dst = String::new();
|
||||||
|
|
||||||
|
|
21
src/commands/enter.rs
Normal file
21
src/commands/enter.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use crate::commands::command::CommandAction;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
//TODO: We could also enter a value in the stream
|
||||||
|
if args.len() == 0 {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"Enter requires a path",
|
||||||
|
"needs parameter",
|
||||||
|
args.call_info.name_span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let location = args.expect_nth(0)?.as_string()?;
|
||||||
|
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
||||||
|
location,
|
||||||
|
)))]
|
||||||
|
.into())
|
||||||
|
}
|
|
@ -1,7 +1,39 @@
|
||||||
use crate::commands::command::CommandAction;
|
use crate::commands::command::CommandAction;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
|
use crate::parser::registry::{CommandConfig, NamedType};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub struct Exit;
|
||||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
|
|
||||||
|
impl Command for Exit {
|
||||||
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
exit(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> CommandConfig {
|
||||||
|
let mut named: IndexMap<String, NamedType> = IndexMap::new();
|
||||||
|
named.insert("now".to_string(), NamedType::Switch);
|
||||||
|
|
||||||
|
CommandConfig {
|
||||||
|
name: self.name().to_string(),
|
||||||
|
positional: vec![],
|
||||||
|
rest_positional: false,
|
||||||
|
named,
|
||||||
|
is_sink: false,
|
||||||
|
is_filter: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
if args.call_info.args.has("now") {
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
|
||||||
|
} else {
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::LeaveShell))].into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +1,6 @@
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::dir_entry_dict;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let env = args.env.lock().unwrap();
|
args.shell_manager.ls(args.call_info, args.input)
|
||||||
let path = env.path.to_path_buf();
|
|
||||||
let cwd = path.clone();
|
|
||||||
let mut full_path = PathBuf::from(path);
|
|
||||||
match &args.nth(0) {
|
|
||||||
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let entries = glob::glob(&full_path.to_string_lossy());
|
|
||||||
|
|
||||||
if entries.is_err() {
|
|
||||||
return Err(ShellError::string("Invalid pattern."));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut shell_entries = VecDeque::new();
|
|
||||||
let entries: Vec<_> = entries.unwrap().collect();
|
|
||||||
|
|
||||||
// If this is a single entry, try to display the contents of the entry if it's a directory
|
|
||||||
if entries.len() == 1 {
|
|
||||||
if let Ok(entry) = &entries[0] {
|
|
||||||
if entry.is_dir() {
|
|
||||||
let entries = std::fs::read_dir(&full_path);
|
|
||||||
|
|
||||||
let entries = match entries {
|
|
||||||
Err(e) => {
|
|
||||||
if let Some(s) = args.nth(0) {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
e.to_string(),
|
|
||||||
e.to_string(),
|
|
||||||
s.span(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
e.to_string(),
|
|
||||||
e.to_string(),
|
|
||||||
args.call_info.name_span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(o) => o,
|
|
||||||
};
|
|
||||||
for entry in entries {
|
|
||||||
let entry = entry?;
|
|
||||||
let filepath = entry.path();
|
|
||||||
let filename = filepath.strip_prefix(&cwd).unwrap();
|
|
||||||
let value = dir_entry_dict(
|
|
||||||
filename,
|
|
||||||
&entry.metadata()?,
|
|
||||||
Tag::unknown_origin(args.call_info.name_span),
|
|
||||||
)?;
|
|
||||||
shell_entries.push_back(ReturnSuccess::value(value))
|
|
||||||
}
|
|
||||||
return Ok(shell_entries.to_output_stream());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate the entries from the glob and add each
|
|
||||||
for entry in entries {
|
|
||||||
if let Ok(entry) = entry {
|
|
||||||
let filename = entry.strip_prefix(&cwd).unwrap();
|
|
||||||
let metadata = std::fs::metadata(&entry)?;
|
|
||||||
let value = dir_entry_dict(
|
|
||||||
filename,
|
|
||||||
&metadata,
|
|
||||||
Tag::unknown_origin(args.call_info.name_span),
|
|
||||||
)?;
|
|
||||||
shell_entries.push_back(ReturnSuccess::value(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(shell_entries.to_output_stream())
|
|
||||||
}
|
}
|
||||||
|
|
7
src/commands/next.rs
Normal file
7
src/commands/next.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::commands::command::CommandAction;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn next(_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::NextShell))].into())
|
||||||
|
}
|
|
@ -12,11 +12,7 @@ command! {
|
||||||
let span = args.call_info.name_span;
|
let span = args.call_info.name_span;
|
||||||
|
|
||||||
let cwd = args
|
let cwd = args
|
||||||
.env
|
.shell_manager.path();
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.path()
|
|
||||||
.to_path_buf();
|
|
||||||
|
|
||||||
let full_path = PathBuf::from(cwd);
|
let full_path = PathBuf::from(cwd);
|
||||||
|
|
||||||
|
|
7
src/commands/prev.rs
Normal file
7
src/commands/prev.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::commands::command::CommandAction;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn prev(_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::PreviousShell))].into())
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use crate::prelude::*;
|
||||||
|
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct Remove;
|
pub struct Remove;
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ impl Command for Remove {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let mut full_path = args.env.lock().unwrap().path().to_path_buf();
|
let mut full_path = PathBuf::from(args.shell_manager.path());
|
||||||
|
|
||||||
match args
|
match args
|
||||||
.nth(0)
|
.nth(0)
|
||||||
|
|
|
@ -5,11 +5,40 @@ use crate::commands::to_toml::value_to_toml_value;
|
||||||
use crate::commands::to_yaml::value_to_yaml_value;
|
use crate::commands::to_yaml::value_to_yaml_value;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::{Primitive, Value};
|
use crate::object::{Primitive, Value};
|
||||||
|
use crate::parser::registry::{CommandConfig, NamedType};
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::SpanSource;
|
use crate::SpanSource;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub struct Save;
|
||||||
|
|
||||||
|
impl Sink for Save {
|
||||||
|
fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> {
|
||||||
|
save(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"save"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> CommandConfig {
|
||||||
|
let mut named: IndexMap<String, NamedType> = IndexMap::new();
|
||||||
|
named.insert("raw".to_string(), NamedType::Switch);
|
||||||
|
|
||||||
|
CommandConfig {
|
||||||
|
name: self.name().to_string(),
|
||||||
|
positional: vec![],
|
||||||
|
rest_positional: false,
|
||||||
|
named,
|
||||||
|
is_sink: false,
|
||||||
|
is_filter: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
|
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
|
||||||
let cwd = args.ctx.env.lock().unwrap().path().to_path_buf();
|
let cwd = args.ctx.shell_manager.path();
|
||||||
let mut full_path = PathBuf::from(cwd);
|
let mut full_path = PathBuf::from(cwd);
|
||||||
|
|
||||||
let save_raw = if args.call_info.args.has("raw") {
|
let save_raw = if args.call_info.args.has("raw") {
|
||||||
|
|
18
src/commands/shells.rs
Normal file
18
src/commands/shells.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::object::TaggedDictBuilder;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn shells(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let mut shells_out = VecDeque::new();
|
||||||
|
let span = args.call_info.name_span;
|
||||||
|
|
||||||
|
for shell in args.shell_manager.shells.lock().unwrap().iter() {
|
||||||
|
let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span));
|
||||||
|
dict.insert("name", shell.name());
|
||||||
|
dict.insert("path", shell.path());
|
||||||
|
|
||||||
|
shells_out.push_back(dict.into_tagged_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(shells_out.to_output_stream())
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ pub struct Context {
|
||||||
sinks: IndexMap<String, Arc<dyn Sink>>,
|
sinks: IndexMap<String, Arc<dyn Sink>>,
|
||||||
crate source_map: SourceMap,
|
crate source_map: SourceMap,
|
||||||
crate host: Arc<Mutex<dyn Host + Send>>,
|
crate host: Arc<Mutex<dyn Host + Send>>,
|
||||||
crate env: Arc<Mutex<Environment>>,
|
crate shell_manager: ShellManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
@ -48,7 +48,7 @@ impl Context {
|
||||||
sinks: indexmap::IndexMap::new(),
|
sinks: indexmap::IndexMap::new(),
|
||||||
source_map: SourceMap::new(),
|
source_map: SourceMap::new(),
|
||||||
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
|
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
|
||||||
env: Arc::new(Mutex::new(Environment::basic()?)),
|
shell_manager: ShellManager::basic()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,10 +96,6 @@ impl Context {
|
||||||
command.run(command_args)
|
command.run(command_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_commands(&self) -> indexmap::IndexMap<String, Arc<dyn Command>> {
|
|
||||||
self.commands.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
crate fn has_command(&self, name: &str) -> bool {
|
crate fn has_command(&self, name: &str) -> bool {
|
||||||
self.commands.contains_key(name)
|
self.commands.contains_key(name)
|
||||||
}
|
}
|
||||||
|
@ -118,7 +114,7 @@ impl Context {
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let command_args = CommandArgs {
|
let command_args = CommandArgs {
|
||||||
host: self.host.clone(),
|
host: self.host.clone(),
|
||||||
env: self.env.clone(),
|
shell_manager: self.shell_manager.clone(),
|
||||||
call_info: CallInfo {
|
call_info: CallInfo {
|
||||||
name_span,
|
name_span,
|
||||||
source_map,
|
source_map,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
crate mod environment;
|
|
||||||
crate mod host;
|
crate mod host;
|
||||||
|
|
||||||
crate use self::environment::Environment;
|
|
||||||
crate use self::host::Host;
|
crate use self::host::Host;
|
||||||
|
|
18
src/env/environment.rs
vendored
18
src/env/environment.rs
vendored
|
@ -1,18 +0,0 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Environment {
|
|
||||||
crate path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Environment {
|
|
||||||
pub fn basic() -> Result<Environment, std::io::Error> {
|
|
||||||
let path = std::env::current_dir()?;
|
|
||||||
|
|
||||||
Ok(Environment { path })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> &Path {
|
|
||||||
self.path.as_path()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,11 +38,14 @@ crate use crate::commands::command::{
|
||||||
};
|
};
|
||||||
crate use crate::context::{Context, SpanSource};
|
crate use crate::context::{Context, SpanSource};
|
||||||
crate use crate::env::host::handle_unexpected;
|
crate use crate::env::host::handle_unexpected;
|
||||||
crate use crate::env::{Environment, Host};
|
crate use crate::env::Host;
|
||||||
crate use crate::errors::ShellError;
|
crate use crate::errors::ShellError;
|
||||||
crate use crate::object::meta::{Tag, Tagged, TaggedItem};
|
crate use crate::object::meta::{Tag, Tagged, TaggedItem};
|
||||||
crate use crate::object::types::ExtractType;
|
crate use crate::object::types::ExtractType;
|
||||||
crate use crate::object::{Primitive, Value};
|
crate use crate::object::{Primitive, Value};
|
||||||
|
crate use crate::shell::filesystem_shell::FilesystemShell;
|
||||||
|
crate use crate::shell::shell_manager::ShellManager;
|
||||||
|
crate use crate::shell::value_shell::ValueShell;
|
||||||
crate use crate::stream::{InputStream, OutputStream};
|
crate use crate::stream::{InputStream, OutputStream};
|
||||||
crate use crate::Span;
|
crate use crate::Span;
|
||||||
crate use crate::Text;
|
crate use crate::Text;
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
crate mod completer;
|
crate mod completer;
|
||||||
|
crate mod filesystem_shell;
|
||||||
crate mod helper;
|
crate mod helper;
|
||||||
|
crate mod shell;
|
||||||
|
crate mod shell_manager;
|
||||||
|
crate mod value_shell;
|
||||||
|
|
||||||
crate use helper::Helper;
|
crate use helper::Helper;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use rustyline::completion::Completer;
|
use rustyline::completion::Completer;
|
||||||
use rustyline::completion::{self, FilenameCompleter};
|
use rustyline::completion::{self, FilenameCompleter};
|
||||||
|
@ -7,7 +6,7 @@ use rustyline::line_buffer::LineBuffer;
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
crate struct NuCompleter {
|
crate struct NuCompleter {
|
||||||
pub file_completer: FilenameCompleter,
|
pub file_completer: FilenameCompleter,
|
||||||
pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
//pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completer for NuCompleter {
|
impl Completer for NuCompleter {
|
||||||
|
@ -19,7 +18,7 @@ impl Completer for NuCompleter {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
context: &rustyline::Context,
|
context: &rustyline::Context,
|
||||||
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
|
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
|
||||||
let commands: Vec<String> = self.commands.keys().cloned().collect();
|
//let commands: Vec<String> = self.commands.keys().cloned().collect();
|
||||||
|
|
||||||
let mut completions = self.file_completer.complete(line, pos, context)?.1;
|
let mut completions = self.file_completer.complete(line, pos, context)?.1;
|
||||||
|
|
||||||
|
@ -50,6 +49,7 @@ impl Completer for NuCompleter {
|
||||||
replace_pos -= 1;
|
replace_pos -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
for command in commands.iter() {
|
for command in commands.iter() {
|
||||||
let mut pos = replace_pos;
|
let mut pos = replace_pos;
|
||||||
let mut matched = true;
|
let mut matched = true;
|
||||||
|
@ -73,6 +73,7 @@ impl Completer for NuCompleter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Ok((replace_pos, completions))
|
Ok((replace_pos, completions))
|
||||||
}
|
}
|
||||||
|
|
206
src/shell/filesystem_shell.rs
Normal file
206
src/shell/filesystem_shell.rs
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
use crate::commands::command::CallInfo;
|
||||||
|
use crate::object::dir_entry_dict;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::shell::completer::NuCompleter;
|
||||||
|
use crate::shell::shell::Shell;
|
||||||
|
use rustyline::completion::{self, Completer, FilenameCompleter};
|
||||||
|
use rustyline::error::ReadlineError;
|
||||||
|
use rustyline::hint::{Hinter, HistoryHinter};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
pub struct FilesystemShell {
|
||||||
|
crate path: String,
|
||||||
|
completer: NuCompleter,
|
||||||
|
hinter: HistoryHinter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FilesystemShell {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
FilesystemShell {
|
||||||
|
path: self.path.clone(),
|
||||||
|
completer: NuCompleter {
|
||||||
|
file_completer: FilenameCompleter::new(),
|
||||||
|
},
|
||||||
|
hinter: HistoryHinter {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilesystemShell {
|
||||||
|
pub fn basic() -> Result<FilesystemShell, std::io::Error> {
|
||||||
|
let path = std::env::current_dir()?;
|
||||||
|
|
||||||
|
Ok(FilesystemShell {
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
completer: NuCompleter {
|
||||||
|
file_completer: FilenameCompleter::new(),
|
||||||
|
},
|
||||||
|
hinter: HistoryHinter {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_location(path: String) -> Result<FilesystemShell, std::io::Error> {
|
||||||
|
Ok(FilesystemShell {
|
||||||
|
path,
|
||||||
|
completer: NuCompleter {
|
||||||
|
file_completer: FilenameCompleter::new(),
|
||||||
|
},
|
||||||
|
hinter: HistoryHinter {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shell for FilesystemShell {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"filesystem".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ls(&self, call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
let cwd = self.path.clone();
|
||||||
|
let mut full_path = PathBuf::from(&self.path);
|
||||||
|
match &call_info.args.nth(0) {
|
||||||
|
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let entries = glob::glob(&full_path.to_string_lossy());
|
||||||
|
|
||||||
|
if entries.is_err() {
|
||||||
|
return Err(ShellError::string("Invalid pattern."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut shell_entries = VecDeque::new();
|
||||||
|
let entries: Vec<_> = entries.unwrap().collect();
|
||||||
|
|
||||||
|
// If this is a single entry, try to display the contents of the entry if it's a directory
|
||||||
|
if entries.len() == 1 {
|
||||||
|
if let Ok(entry) = &entries[0] {
|
||||||
|
if entry.is_dir() {
|
||||||
|
let entries = std::fs::read_dir(&full_path);
|
||||||
|
|
||||||
|
let entries = match entries {
|
||||||
|
Err(e) => {
|
||||||
|
if let Some(s) = call_info.args.nth(0) {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
e.to_string(),
|
||||||
|
e.to_string(),
|
||||||
|
s.span(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
e.to_string(),
|
||||||
|
e.to_string(),
|
||||||
|
call_info.name_span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(o) => o,
|
||||||
|
};
|
||||||
|
for entry in entries {
|
||||||
|
let entry = entry?;
|
||||||
|
let filepath = entry.path();
|
||||||
|
let filename = filepath.strip_prefix(&cwd).unwrap();
|
||||||
|
let value = dir_entry_dict(
|
||||||
|
filename,
|
||||||
|
&entry.metadata()?,
|
||||||
|
Tag::unknown_origin(call_info.name_span),
|
||||||
|
)?;
|
||||||
|
shell_entries.push_back(ReturnSuccess::value(value))
|
||||||
|
}
|
||||||
|
return Ok(shell_entries.to_output_stream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enumerate the entries from the glob and add each
|
||||||
|
for entry in entries {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
let filename = entry.strip_prefix(&cwd).unwrap();
|
||||||
|
let metadata = std::fs::metadata(&entry)?;
|
||||||
|
let value = dir_entry_dict(
|
||||||
|
filename,
|
||||||
|
&metadata,
|
||||||
|
Tag::unknown_origin(call_info.name_span),
|
||||||
|
)?;
|
||||||
|
shell_entries.push_back(ReturnSuccess::value(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(shell_entries.to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cd(&self, call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
let path = match call_info.args.nth(0) {
|
||||||
|
None => match dirs::home_dir() {
|
||||||
|
Some(o) => o,
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"Can not change to home directory",
|
||||||
|
"can not go to home",
|
||||||
|
call_info.name_span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(v) => {
|
||||||
|
let target = v.as_string()?;
|
||||||
|
let path = PathBuf::from(self.path());
|
||||||
|
match dunce::canonicalize(path.join(target).as_path()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"Can not change to directory",
|
||||||
|
"directory not found",
|
||||||
|
v.span().clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stream = VecDeque::new();
|
||||||
|
match std::env::set_current_dir(&path) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
if call_info.args.len() > 0 {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"Can not change to directory",
|
||||||
|
"directory not found",
|
||||||
|
call_info.args.nth(0).unwrap().span().clone(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return Err(ShellError::string("Can not change to directory"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.push_back(ReturnSuccess::change_cwd(
|
||||||
|
path.to_string_lossy().to_string(),
|
||||||
|
));
|
||||||
|
Ok(stream.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_path(&mut self, path: String) {
|
||||||
|
let _ = std::env::set_current_dir(&path);
|
||||||
|
self.path = path.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completer for FilesystemShell {
|
||||||
|
type Candidate = completion::Pair;
|
||||||
|
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
line: &str,
|
||||||
|
pos: usize,
|
||||||
|
ctx: &rustyline::Context<'_>,
|
||||||
|
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
||||||
|
self.completer.complete(line, pos, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hinter for FilesystemShell {
|
||||||
|
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||||
|
self.hinter.hint(line, pos, ctx)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,30 +2,22 @@ use crate::parser::nom_input;
|
||||||
use crate::parser::parse::token_tree::TokenNode;
|
use crate::parser::parse::token_tree::TokenNode;
|
||||||
use crate::parser::parse::tokens::RawToken;
|
use crate::parser::parse::tokens::RawToken;
|
||||||
use crate::parser::{Pipeline, PipelineElement};
|
use crate::parser::{Pipeline, PipelineElement};
|
||||||
use crate::prelude::*;
|
use crate::shell::shell_manager::ShellManager;
|
||||||
use crate::shell::completer::NuCompleter;
|
|
||||||
use crate::Tagged;
|
use crate::Tagged;
|
||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use rustyline::completion::{self, Completer, FilenameCompleter};
|
use rustyline::completion::{self, Completer};
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::highlight::Highlighter;
|
use rustyline::highlight::Highlighter;
|
||||||
use rustyline::hint::{Hinter, HistoryHinter};
|
use rustyline::hint::Hinter;
|
||||||
use std::borrow::Cow::{self, Owned};
|
use std::borrow::Cow::{self, Owned};
|
||||||
|
|
||||||
crate struct Helper {
|
crate struct Helper {
|
||||||
completer: NuCompleter,
|
helper: ShellManager,
|
||||||
hinter: HistoryHinter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Helper {
|
impl Helper {
|
||||||
crate fn new(commands: indexmap::IndexMap<String, Arc<dyn Command>>) -> Helper {
|
crate fn new(helper: ShellManager) -> Helper {
|
||||||
Helper {
|
Helper { helper }
|
||||||
completer: NuCompleter {
|
|
||||||
file_completer: FilenameCompleter::new(),
|
|
||||||
commands,
|
|
||||||
},
|
|
||||||
hinter: HistoryHinter {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,13 +30,13 @@ impl Completer for Helper {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
ctx: &rustyline::Context<'_>,
|
ctx: &rustyline::Context<'_>,
|
||||||
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
||||||
self.completer.complete(line, pos, ctx)
|
self.helper.complete(line, pos, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hinter for Helper {
|
impl Hinter for Helper {
|
||||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||||
self.hinter.hint(line, pos, ctx)
|
self.helper.hint(line, pos, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/shell/shell.rs
Normal file
16
src/shell/shell.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::commands::command::CallInfo;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::stream::{InputStream, OutputStream};
|
||||||
|
use rustyline::{completion::Completer, hint::Hinter};
|
||||||
|
|
||||||
|
pub trait Shell
|
||||||
|
where
|
||||||
|
Self: Completer<Candidate = rustyline::completion::Pair>,
|
||||||
|
Self: Hinter,
|
||||||
|
{
|
||||||
|
fn name(&self) -> String;
|
||||||
|
fn ls(&self, call_info: CallInfo, input: InputStream) -> Result<OutputStream, ShellError>;
|
||||||
|
fn cd(&self, call_info: CallInfo, input: InputStream) -> Result<OutputStream, ShellError>;
|
||||||
|
fn path(&self) -> String;
|
||||||
|
fn set_path(&mut self, path: String);
|
||||||
|
}
|
100
src/shell/shell_manager.rs
Normal file
100
src/shell/shell_manager.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use crate::commands::command::CallInfo;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::shell::filesystem_shell::FilesystemShell;
|
||||||
|
use crate::shell::shell::Shell;
|
||||||
|
use crate::stream::{InputStream, OutputStream};
|
||||||
|
use rustyline::completion::{self, Completer};
|
||||||
|
use rustyline::error::ReadlineError;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ShellManager {
|
||||||
|
crate shells: Arc<Mutex<Vec<Box<dyn Shell>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShellManager {
|
||||||
|
pub fn basic() -> Result<ShellManager, Box<dyn Error>> {
|
||||||
|
Ok(ShellManager {
|
||||||
|
shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic()?)])),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, shell: Box<dyn Shell>) {
|
||||||
|
self.shells.lock().unwrap().push(shell);
|
||||||
|
self.set_path(self.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
self.shells.lock().unwrap().pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.shells.lock().unwrap().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> String {
|
||||||
|
self.shells.lock().unwrap().last().unwrap().path()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_path(&mut self, path: String) {
|
||||||
|
self.shells
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.set_path(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(
|
||||||
|
&self,
|
||||||
|
line: &str,
|
||||||
|
pos: usize,
|
||||||
|
ctx: &rustyline::Context<'_>,
|
||||||
|
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
||||||
|
self.shells
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.complete(line, pos, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||||
|
self.shells
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.hint(line, pos, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) {
|
||||||
|
{
|
||||||
|
let mut x = self.shells.lock().unwrap();
|
||||||
|
let shell = x.pop().unwrap();
|
||||||
|
x.insert(0, shell);
|
||||||
|
}
|
||||||
|
self.set_path(self.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev(&mut self) {
|
||||||
|
{
|
||||||
|
let mut x = self.shells.lock().unwrap();
|
||||||
|
let shell = x.remove(0);
|
||||||
|
x.push(shell);
|
||||||
|
}
|
||||||
|
self.set_path(self.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ls(&self, call_info: CallInfo, input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
let env = self.shells.lock().unwrap();
|
||||||
|
|
||||||
|
env.last().unwrap().ls(call_info, input)
|
||||||
|
}
|
||||||
|
pub fn cd(&self, call_info: CallInfo, input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
let env = self.shells.lock().unwrap();
|
||||||
|
|
||||||
|
env.last().unwrap().cd(call_info, input)
|
||||||
|
}
|
||||||
|
}
|
170
src/shell/value_shell.rs
Normal file
170
src/shell/value_shell.rs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
use crate::commands::command::CallInfo;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::shell::shell::Shell;
|
||||||
|
use rustyline::completion::{self, Completer};
|
||||||
|
use rustyline::error::ReadlineError;
|
||||||
|
use rustyline::hint::Hinter;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ValueShell {
|
||||||
|
crate path: String,
|
||||||
|
crate value: Tagged<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueShell {
|
||||||
|
pub fn new(value: Tagged<Value>) -> ValueShell {
|
||||||
|
ValueShell {
|
||||||
|
path: "/".to_string(),
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn members(&self) -> VecDeque<Tagged<Value>> {
|
||||||
|
let mut shell_entries = VecDeque::new();
|
||||||
|
let full_path = PathBuf::from(&self.path);
|
||||||
|
let mut viewed = self.value.clone();
|
||||||
|
let sep_string = std::path::MAIN_SEPARATOR.to_string();
|
||||||
|
let sep = OsStr::new(&sep_string);
|
||||||
|
for p in full_path.iter() {
|
||||||
|
match p {
|
||||||
|
x if x == sep => {}
|
||||||
|
step => match viewed.get_data_by_key(step.to_str().unwrap()) {
|
||||||
|
Some(v) => {
|
||||||
|
viewed = v.clone();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match viewed {
|
||||||
|
Tagged {
|
||||||
|
item: Value::List(l),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for item in l {
|
||||||
|
shell_entries.push_back(item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
shell_entries.push_back(x.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shell for ValueShell {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"value".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ls(&self, _call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
Ok(self
|
||||||
|
.members()
|
||||||
|
.map(|x| ReturnSuccess::value(x))
|
||||||
|
.to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cd(&self, call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
|
||||||
|
let path = match call_info.args.nth(0) {
|
||||||
|
None => "/".to_string(),
|
||||||
|
Some(v) => {
|
||||||
|
let target = v.as_string()?;
|
||||||
|
|
||||||
|
let mut cwd = PathBuf::from(&self.path);
|
||||||
|
match target {
|
||||||
|
x if x == ".." => {
|
||||||
|
cwd.pop();
|
||||||
|
}
|
||||||
|
_ => match target.chars().nth(0) {
|
||||||
|
Some(x) if x == '/' => cwd = PathBuf::from(target),
|
||||||
|
_ => {
|
||||||
|
cwd.push(target);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cwd.to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stream = VecDeque::new();
|
||||||
|
stream.push_back(ReturnSuccess::change_cwd(path));
|
||||||
|
Ok(stream.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_path(&mut self, path: String) {
|
||||||
|
let _ = std::env::set_current_dir(&path);
|
||||||
|
self.path = path.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completer for ValueShell {
|
||||||
|
type Candidate = completion::Pair;
|
||||||
|
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
line: &str,
|
||||||
|
pos: usize,
|
||||||
|
_ctx: &rustyline::Context<'_>,
|
||||||
|
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
||||||
|
let mut completions = vec![];
|
||||||
|
|
||||||
|
let mut possible_completion = vec![];
|
||||||
|
let members = self.members();
|
||||||
|
for member in members {
|
||||||
|
match member {
|
||||||
|
Tagged { item, .. } => {
|
||||||
|
for desc in item.data_descriptors() {
|
||||||
|
possible_completion.push(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let line_chars: Vec<_> = line.chars().collect();
|
||||||
|
let mut replace_pos = pos;
|
||||||
|
while replace_pos > 0 {
|
||||||
|
if line_chars[replace_pos - 1] == ' ' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
replace_pos -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for command in possible_completion.iter() {
|
||||||
|
let mut pos = replace_pos;
|
||||||
|
let mut matched = true;
|
||||||
|
if pos < line_chars.len() {
|
||||||
|
for chr in command.chars() {
|
||||||
|
if line_chars[pos] != chr {
|
||||||
|
matched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
if pos == line_chars.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
completions.push(completion::Pair {
|
||||||
|
display: command.to_string(),
|
||||||
|
replacement: command.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((replace_pos, completions))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hinter for ValueShell {
|
||||||
|
fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue