2019-06-07 16:30:50 +00:00
|
|
|
use crate::commands::autoview;
|
2019-05-24 07:29:16 +00:00
|
|
|
use crate::commands::classified::{
|
2019-05-26 06:54:41 +00:00
|
|
|
ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand,
|
|
|
|
StreamNext,
|
2019-05-24 07:29:16 +00:00
|
|
|
};
|
2019-07-04 03:06:43 +00:00
|
|
|
use crate::commands::plugin::JsonRpc;
|
2019-08-09 07:54:21 +00:00
|
|
|
use crate::commands::plugin::{PluginCommand, PluginSink};
|
2019-08-15 05:02:02 +00:00
|
|
|
use crate::commands::whole_stream_command;
|
2019-05-23 04:30:43 +00:00
|
|
|
use crate::context::Context;
|
|
|
|
crate use crate::errors::ShellError;
|
2019-07-03 17:37:09 +00:00
|
|
|
use crate::git::current_branch;
|
|
|
|
use crate::object::Value;
|
2019-08-02 19:15:07 +00:00
|
|
|
use crate::parser::registry::Signature;
|
2019-08-15 22:18:18 +00:00
|
|
|
use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode};
|
2019-07-03 17:37:09 +00:00
|
|
|
use crate::prelude::*;
|
2019-05-23 04:30:43 +00:00
|
|
|
|
2019-06-22 03:43:37 +00:00
|
|
|
use log::{debug, trace};
|
2019-07-04 03:06:43 +00:00
|
|
|
use regex::Regex;
|
2019-05-23 04:30:43 +00:00
|
|
|
use rustyline::error::ReadlineError;
|
|
|
|
use rustyline::{self, ColorMode, Config, Editor};
|
2019-07-03 17:37:09 +00:00
|
|
|
use std::env;
|
2019-05-23 04:30:43 +00:00
|
|
|
use std::error::Error;
|
2019-07-03 17:37:09 +00:00
|
|
|
use std::io::{BufRead, BufReader, Write};
|
2019-05-24 07:29:16 +00:00
|
|
|
use std::iter::Iterator;
|
2019-06-07 00:31:22 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-05-23 04:30:43 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum MaybeOwned<'a, T> {
|
|
|
|
Owned(T),
|
|
|
|
Borrowed(&'a T),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> MaybeOwned<'a, T> {
|
2019-07-04 05:11:56 +00:00
|
|
|
pub fn borrow(&self) -> &T {
|
2019-05-23 04:30:43 +00:00
|
|
|
match self {
|
|
|
|
MaybeOwned::Owned(v) => v,
|
|
|
|
MaybeOwned::Borrowed(v) => v,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 03:06:43 +00:00
|
|
|
fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> {
|
|
|
|
let mut child = std::process::Command::new(path)
|
2019-07-03 17:37:09 +00:00
|
|
|
.stdin(std::process::Stdio::piped())
|
|
|
|
.stdout(std::process::Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.expect("Failed to spawn child process");
|
|
|
|
|
|
|
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
|
|
|
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
|
|
|
|
|
|
|
|
let mut reader = BufReader::new(stdout);
|
|
|
|
|
|
|
|
let request = JsonRpc::new("config", Vec::<Value>::new());
|
2019-08-17 03:53:39 +00:00
|
|
|
let request_raw = serde_json::to_string(&request)?;
|
2019-07-03 17:37:09 +00:00
|
|
|
stdin.write(format!("{}\n", request_raw).as_bytes())?;
|
2019-08-17 03:53:39 +00:00
|
|
|
let path = dunce::canonicalize(path)?;
|
2019-07-03 17:37:09 +00:00
|
|
|
|
|
|
|
let mut input = String::new();
|
|
|
|
match reader.read_line(&mut input) {
|
|
|
|
Ok(_) => {
|
2019-08-02 19:15:07 +00:00
|
|
|
let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input);
|
2019-07-03 17:37:09 +00:00
|
|
|
match response {
|
|
|
|
Ok(jrpc) => match jrpc.params {
|
|
|
|
Ok(params) => {
|
2019-07-04 03:06:43 +00:00
|
|
|
let fname = path.to_string_lossy();
|
2019-07-03 17:37:09 +00:00
|
|
|
if params.is_filter {
|
|
|
|
let fname = fname.to_string();
|
2019-07-16 07:08:35 +00:00
|
|
|
let name = params.name.clone();
|
2019-08-15 05:02:02 +00:00
|
|
|
context.add_commands(vec![whole_stream_command(PluginCommand::new(
|
2019-07-16 07:08:35 +00:00
|
|
|
name, fname, params,
|
|
|
|
))]);
|
2019-07-03 17:37:09 +00:00
|
|
|
Ok(())
|
|
|
|
} else {
|
2019-08-09 07:54:21 +00:00
|
|
|
let fname = fname.to_string();
|
|
|
|
let name = params.name.clone();
|
2019-08-15 05:02:02 +00:00
|
|
|
context.add_commands(vec![whole_stream_command(PluginSink::new(
|
2019-08-09 07:54:21 +00:00
|
|
|
name, fname, params,
|
|
|
|
))]);
|
2019-07-03 17:37:09 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => Err(e),
|
|
|
|
},
|
|
|
|
Err(e) => Err(ShellError::string(format!("Error: {:?}", e))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => Err(ShellError::string(format!("Error: {:?}", e))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 03:06:43 +00:00
|
|
|
fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Result<(), ShellError> {
|
2019-08-17 03:53:39 +00:00
|
|
|
let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?;
|
|
|
|
let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.exe$")?;
|
2019-07-04 03:06:43 +00:00
|
|
|
|
|
|
|
match std::fs::read_dir(path) {
|
|
|
|
Ok(p) => {
|
|
|
|
for entry in p {
|
2019-08-17 03:53:39 +00:00
|
|
|
let entry = entry?;
|
2019-07-04 03:06:43 +00:00
|
|
|
let filename = entry.file_name();
|
|
|
|
let f_name = filename.to_string_lossy();
|
|
|
|
if re_bin.is_match(&f_name) || re_exe.is_match(&f_name) {
|
|
|
|
let mut load_path = path.clone();
|
|
|
|
load_path.push(f_name.to_string());
|
|
|
|
load_plugin(&load_path, context)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-07-03 17:37:09 +00:00
|
|
|
fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
|
|
|
|
match env::var_os("PATH") {
|
|
|
|
Some(paths) => {
|
|
|
|
for path in env::split_paths(&paths) {
|
2019-07-04 03:06:43 +00:00
|
|
|
let _ = load_plugins_in_dir(&path, context);
|
2019-07-03 17:37:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => println!("PATH is not defined in the environment."),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also use our debug output for now
|
|
|
|
let mut path = std::path::PathBuf::from(".");
|
|
|
|
path.push("target");
|
|
|
|
path.push("debug");
|
|
|
|
|
2019-07-04 03:06:43 +00:00
|
|
|
let _ = load_plugins_in_dir(&path, context);
|
2019-07-03 17:37:09 +00:00
|
|
|
|
2019-07-04 22:17:18 +00:00
|
|
|
// Also use our release output for now
|
|
|
|
let mut path = std::path::PathBuf::from(".");
|
|
|
|
path.push("target");
|
|
|
|
path.push("release");
|
|
|
|
|
|
|
|
let _ = load_plugins_in_dir(&path, context);
|
|
|
|
|
2019-07-03 17:37:09 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-03 03:48:58 +00:00
|
|
|
pub async fn cli() -> Result<(), Box<dyn Error>> {
|
2019-05-23 04:30:43 +00:00
|
|
|
let mut context = Context::basic()?;
|
|
|
|
|
|
|
|
{
|
|
|
|
use crate::commands::*;
|
|
|
|
|
|
|
|
context.add_commands(vec![
|
2019-08-19 05:16:39 +00:00
|
|
|
whole_stream_command(PS),
|
|
|
|
whole_stream_command(LS),
|
|
|
|
whole_stream_command(CD),
|
|
|
|
whole_stream_command(Size),
|
|
|
|
whole_stream_command(Nth),
|
|
|
|
whole_stream_command(Next),
|
|
|
|
whole_stream_command(Previous),
|
|
|
|
whole_stream_command(Debug),
|
|
|
|
whole_stream_command(Lines),
|
|
|
|
whole_stream_command(Shells),
|
|
|
|
whole_stream_command(SplitColumn),
|
|
|
|
whole_stream_command(SplitRow),
|
|
|
|
whole_stream_command(Lines),
|
|
|
|
whole_stream_command(Reject),
|
|
|
|
whole_stream_command(Trim),
|
|
|
|
whole_stream_command(ToArray),
|
|
|
|
whole_stream_command(ToCSV),
|
|
|
|
whole_stream_command(ToJSON),
|
|
|
|
whole_stream_command(ToTOML),
|
|
|
|
whole_stream_command(ToYAML),
|
|
|
|
whole_stream_command(SortBy),
|
|
|
|
whole_stream_command(Tags),
|
|
|
|
whole_stream_command(First),
|
|
|
|
whole_stream_command(FromArray),
|
|
|
|
whole_stream_command(FromCSV),
|
|
|
|
whole_stream_command(FromINI),
|
|
|
|
whole_stream_command(FromJSON),
|
|
|
|
whole_stream_command(FromTOML),
|
|
|
|
whole_stream_command(FromXML),
|
|
|
|
whole_stream_command(FromYAML),
|
|
|
|
whole_stream_command(Pick),
|
2019-08-15 05:02:02 +00:00
|
|
|
whole_stream_command(Get),
|
|
|
|
per_item_command(Remove),
|
|
|
|
per_item_command(Open),
|
2019-08-14 17:02:39 +00:00
|
|
|
per_item_command(Where),
|
2019-08-15 05:02:02 +00:00
|
|
|
whole_stream_command(Config),
|
|
|
|
whole_stream_command(SkipWhile),
|
2019-08-14 17:02:39 +00:00
|
|
|
per_item_command(Enter),
|
2019-08-15 05:02:02 +00:00
|
|
|
whole_stream_command(Exit),
|
|
|
|
whole_stream_command(Clip),
|
|
|
|
whole_stream_command(Autoview),
|
|
|
|
per_item_command(Cpy),
|
|
|
|
whole_stream_command(Date),
|
|
|
|
per_item_command(Mkdir),
|
|
|
|
per_item_command(Move),
|
|
|
|
whole_stream_command(Save),
|
|
|
|
whole_stream_command(Table),
|
|
|
|
whole_stream_command(VTable),
|
2019-08-19 01:30:29 +00:00
|
|
|
whole_stream_command(Version),
|
2019-08-15 05:02:02 +00:00
|
|
|
whole_stream_command(Which),
|
2019-06-07 07:50:26 +00:00
|
|
|
]);
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
2019-07-04 03:06:43 +00:00
|
|
|
let _ = load_plugins(&mut context);
|
2019-05-23 04:30:43 +00:00
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
2019-08-07 17:49:11 +00:00
|
|
|
let mut rl: Editor<_> = Editor::with_config(config);
|
2019-05-26 06:54:41 +00:00
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let _ = ansi_term::enable_ansi_support();
|
|
|
|
}
|
|
|
|
|
2019-06-03 00:03:40 +00:00
|
|
|
let _ = rl.load_history("history.txt");
|
2019-05-26 06:54:41 +00:00
|
|
|
|
2019-06-07 00:31:22 +00:00
|
|
|
let ctrl_c = Arc::new(AtomicBool::new(false));
|
|
|
|
let cc = ctrl_c.clone();
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
cc.store(true, Ordering::SeqCst);
|
|
|
|
})
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
2019-06-15 18:36:17 +00:00
|
|
|
let mut ctrlcbreak = false;
|
2019-05-23 04:30:43 +00:00
|
|
|
loop {
|
2019-06-07 00:31:22 +00:00
|
|
|
if ctrl_c.load(Ordering::SeqCst) {
|
|
|
|
ctrl_c.store(false, Ordering::SeqCst);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-08-07 17:49:11 +00:00
|
|
|
let cwd = context.shell_manager.path();
|
|
|
|
|
|
|
|
rl.set_helper(Some(crate::shell::Helper::new(
|
|
|
|
context.shell_manager.clone(),
|
|
|
|
)));
|
|
|
|
|
2019-07-16 19:10:25 +00:00
|
|
|
let readline = rl.readline(&format!(
|
|
|
|
"{}{}> ",
|
|
|
|
cwd,
|
|
|
|
match current_branch() {
|
|
|
|
Some(s) => format!("({})", s),
|
|
|
|
None => "".to_string(),
|
|
|
|
}
|
|
|
|
));
|
2019-05-23 04:30:43 +00:00
|
|
|
|
2019-05-23 07:23:06 +00:00
|
|
|
match process_line(readline, &mut context).await {
|
2019-05-23 04:30:43 +00:00
|
|
|
LineResult::Success(line) => {
|
|
|
|
rl.add_history_entry(line.clone());
|
|
|
|
}
|
|
|
|
|
2019-06-15 18:36:17 +00:00
|
|
|
LineResult::CtrlC => {
|
|
|
|
if ctrlcbreak {
|
|
|
|
std::process::exit(0);
|
|
|
|
} else {
|
|
|
|
context
|
|
|
|
.host
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.stdout("CTRL-C pressed (again to quit)");
|
|
|
|
ctrlcbreak = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 00:39:09 +00:00
|
|
|
LineResult::Error(mut line, err) => {
|
|
|
|
rl.add_history_entry(line.clone());
|
2019-06-24 00:55:31 +00:00
|
|
|
let diag = err.to_diagnostic();
|
|
|
|
let host = context.host.lock().unwrap();
|
|
|
|
let writer = host.err_termcolor();
|
|
|
|
line.push_str(" ");
|
|
|
|
let files = crate::parser::Files::new(line);
|
2019-08-21 06:39:57 +00:00
|
|
|
let _ = std::panic::catch_unwind(move || {
|
|
|
|
let _ = language_reporting::emit(
|
|
|
|
&mut writer.lock(),
|
|
|
|
&files,
|
|
|
|
&diag,
|
|
|
|
&language_reporting::DefaultConfig,
|
|
|
|
);
|
|
|
|
});
|
2019-06-18 00:39:09 +00:00
|
|
|
}
|
2019-05-23 04:30:43 +00:00
|
|
|
|
|
|
|
LineResult::Break => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
LineResult::FatalError(_, err) => {
|
2019-05-23 04:30:43 +00:00
|
|
|
context
|
|
|
|
.host
|
2019-05-23 07:23:06 +00:00
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2019-05-23 04:30:43 +00:00
|
|
|
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
|
|
|
|
}
|
|
|
|
}
|
2019-06-15 18:36:17 +00:00
|
|
|
ctrlcbreak = false;
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
2019-08-17 03:53:39 +00:00
|
|
|
rl.save_history("history.txt")?;
|
2019-05-23 04:30:43 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
enum LineResult {
|
|
|
|
Success(String),
|
2019-06-07 22:35:07 +00:00
|
|
|
Error(String, ShellError),
|
2019-06-15 18:36:17 +00:00
|
|
|
CtrlC,
|
2019-05-23 04:30:43 +00:00
|
|
|
Break,
|
|
|
|
|
|
|
|
#[allow(unused)]
|
2019-06-24 00:55:31 +00:00
|
|
|
FatalError(String, ShellError),
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 04:34:43 +00:00
|
|
|
impl std::ops::Try for LineResult {
|
|
|
|
type Ok = Option<String>;
|
2019-06-24 00:55:31 +00:00
|
|
|
type Error = (String, ShellError);
|
2019-05-24 04:34:43 +00:00
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
fn into_result(self) -> Result<Option<String>, (String, ShellError)> {
|
2019-05-24 04:34:43 +00:00
|
|
|
match self {
|
|
|
|
LineResult::Success(s) => Ok(Some(s)),
|
2019-06-24 00:55:31 +00:00
|
|
|
LineResult::Error(string, err) => Err((string, err)),
|
2019-05-24 04:34:43 +00:00
|
|
|
LineResult::Break => Ok(None),
|
2019-06-15 18:36:17 +00:00
|
|
|
LineResult::CtrlC => Ok(None),
|
2019-06-24 00:55:31 +00:00
|
|
|
LineResult::FatalError(string, err) => Err((string, err)),
|
2019-05-24 04:34:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-24 00:55:31 +00:00
|
|
|
fn from_error(v: (String, ShellError)) -> Self {
|
|
|
|
LineResult::Error(v.0, v.1)
|
2019-05-24 04:34:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn from_ok(v: Option<String>) -> Self {
|
|
|
|
match v {
|
|
|
|
None => LineResult::Break,
|
|
|
|
Some(v) => LineResult::Success(v),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 07:23:06 +00:00
|
|
|
async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult {
|
2019-05-23 04:30:43 +00:00
|
|
|
match &readline {
|
|
|
|
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
|
|
|
|
|
|
|
|
Ok(line) => {
|
2019-06-02 16:28:40 +00:00
|
|
|
let result = match crate::parser::parse(&line) {
|
2019-05-23 04:30:43 +00:00
|
|
|
Err(err) => {
|
2019-06-09 17:52:56 +00:00
|
|
|
return LineResult::Error(line.clone(), err);
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(val) => val,
|
|
|
|
};
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
debug!("=== Parsed ===");
|
|
|
|
debug!("{:#?}", result);
|
2019-05-24 07:29:16 +00:00
|
|
|
|
2019-06-24 00:55:31 +00:00
|
|
|
let mut pipeline = classify_pipeline(&result, ctx, &Text::from(line))
|
|
|
|
.map_err(|err| (line.clone(), err))?;
|
2019-06-07 06:34:42 +00:00
|
|
|
|
|
|
|
match pipeline.commands.last() {
|
2019-06-07 07:54:52 +00:00
|
|
|
Some(ClassifiedCommand::External(_)) => {}
|
2019-08-02 19:15:07 +00:00
|
|
|
_ => pipeline
|
|
|
|
.commands
|
|
|
|
.push(ClassifiedCommand::Internal(InternalCommand {
|
2019-08-15 05:02:02 +00:00
|
|
|
command: whole_stream_command(autoview::Autoview),
|
2019-08-09 04:51:21 +00:00
|
|
|
name_span: Span::unknown(),
|
2019-08-02 19:15:07 +00:00
|
|
|
args: hir::Call::new(
|
|
|
|
Box::new(hir::Expression::synthetic_string("autoview")),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
),
|
|
|
|
})),
|
2019-06-07 06:34:42 +00:00
|
|
|
}
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
let mut input = ClassifiedInputStream::new();
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
let mut iter = pipeline.commands.into_iter().peekable();
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
loop {
|
|
|
|
let item: Option<ClassifiedCommand> = iter.next();
|
|
|
|
let next: Option<&ClassifiedCommand> = iter.peek();
|
|
|
|
|
|
|
|
input = match (item, next) {
|
|
|
|
(None, _) => break,
|
|
|
|
|
2019-06-04 21:42:31 +00:00
|
|
|
(Some(ClassifiedCommand::Expr(_)), _) => {
|
2019-06-15 18:36:17 +00:00
|
|
|
return LineResult::Error(
|
|
|
|
line.clone(),
|
|
|
|
ShellError::unimplemented("Expression-only commands"),
|
|
|
|
)
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(_, Some(ClassifiedCommand::Expr(_))) => {
|
2019-06-15 18:36:17 +00:00
|
|
|
return LineResult::Error(
|
|
|
|
line.clone(),
|
|
|
|
ShellError::unimplemented("Expression-only commands"),
|
|
|
|
)
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 07:29:16 +00:00
|
|
|
(
|
|
|
|
Some(ClassifiedCommand::Internal(left)),
|
2019-06-07 06:34:42 +00:00
|
|
|
Some(ClassifiedCommand::External(_)),
|
2019-07-23 22:22:11 +00:00
|
|
|
) => match left.run(ctx, input, Text::from(line)).await {
|
2019-06-07 06:34:42 +00:00
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-06-07 06:34:42 +00:00
|
|
|
},
|
|
|
|
|
2019-06-15 18:36:17 +00:00
|
|
|
(Some(ClassifiedCommand::Internal(left)), Some(_)) => {
|
2019-07-23 22:22:11 +00:00
|
|
|
match left.run(ctx, input, Text::from(line)).await {
|
2019-06-15 18:36:17 +00:00
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
|
|
|
}
|
|
|
|
}
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
(Some(ClassifiedCommand::Internal(left)), None) => {
|
2019-07-23 22:22:11 +00:00
|
|
|
match left.run(ctx, input, Text::from(line)).await {
|
2019-05-24 07:29:16 +00:00
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(
|
|
|
|
Some(ClassifiedCommand::External(left)),
|
|
|
|
Some(ClassifiedCommand::External(_)),
|
2019-05-24 18:48:33 +00:00
|
|
|
) => match left.run(ctx, input, StreamNext::External).await {
|
2019-05-24 07:29:16 +00:00
|
|
|
Ok(val) => val,
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
},
|
|
|
|
|
2019-06-15 18:36:17 +00:00
|
|
|
(Some(ClassifiedCommand::External(left)), Some(_)) => {
|
|
|
|
match left.run(ctx, input, StreamNext::Internal).await {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
|
|
|
}
|
|
|
|
}
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
(Some(ClassifiedCommand::External(left)), None) => {
|
2019-05-24 18:48:33 +00:00
|
|
|
match left.run(ctx, input, StreamNext::Last).await {
|
2019-05-24 07:29:16 +00:00
|
|
|
Ok(val) => val,
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
2019-06-09 17:52:56 +00:00
|
|
|
LineResult::Success(line.clone())
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
2019-06-15 18:36:17 +00:00
|
|
|
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
2019-05-23 04:30:43 +00:00
|
|
|
Err(ReadlineError::Eof) => {
|
|
|
|
println!("CTRL-D");
|
|
|
|
LineResult::Break
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
println!("Error: {:?}", err);
|
|
|
|
LineResult::Break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
fn classify_pipeline(
|
2019-06-22 03:43:37 +00:00
|
|
|
pipeline: &TokenNode,
|
2019-05-26 06:54:41 +00:00
|
|
|
context: &Context,
|
2019-06-22 20:46:16 +00:00
|
|
|
source: &Text,
|
2019-05-26 06:54:41 +00:00
|
|
|
) -> Result<ClassifiedPipeline, ShellError> {
|
2019-06-22 03:43:37 +00:00
|
|
|
let pipeline = pipeline.as_pipeline()?;
|
|
|
|
|
2019-06-23 17:35:43 +00:00
|
|
|
let Pipeline { parts, .. } = pipeline;
|
|
|
|
|
|
|
|
let commands: Result<Vec<_>, ShellError> = parts
|
2019-05-26 06:54:41 +00:00
|
|
|
.iter()
|
2019-06-22 20:46:16 +00:00
|
|
|
.map(|item| classify_command(&item, context, &source))
|
2019-05-26 06:54:41 +00:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(ClassifiedPipeline {
|
|
|
|
commands: commands?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-05-23 04:30:43 +00:00
|
|
|
fn classify_command(
|
2019-06-22 03:43:37 +00:00
|
|
|
command: &PipelineElement,
|
2019-05-23 04:30:43 +00:00
|
|
|
context: &Context,
|
2019-06-22 20:46:16 +00:00
|
|
|
source: &Text,
|
2019-05-23 04:30:43 +00:00
|
|
|
) -> Result<ClassifiedCommand, ShellError> {
|
2019-06-22 03:43:37 +00:00
|
|
|
let call = command.call();
|
2019-06-04 21:42:31 +00:00
|
|
|
|
2019-06-22 03:43:37 +00:00
|
|
|
match call {
|
2019-08-15 22:18:18 +00:00
|
|
|
// If the command starts with `^`, treat it as an external command no matter what
|
|
|
|
call if call.head().is_external() => {
|
|
|
|
let name_span = call.head().expect_external();
|
|
|
|
let name = name_span.slice(source);
|
|
|
|
|
|
|
|
Ok(external_command(call, source, name.tagged(name_span)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, if the command is a bare word, we'll need to triage it
|
2019-06-22 03:43:37 +00:00
|
|
|
call if call.head().is_bare() => {
|
|
|
|
let head = call.head();
|
|
|
|
let name = head.source(source);
|
|
|
|
|
|
|
|
match context.has_command(name) {
|
2019-08-15 22:18:18 +00:00
|
|
|
// if the command is in the registry, it's an internal command
|
2019-06-06 06:34:59 +00:00
|
|
|
true => {
|
2019-06-22 03:43:37 +00:00
|
|
|
let command = context.get_command(name);
|
2019-08-02 19:15:07 +00:00
|
|
|
let config = command.signature();
|
2019-06-06 06:34:59 +00:00
|
|
|
|
2019-07-12 19:22:08 +00:00
|
|
|
trace!(target: "nu::build_pipeline", "classifying {:?}", config);
|
2019-06-22 03:43:37 +00:00
|
|
|
|
2019-07-23 22:22:11 +00:00
|
|
|
let args: hir::Call = config.parse_args(call, context.registry(), source)?;
|
2019-06-06 06:34:59 +00:00
|
|
|
|
|
|
|
Ok(ClassifiedCommand::Internal(InternalCommand {
|
|
|
|
command,
|
2019-08-05 08:54:29 +00:00
|
|
|
name_span: head.span().clone(),
|
2019-06-06 06:34:59 +00:00
|
|
|
args,
|
|
|
|
}))
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
2019-08-15 22:18:18 +00:00
|
|
|
|
|
|
|
// otherwise, it's an external command
|
|
|
|
false => Ok(external_command(call, source, name.tagged(head.span()))),
|
2019-06-22 03:43:37 +00:00
|
|
|
}
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
2019-06-22 03:43:37 +00:00
|
|
|
|
2019-08-15 22:18:18 +00:00
|
|
|
// If the command is something else (like a number or a variable), that is currently unsupported.
|
|
|
|
// We might support `$somevar` as a curried command in the future.
|
|
|
|
call => Err(ShellError::invalid_command(call.head().span())),
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-15 22:18:18 +00:00
|
|
|
|
|
|
|
// Classify this command as an external command, which doesn't give special meaning
|
|
|
|
// to nu syntactic constructs, and passes all arguments to the external command as
|
|
|
|
// strings.
|
|
|
|
fn external_command(
|
|
|
|
call: &Tagged<CallNode>,
|
|
|
|
source: &Text,
|
|
|
|
name: Tagged<&str>,
|
|
|
|
) -> ClassifiedCommand {
|
|
|
|
let arg_list_strings: Vec<Tagged<String>> = match call.children() {
|
|
|
|
Some(args) => args
|
|
|
|
.iter()
|
|
|
|
.filter_map(|i| match i {
|
|
|
|
TokenNode::Whitespace(_) => None,
|
|
|
|
other => Some(Tagged::from_simple_spanned_item(
|
|
|
|
other.as_external_arg(source),
|
|
|
|
other.span(),
|
|
|
|
)),
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
None => vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
let (name, tag) = name.into_parts();
|
|
|
|
|
|
|
|
ClassifiedCommand::External(ExternalCommand {
|
|
|
|
name: name.to_string(),
|
|
|
|
name_span: tag.span,
|
|
|
|
args: arg_list_strings,
|
|
|
|
})
|
|
|
|
}
|