mirror of
https://github.com/nushell/nushell.git
synced 2024-12-22 15:13:01 +01:00
parent
bb50f1eb14
commit
c231dd32cd
40
Cargo.lock
generated
40
Cargo.lock
generated
@ -443,20 +443,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossterm_cursor 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm_input 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm_screen 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm_style 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm_terminal 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.9 (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.1 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_cursor"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -466,10 +466,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_input"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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_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)",
|
||||
@ -478,7 +478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_screen"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -488,7 +488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_style"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -498,10 +498,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_terminal"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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_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)",
|
||||
@ -1804,7 +1804,7 @@ dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
@ -3623,12 +3623,12 @@ dependencies = [
|
||||
"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.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_cursor 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10235efee04a9d6cb3e98a46714da3b30bf4ed6210c02ab3bab33cdf10f74e63"
|
||||
"checksum crossterm_input 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a49e74609fe693d994a41b729054dbfb41d2c5fa14d8457113bdfeab28973315"
|
||||
"checksum crossterm_screen 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "957112221da964bd743451559a62def9b58392747a4676ae8cb2a0fd181d8337"
|
||||
"checksum crossterm_style 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5462cb56aa9572c5e3c1911213da2f9eb23f636253e932e73e7e2e97eef7ddda"
|
||||
"checksum crossterm_terminal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0a100ca73011f81ddab21c7ffc0b57ac0a3e459fb3874520e41d522321c102"
|
||||
"checksum crossterm 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9abce7d7c50e9823ea0c0dbeb8f16d7e247af06d75b4c6244ea0a0998b3a6f35"
|
||||
"checksum crossterm_cursor 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fb4bfd085f17d83e6cd2943f0150d3b4331e465de8dba1750d1966192faf63dc"
|
||||
"checksum crossterm_input 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c6dd255ca05a596bae31ec392fdb67a829509bb767213f00f37c6b62814db663"
|
||||
"checksum crossterm_screen 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf294484fc34c22d514c41afc0b97ce74e10ea54d6eb5fe4806d1e1ac0f7b76"
|
||||
"checksum crossterm_style 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b950f8262e29a446a8a976e0290b67a9067ddc9620f9fb37961d2377f0d8c09"
|
||||
"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_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"
|
||||
|
10
Cargo.toml
10
Cargo.toml
@ -33,13 +33,13 @@ futures-sink-preview = "=0.3.0-alpha.17"
|
||||
futures_codec = "0.2.5"
|
||||
term = "0.5.2"
|
||||
bytes = "0.4.12"
|
||||
log = "0.4.7"
|
||||
log = "0.4.8"
|
||||
pretty_env_logger = "0.3.0"
|
||||
serde = "1.0.97"
|
||||
serde = "1.0.98"
|
||||
serde_json = "1.0.40"
|
||||
serde-hjson = "0.9.0"
|
||||
serde_yaml = "0.8"
|
||||
serde_derive = "1.0.97"
|
||||
serde_derive = "1.0.98"
|
||||
serde_bytes = "0.11.1"
|
||||
getset = "0.0.7"
|
||||
logos = "0.10.0-rc2"
|
||||
@ -73,9 +73,9 @@ regex = "1.2.0"
|
||||
pretty-hex = "0.1.0"
|
||||
neso = "0.5.0"
|
||||
rawkey = "0.1.2"
|
||||
crossterm = "0.10.1"
|
||||
crossterm = "0.10.2"
|
||||
tempfile = "3.1.0"
|
||||
image = "0.22.0"
|
||||
image = "0.22.1"
|
||||
semver = "0.9.0"
|
||||
uuid = {version = "0.7.4", features = [ "v4", "serde" ]}
|
||||
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
|
||||
-------------+----------------------------+---------+---------+------+---------
|
||||
[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
|
||||
0.1.2
|
||||
0.1.3
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@ -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 |
|
||||
| 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') |
|
||||
| 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)
|
||||
| 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-yaml", Box::new(from_yaml::from_yaml)),
|
||||
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("pick", Box::new(pick::pick)),
|
||||
command("shells", Box::new(shells::shells)),
|
||||
command("split-column", Box::new(split_column::split_column)),
|
||||
command("split-row", Box::new(split_row::split_row)),
|
||||
command("lines", Box::new(lines::lines)),
|
||||
@ -182,29 +185,30 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
Arc::new(Date),
|
||||
Arc::new(Where),
|
||||
Arc::new(Config),
|
||||
Arc::new(Exit),
|
||||
Arc::new(SkipWhile),
|
||||
]);
|
||||
|
||||
context.add_sinks(vec![
|
||||
sink("autoview", Box::new(autoview::autoview)),
|
||||
sink("clip", Box::new(clip::clip)),
|
||||
sink("save", Box::new(save::save)),
|
||||
sink("table", Box::new(table::table)),
|
||||
sink("vtable", Box::new(vtable::vtable)),
|
||||
Arc::new(Save),
|
||||
]);
|
||||
}
|
||||
let _ = load_plugins(&mut context);
|
||||
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let h = crate::shell::Helper::new(context.clone_commands());
|
||||
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
|
||||
//let h = crate::shell::Helper::new(context.clone_commands());
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = ansi_term::enable_ansi_support();
|
||||
}
|
||||
|
||||
rl.set_helper(Some(h));
|
||||
//rl.set_helper(Some(h));
|
||||
let _ = rl.load_history("history.txt");
|
||||
|
||||
let ctrl_c = Arc::new(AtomicBool::new(false));
|
||||
@ -220,10 +224,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cwd = {
|
||||
let env = context.env.lock().unwrap();
|
||||
env.path().display().to_string()
|
||||
};
|
||||
let cwd = context.shell_manager.path();
|
||||
|
||||
rl.set_helper(Some(crate::shell::Helper::new(
|
||||
context.shell_manager.clone(),
|
||||
)));
|
||||
|
||||
let readline = rl.readline(&format!(
|
||||
"{}{}> ",
|
||||
cwd,
|
||||
|
@ -10,6 +10,7 @@ crate mod command;
|
||||
crate mod config;
|
||||
crate mod cp;
|
||||
crate mod date;
|
||||
crate mod enter;
|
||||
crate mod exit;
|
||||
crate mod first;
|
||||
crate mod from_csv;
|
||||
@ -21,13 +22,16 @@ crate mod from_yaml;
|
||||
crate mod get;
|
||||
crate mod lines;
|
||||
crate mod ls;
|
||||
crate mod next;
|
||||
crate mod open;
|
||||
crate mod pick;
|
||||
crate mod plugin;
|
||||
crate mod prev;
|
||||
crate mod ps;
|
||||
crate mod reject;
|
||||
crate mod rm;
|
||||
crate mod save;
|
||||
crate mod shells;
|
||||
crate mod size;
|
||||
crate mod skip_while;
|
||||
crate mod sort_by;
|
||||
@ -48,7 +52,9 @@ crate use command::command;
|
||||
crate use config::Config;
|
||||
crate use cp::Copycp;
|
||||
crate use date::Date;
|
||||
crate use exit::Exit;
|
||||
crate use open::Open;
|
||||
crate use rm::Remove;
|
||||
crate use save::Save;
|
||||
crate use skip_while::SkipWhile;
|
||||
crate use where_::Where;
|
||||
|
@ -1,52 +1,6 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use std::env;
|
||||
|
||||
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let env = args.env.lock().unwrap();
|
||||
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())
|
||||
args.shell_manager.cd(args.call_info, args.input)
|
||||
}
|
||||
|
@ -145,12 +145,62 @@ impl InternalCommand {
|
||||
match item? {
|
||||
ReturnSuccess::Action(action) => match action {
|
||||
CommandAction::ChangePath(path) => {
|
||||
context.env.lock().unwrap().path = path;
|
||||
context.shell_manager.set_path(path);
|
||||
}
|
||||
CommandAction::AddSpanSource(uuid, span_source) => {
|
||||
context.add_span_source(uuid, span_source);
|
||||
}
|
||||
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) => {
|
||||
@ -298,7 +348,7 @@ impl ExternalCommand {
|
||||
|
||||
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 {
|
||||
StreamNext::Last => process,
|
||||
|
@ -6,7 +6,6 @@ use crate::parser::registry::{self, Args};
|
||||
use crate::prelude::*;
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
@ -20,7 +19,7 @@ pub struct CallInfo {
|
||||
#[get = "crate"]
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host + Send>>,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: CallInfo,
|
||||
pub input: InputStream,
|
||||
}
|
||||
@ -60,9 +59,13 @@ pub struct SinkCommandArgs {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum CommandAction {
|
||||
ChangePath(PathBuf),
|
||||
ChangePath(String),
|
||||
AddSpanSource(Uuid, SpanSource),
|
||||
Exit,
|
||||
EnterShell(String),
|
||||
PreviousShell,
|
||||
NextShell,
|
||||
LeaveShell,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -80,7 +83,7 @@ impl From<Tagged<Value>> for ReturnValue {
|
||||
}
|
||||
|
||||
impl ReturnSuccess {
|
||||
pub fn change_cwd(path: PathBuf) -> ReturnValue {
|
||||
pub fn change_cwd(path: String) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
|
||||
use crate::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct Copycp;
|
||||
|
||||
@ -32,8 +32,8 @@ impl Command for Copycp {
|
||||
}
|
||||
|
||||
pub fn cp(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut source = args.env.lock().unwrap().path().to_path_buf();
|
||||
let mut destination = args.env.lock().unwrap().path().to_path_buf();
|
||||
let mut source = PathBuf::from(args.shell_manager.path());
|
||||
let mut destination = PathBuf::from(args.shell_manager.path());
|
||||
|
||||
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::errors::ShellError;
|
||||
use crate::parser::registry::{CommandConfig, NamedType};
|
||||
use crate::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
|
||||
pub struct Exit;
|
||||
|
||||
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::object::dir_entry_dict;
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let env = args.env.lock().unwrap();
|
||||
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())
|
||||
args.shell_manager.ls(args.call_info, args.input)
|
||||
}
|
||||
|
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 cwd = args
|
||||
.env
|
||||
.lock()
|
||||
.unwrap()
|
||||
.path()
|
||||
.to_path_buf();
|
||||
.shell_manager.path();
|
||||
|
||||
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 indexmap::IndexMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Remove;
|
||||
|
||||
@ -33,7 +34,7 @@ impl Command for Remove {
|
||||
}
|
||||
|
||||
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
|
||||
.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::errors::ShellError;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::parser::registry::{CommandConfig, NamedType};
|
||||
use crate::prelude::*;
|
||||
use crate::SpanSource;
|
||||
use indexmap::IndexMap;
|
||||
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> {
|
||||
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 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>>,
|
||||
crate source_map: SourceMap,
|
||||
crate host: Arc<Mutex<dyn Host + Send>>,
|
||||
crate env: Arc<Mutex<Environment>>,
|
||||
crate shell_manager: ShellManager,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
@ -48,7 +48,7 @@ impl Context {
|
||||
sinks: indexmap::IndexMap::new(),
|
||||
source_map: SourceMap::new(),
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn clone_commands(&self) -> indexmap::IndexMap<String, Arc<dyn Command>> {
|
||||
self.commands.clone()
|
||||
}
|
||||
|
||||
crate fn has_command(&self, name: &str) -> bool {
|
||||
self.commands.contains_key(name)
|
||||
}
|
||||
@ -118,7 +114,7 @@ impl Context {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let command_args = CommandArgs {
|
||||
host: self.host.clone(),
|
||||
env: self.env.clone(),
|
||||
shell_manager: self.shell_manager.clone(),
|
||||
call_info: CallInfo {
|
||||
name_span,
|
||||
source_map,
|
||||
|
@ -1,5 +1,3 @@
|
||||
crate mod environment;
|
||||
crate mod host;
|
||||
|
||||
crate use self::environment::Environment;
|
||||
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::env::host::handle_unexpected;
|
||||
crate use crate::env::{Environment, Host};
|
||||
crate use crate::env::Host;
|
||||
crate use crate::errors::ShellError;
|
||||
crate use crate::object::meta::{Tag, Tagged, TaggedItem};
|
||||
crate use crate::object::types::ExtractType;
|
||||
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::Span;
|
||||
crate use crate::Text;
|
||||
|
@ -1,4 +1,8 @@
|
||||
crate mod completer;
|
||||
crate mod filesystem_shell;
|
||||
crate mod helper;
|
||||
crate mod shell;
|
||||
crate mod shell_manager;
|
||||
crate mod value_shell;
|
||||
|
||||
crate use helper::Helper;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::completion::{self, FilenameCompleter};
|
||||
@ -7,7 +6,7 @@ use rustyline::line_buffer::LineBuffer;
|
||||
#[derive(new)]
|
||||
crate struct NuCompleter {
|
||||
pub file_completer: FilenameCompleter,
|
||||
pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
||||
//pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
||||
}
|
||||
|
||||
impl Completer for NuCompleter {
|
||||
@ -19,7 +18,7 @@ impl Completer for NuCompleter {
|
||||
pos: usize,
|
||||
context: &rustyline::Context,
|
||||
) -> 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;
|
||||
|
||||
@ -50,6 +49,7 @@ impl Completer for NuCompleter {
|
||||
replace_pos -= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
for command in commands.iter() {
|
||||
let mut pos = replace_pos;
|
||||
let mut matched = true;
|
||||
@ -73,6 +73,7 @@ impl Completer for NuCompleter {
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
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::tokens::RawToken;
|
||||
use crate::parser::{Pipeline, PipelineElement};
|
||||
use crate::prelude::*;
|
||||
use crate::shell::completer::NuCompleter;
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
use crate::Tagged;
|
||||
use ansi_term::Color;
|
||||
use rustyline::completion::{self, Completer, FilenameCompleter};
|
||||
use rustyline::completion::{self, Completer};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::highlight::Highlighter;
|
||||
use rustyline::hint::{Hinter, HistoryHinter};
|
||||
use rustyline::hint::Hinter;
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
|
||||
crate struct Helper {
|
||||
completer: NuCompleter,
|
||||
hinter: HistoryHinter,
|
||||
helper: ShellManager,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
crate fn new(commands: indexmap::IndexMap<String, Arc<dyn Command>>) -> Helper {
|
||||
Helper {
|
||||
completer: NuCompleter {
|
||||
file_completer: FilenameCompleter::new(),
|
||||
commands,
|
||||
},
|
||||
hinter: HistoryHinter {},
|
||||
}
|
||||
crate fn new(helper: ShellManager) -> Helper {
|
||||
Helper { helper }
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,13 +30,13 @@ impl Completer for Helper {
|
||||
pos: usize,
|
||||
ctx: &rustyline::Context<'_>,
|
||||
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
|
||||
self.completer.complete(line, pos, ctx)
|
||||
self.helper.complete(line, pos, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hinter for Helper {
|
||||
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 New Issue
Block a user