diff --git a/Cargo.lock b/Cargo.lock index bd7d5a4629..40b794e3ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1702,6 +1702,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1709,6 +1710,7 @@ dependencies = [ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "toml-query 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3202,6 +3204,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 041d5b11a5..953c89f7ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,8 @@ crossterm = "0.9.6" tempfile = "3.1.0" image = "0.21.2" semver = "0.9.0" +uuid = {version = "0.7.4", features = [ "v4", "serde" ]} +syntect = "3.2.0" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/assets/themes.bin b/assets/themes.bin new file mode 100644 index 0000000000..23ad913280 Binary files /dev/null and b/assets/themes.bin differ diff --git a/src/cli.rs b/src/cli.rs index 1d09ab6552..55b02a11a4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -495,6 +495,7 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand { command, name_span: Some(head.span().clone()), + source_map: context.source_map.clone(), args, })) } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index e9f6599f8b..b092159829 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,7 +1,9 @@ use crate::commands::command::SinkCommandArgs; +use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::format::GenericView; use crate::prelude::*; +use std::path::Path; pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { if args.input.len() > 0 { @@ -11,6 +13,8 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { } = args.input[0] { args.ctx.get_sink("binaryview").run(args)?; + } else if is_single_text_value(&args.input) { + view_text_value(&args.input[0], &args.call_info.source_map); } else if equal_shapes(&args.input) { args.ctx.get_sink("table").run(args)?; } else { @@ -44,3 +48,76 @@ fn equal_shapes(input: &Vec>) -> bool { true } + +fn is_single_text_value(input: &Vec>) -> bool { + if input.len() != 1 { + return false; + } + if let Spanned { + item: Value::Primitive(Primitive::String(_)), + .. + } = input[0] + { + true + } else { + false + } +} + +fn view_text_value(value: &Spanned, source_map: &SourceMap) { + match value { + Spanned { + item: Value::Primitive(Primitive::String(s)), + span, + } => { + let source = span.source.map(|x| source_map.get(&x)).flatten(); + + if let Some(source) = source { + match source { + SpanSource::File(file) => { + let path = Path::new(file); + match path.extension() { + Some(extension) => { + use syntect::easy::HighlightLines; + use syntect::highlighting::{Style, ThemeSet}; + use syntect::parsing::SyntaxSet; + use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings}; + + // Load these once at the start of your program + let ps = SyntaxSet::load_defaults_newlines(); + + if let Some(syntax) = + ps.find_syntax_by_extension(extension.to_str().unwrap()) + { + let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!( + "../../assets/themes.bin" + )); + let mut h = + HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); + + for line in LinesWithEndings::from(s) { + let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); + let escaped = + as_24_bit_terminal_escaped(&ranges[..], false); + print!("{}", escaped); + } + } else { + println!("{}", s); + } + } + _ => { + println!("{}", s); + } + } + } + _ => { + println!("{}", s); + } + } + } else { + println!("{}", s); + } + } + _ => {} + } +} diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 246c4c40d1..b68a0deb70 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -13,7 +13,7 @@ pub fn cd(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "Can not change to home directory", "can not go to home", - args.name_span, + args.call_info.name_span, )) } }, diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 7d3fd288f0..da4ce706cc 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,4 +1,5 @@ use crate::commands::command::Sink; +use crate::context::SourceMap; use crate::parser::{registry::Args, Span, Spanned, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; @@ -116,6 +117,7 @@ impl SinkCommand { crate struct InternalCommand { crate command: Arc, crate name_span: Option, + crate source_map: SourceMap, crate args: Args, } @@ -134,8 +136,13 @@ impl InternalCommand { let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); - let result = - context.run_command(self.command, self.name_span.clone(), self.args, objects)?; + let result = context.run_command( + self.command, + self.name_span.clone(), + self.source_map, + self.args, + objects, + )?; let mut result = result.values; @@ -146,6 +153,9 @@ impl InternalCommand { CommandAction::ChangePath(path) => { context.env.lock().unwrap().path = path; } + CommandAction::AddSpanSource(uuid, span_source) => { + context.add_span_source(uuid, span_source); + } CommandAction::Exit => std::process::exit(0), }, diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 96b6b7081e..32294bf776 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -16,7 +16,7 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { } let string = i.as_string().map_err(labelled( - args.name_span, + args.call_info.name_span, "Given non-string data", "expected strings from pipeline", ))?; diff --git a/src/commands/command.rs b/src/commands/command.rs index 6dd31e216c..e5057c6cd1 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,3 +1,5 @@ +use crate::context::SourceMap; +use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::Value; use crate::parser::{ @@ -8,54 +10,61 @@ use crate::prelude::*; use getset::Getters; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use uuid::Uuid; + +#[derive(Deserialize, Serialize, Debug)] +pub struct CallInfo { + pub args: Args, + pub source_map: SourceMap, + pub name_span: Option, +} #[derive(Getters)] #[get = "crate"] pub struct CommandArgs { pub host: Arc>, pub env: Arc>, - pub name_span: Option, - pub args: Args, + pub call_info: CallInfo, pub input: InputStream, } impl CommandArgs { pub fn nth(&self, pos: usize) -> Option<&Spanned> { - self.args.nth(pos) + self.call_info.args.nth(pos) } pub fn positional_iter(&self) -> impl Iterator> { - self.args.positional_iter() + self.call_info.args.positional_iter() } pub fn expect_nth(&self, pos: usize) -> Result<&Spanned, ShellError> { - self.args.expect_nth(pos) + self.call_info.args.expect_nth(pos) } pub fn len(&self) -> usize { - self.args.len() + self.call_info.args.len() } pub fn get(&self, name: &str) -> Option<&Spanned> { - self.args.get(name) + self.call_info.args.get(name) } #[allow(unused)] pub fn has(&self, name: &str) -> bool { - self.args.has(name) + self.call_info.args.has(name) } } pub struct SinkCommandArgs { pub ctx: Context, - pub name_span: Option, - pub args: Args, + pub call_info: CallInfo, pub input: Vec>, } #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), + AddSpanSource(Uuid, SpanSource), Exit, } @@ -82,6 +91,10 @@ impl ReturnSuccess { Ok(ReturnSuccess::Value(input.into())) } + pub fn action(input: CommandAction) -> ReturnValue { + Ok(ReturnSuccess::Action(input)) + } + pub fn spanned_value(input: Value, span: Span) -> ReturnValue { Ok(ReturnSuccess::Value(Spanned::from_item(input, span))) } diff --git a/src/commands/config.rs b/src/commands/config.rs index 954cc08efc..7da3c91c66 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -39,10 +39,10 @@ impl Command for Config { } pub fn config(args: CommandArgs) -> Result { - let mut result = crate::object::config::config(args.name_span)?; + let mut result = crate::object::config::config(args.call_info.name_span)?; - trace!("{:#?}", args.args.positional); - trace!("{:#?}", args.args.named); + trace!("{:#?}", args.call_info.args.positional); + trace!("{:#?}", args.call_info.args.named); if let Some(v) = args.get("get") { let key = v.as_string()?; @@ -95,7 +95,7 @@ pub fn config(args: CommandArgs) -> Result { } if args.len() == 0 { - return Ok(vec![Value::Object(result.into()).spanned(args.name_span)].into()); + return Ok(vec![Value::Object(result.into()).spanned(args.call_info.name_span)].into()); } Err(ShellError::string(format!("Unimplemented"))) diff --git a/src/commands/first.rs b/src/commands/first.rs index 5a466a8cd5..aec66b3a62 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -8,7 +8,7 @@ pub fn first(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "First requires an amount", "needs parameter", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index c6e0fa9eca..9c70bf1edf 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -6,8 +6,9 @@ pub fn from_csv_string_to_value( s: String, span: impl Into, ) -> Result, Box> { - - let mut reader = ReaderBuilder::new().has_headers(false).from_reader(s.as_bytes()); + let mut reader = ReaderBuilder::new() + .has_headers(false) + .from_reader(s.as_bytes()); let span = span.into(); let mut fields: VecDeque = VecDeque::new(); @@ -28,9 +29,12 @@ pub fn from_csv_string_to_value( let row_values = row_values?; let mut row = SpannedDictBuilder::new(span); - + for (idx, entry) in row_values.iter().enumerate() { - row.insert_spanned(fields.get(idx).unwrap(), Value::Primitive(Primitive::String(String::from(entry))).spanned(span)); + row.insert_spanned( + fields.get(idx).unwrap(), + Value::Primitive(Primitive::String(String::from(entry))).spanned(span), + ); } rows.insert_spanned(row.into_spanned_value()); @@ -45,7 +49,7 @@ pub fn from_csv_string_to_value( pub fn from_csv(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 340ad26e4f..193356dce8 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -39,7 +39,7 @@ pub fn from_ini_string_to_value( pub fn from_ini(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index 36dae4b1b1..f3968dd120 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -45,7 +45,7 @@ pub fn from_json_string_to_value( pub fn from_json(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index 1745548eeb..cf650dd99f 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -43,7 +43,7 @@ pub fn from_toml_string_to_value( pub fn from_toml(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index db56cb830d..cc221df33e 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -61,7 +61,7 @@ pub fn from_xml_string_to_value( pub fn from_xml(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 12121037bc..475926d4ab 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -50,7 +50,7 @@ pub fn from_yaml_string_to_value( pub fn from_yaml(args: CommandArgs) -> Result { let out = args.input; - let span = args.name_span; + let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/get.rs b/src/commands/get.rs index e6d1de26b1..6cdea2e1fb 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -26,7 +26,7 @@ pub fn get(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "Get requires a field or field path", "needs parameter", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 12cd0c020e..219f4d39db 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -7,7 +7,7 @@ use log::trace; pub fn lines(args: CommandArgs) -> Result { let input = args.input; - let span = args.name_span; + let span = args.call_info.name_span; let stream = input .values diff --git a/src/commands/ls.rs b/src/commands/ls.rs index 17843e69e5..72ef4883b9 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -30,7 +30,7 @@ pub fn ls(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( e.to_string(), e.to_string(), - args.name_span, + args.call_info.name_span, )); } } @@ -40,7 +40,7 @@ pub fn ls(args: CommandArgs) -> Result { let mut shell_entries = VecDeque::new(); for entry in entries { - let value = dir_entry_dict(&entry?, args.name_span)?; + let value = dir_entry_dict(&entry?, args.call_info.name_span)?; shell_entries.push_back(ReturnSuccess::value(value)) } Ok(shell_entries.to_output_stream()) diff --git a/src/commands/open.rs b/src/commands/open.rs index 1d47032d36..60b11a72a5 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,3 +1,4 @@ +use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::{Primitive, Switch, Value}; use crate::parser::parse::span::Span; @@ -5,10 +6,11 @@ use crate::prelude::*; use mime::Mime; use std::path::{Path, PathBuf}; use std::str::FromStr; +use uuid::Uuid; command! { Open as open(args, path: Spanned, --raw: Switch,) { - let span = args.name_span; + let span = args.call_info.name_span; let cwd = args .env @@ -21,19 +23,7 @@ command! { let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".spanned(path.span)))?; - let (file_extension, contents, contents_span) = fetch(&full_path, path_str, path.span)?; - // let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item { - // Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, - // _ => { - // return Err(ShellError::labeled_error( - // "Expected string value for filename", - // "expected filename", - // args.expect_nth(0)?.span, - // )); - // } - // }; - - let mut stream = VecDeque::new(); + let (file_extension, contents, contents_span, span_source) = fetch(&full_path, path_str, path.span)?; let file_extension = if raw.is_present() { None @@ -41,6 +31,13 @@ command! { file_extension }; + let mut stream = VecDeque::new(); + + if let Some(uuid) = contents_span.source { + // If we have loaded something, track its source + stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource(uuid, span_source))) + } + match contents { Value::Primitive(Primitive::String(string)) => stream.push_back(ReturnSuccess::value(parse_as_value( @@ -51,7 +48,7 @@ command! { )?) ), - other => stream.push_back(ReturnSuccess::value(other.spanned(span))), + other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), }; stream @@ -62,7 +59,7 @@ pub fn fetch( cwd: &PathBuf, location: &str, span: Span, -) -> Result<(Option, Value, Span), ShellError> { +) -> Result<(Option, Value, Span, SpanSource), ShellError> { let mut cwd = cwd.clone(); if location.starts_with("http:") || location.starts_with("https:") { let response = reqwest::get(location); @@ -74,12 +71,14 @@ pub fn fetch( (mime::APPLICATION, mime::XML) => Ok(( Some("xml".to_string()), Value::string(r.text().unwrap()), - span, + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( Some("json".to_string()), Value::string(r.text().unwrap()), - span, + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { let mut buf: Vec = vec![]; @@ -90,7 +89,12 @@ pub fn fetch( span, ) })?; - Ok((None, Value::Binary(buf), span)) + Ok(( + None, + Value::Binary(buf), + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), + )) } (mime::IMAGE, image_ty) => { let mut buf: Vec = vec![]; @@ -101,12 +105,18 @@ pub fn fetch( span, ) })?; - Ok((Some(image_ty.to_string()), Value::Binary(buf), span)) + Ok(( + Some(image_ty.to_string()), + Value::Binary(buf), + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), + )) } (mime::TEXT, mime::HTML) => Ok(( Some("html".to_string()), Value::string(r.text().unwrap()), - span, + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), )), (mime::TEXT, mime::PLAIN) => { let path_extension = r @@ -120,16 +130,27 @@ pub fn fetch( .map(|name| name.to_string_lossy().to_string()) }); - Ok((path_extension, Value::string(r.text().unwrap()), span)) + Ok(( + path_extension, + Value::string(r.text().unwrap()), + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), + )) } (ty, sub_ty) => Ok(( None, Value::string(format!("Not yet support MIME type: {} {}", ty, sub_ty)), - span, + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), )), } } - None => Ok((None, Value::string(format!("No content type found")), span)), + None => Ok(( + None, + Value::string(format!("No content type found")), + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::Url(r.url().to_string()), + )), }, Err(_) => { return Err(ShellError::labeled_error( @@ -147,9 +168,15 @@ pub fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - span, + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + Err(_) => Ok(( + None, + Value::Binary(bytes), + Span::unknown_with_uuid(Uuid::new_v4()), + SpanSource::File(cwd.to_string_lossy().to_string()), )), - Err(_) => Ok((None, Value::Binary(bytes), span)), }, Err(_) => { return Err(ShellError::labeled_error( diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 0d53ce9942..b28660c2a1 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -7,7 +7,7 @@ pub fn pick(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "Pick requires fields", "needs parameter", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index a27f1944af..62507ed31f 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -84,7 +84,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result Result Result<(), ShellError> { //use subprocess::Exec; - let request = JsonRpc::new("sink", (args.args, args.input)); + let request = JsonRpc::new("sink", (args.call_info, args.input)); let request_raw = serde_json::to_string(&request).unwrap(); let mut tmpfile = tempfile::NamedTempFile::new()?; let _ = writeln!(tmpfile, "{}", request_raw); diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 14b9ba27fa..2924785a8a 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -10,7 +10,7 @@ pub fn ps(args: CommandArgs) -> Result { let list = list .into_iter() - .map(|(_, process)| process_dict(process, args.name_span)) + .map(|(_, process)| process_dict(process, args.call_info.name_span)) .collect::>(); Ok(list.from_input_stream()) diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 362df135db..7752076295 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -3,13 +3,13 @@ use crate::object::base::reject_fields; use crate::prelude::*; pub fn reject(args: CommandArgs) -> Result { - let name_span = args.name_span; + let name_span = args.call_info.name_span; if args.len() == 0 { return Err(ShellError::maybe_labeled_error( "Reject requires fields", "needs parameter", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 776d27d179..5760a34440 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,7 +1,7 @@ use crate::errors::ShellError; -use crate::prelude::*; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; use crate::parser::hir::SyntaxType; +use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; +use crate::prelude::*; use indexmap::IndexMap; pub struct Remove; @@ -31,28 +31,25 @@ impl Command for Remove { } pub fn rm(args: CommandArgs) -> Result { - let mut full_path = args.env - .lock() - .unwrap() - .path() - .to_path_buf(); + let mut full_path = args.env.lock().unwrap().path().to_path_buf(); - - match args.nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? - .as_string()? - .as_str() { - "." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")), - file => full_path.push(file), + match args + .nth(0) + .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? + .as_string()? + .as_str() + { + "." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")), + file => full_path.push(file), } - if full_path.is_dir() { if !args.has("recursive") { return Err(ShellError::labeled_error( - "is a directory", - "", - args.name_span.unwrap())); + "is a directory", + "", + args.call_info.name_span.unwrap(), + )); } std::fs::remove_dir_all(&full_path).expect("can not remove directory"); } else if full_path.is_file() { diff --git a/src/commands/save.rs b/src/commands/save.rs index 312e7a2cb0..a9545651ad 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -8,15 +8,15 @@ use crate::parser::Spanned; use std::path::{Path, PathBuf}; pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.args.positional.is_none() { + if args.call_info.args.positional.is_none() { return Err(ShellError::maybe_labeled_error( "Save requires a filepath", "needs path", - args.name_span, + args.call_info.name_span, )); } - let positional = match args.args.positional { + let positional = match args.call_info.args.positional { None => return Err(ShellError::string("save requires a filepath")), Some(p) => p, }; diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 605747e694..c0aa0f3b07 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -30,7 +30,7 @@ pub fn skip_while(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "Where requires a condition", "needs condition", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index cf34230376..90c12b8f88 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -5,13 +5,13 @@ use log::trace; pub fn split_column(args: CommandArgs) -> Result { let positional: Vec<_> = args.positional_iter().cloned().collect(); - let span = args.name_span; + let span = args.call_info.name_span; if positional.len() == 0 { return Err(ShellError::maybe_labeled_error( "Split-column needs more information", "needs parameter (eg split-column \",\")", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index 06637ff05e..fb530ac8a4 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -6,13 +6,13 @@ use log::trace; pub fn split_row(args: CommandArgs) -> Result { let positional: Vec> = args.positional_iter().cloned().collect(); - let span = args.name_span; + let span = args.call_info.name_span; if positional.len() == 0 { return Err(ShellError::maybe_labeled_error( "Split-row needs more information", "needs parameter (eg split-row \"\\n\")", - args.name_span, + args.call_info.name_span, )); } diff --git a/src/commands/sysinfo.rs b/src/commands/sysinfo.rs index 421980dca6..425505936f 100644 --- a/src/commands/sysinfo.rs +++ b/src/commands/sysinfo.rs @@ -7,10 +7,11 @@ use sys_info::*; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; pub fn sysinfo(args: CommandArgs) -> Result { - let mut idx = SpannedDictBuilder::new(args.name_span); + let name_span = args.call_info.name_span; + let mut idx = SpannedDictBuilder::new(name_span); if let (Ok(name), Ok(version)) = (os_type(), os_release()) { - let mut os_idx = SpannedDictBuilder::new(args.name_span); + let mut os_idx = SpannedDictBuilder::new(name_span); os_idx.insert("name", Primitive::String(name)); os_idx.insert("version", Primitive::String(version)); @@ -18,7 +19,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { } if let (Ok(num_cpu), Ok(cpu_speed)) = (cpu_num(), cpu_speed()) { - let mut cpu_idx = SpannedDictBuilder::new(args.name_span); + let mut cpu_idx = SpannedDictBuilder::new(name_span); cpu_idx.insert("num", Primitive::Int(num_cpu as i64)); cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64)); @@ -26,7 +27,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { } if let Ok(x) = loadavg() { - let mut load_idx = SpannedDictBuilder::new(args.name_span); + let mut load_idx = SpannedDictBuilder::new(name_span); load_idx.insert("1min", Primitive::Float(OF64::from(x.one))); load_idx.insert("5min", Primitive::Float(OF64::from(x.five))); @@ -36,7 +37,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { } if let Ok(x) = mem_info() { - let mut mem_idx = SpannedDictBuilder::new(args.name_span); + let mut mem_idx = SpannedDictBuilder::new(name_span); mem_idx.insert("total", Primitive::Bytes(x.total as u64 * 1024)); mem_idx.insert("free", Primitive::Bytes(x.free as u64 * 1024)); @@ -70,7 +71,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { #[cfg(not(windows))] { if let Ok(x) = boottime() { - let mut boottime_idx = SpannedDictBuilder::new(args.name_span); + let mut boottime_idx = SpannedDictBuilder::new(name_span); boottime_idx.insert("days", Primitive::Int(x.tv_sec / (24 * 3600))); boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24)); boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60)); @@ -84,7 +85,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { if components_list.len() > 0 { let mut v: Vec> = vec![]; for component in components_list { - let mut component_idx = SpannedDictBuilder::new(args.name_span); + let mut component_idx = SpannedDictBuilder::new(name_span); component_idx.insert("name", Primitive::String(component.get_label().to_string())); component_idx.insert( "temp", @@ -107,7 +108,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { let mut v = vec![]; for disk in disks { - let mut disk_idx = SpannedDictBuilder::new(args.name_span); + let mut disk_idx = SpannedDictBuilder::new(name_span); disk_idx.insert("name", Value::string(disk.get_name().to_string_lossy())); disk_idx.insert("available", Value::bytes(disk.get_available_space())); disk_idx.insert("total", Value::bytes(disk.get_total_space())); @@ -121,7 +122,7 @@ pub fn sysinfo(args: CommandArgs) -> Result { let incoming = network.get_income(); let outgoing = network.get_outcome(); - let mut network_idx = SpannedDictBuilder::new(args.name_span); + let mut network_idx = SpannedDictBuilder::new(name_span); network_idx.insert("incoming", Value::bytes(incoming)); network_idx.insert("outgoing", Value::bytes(outgoing)); idx.insert_spanned("network", network_idx); diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index c6ace91032..5cd4c913ce 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -42,7 +42,7 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { pub fn to_json(args: CommandArgs) -> Result { let out = args.input; - let name_span = args.name_span; + let name_span = args.call_info.name_span; Ok(out .values .map( diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 6ffbe6ba38..354976a87c 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -32,7 +32,7 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { pub fn to_toml(args: CommandArgs) -> Result { let out = args.input; - let name_span = args.name_span; + let name_span = args.call_info.name_span; Ok(out .values diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 1a64898bac..66db5afa40 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -40,7 +40,7 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { pub fn to_yaml(args: CommandArgs) -> Result { let out = args.input; - let name_span = args.name_span; + let name_span = args.call_info.name_span; Ok(out .values .map( diff --git a/src/commands/trim.rs b/src/commands/trim.rs index e0f2d42b0c..838875faa0 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -2,8 +2,6 @@ use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -// TODO: "Amount remaining" wrapper - pub fn trim(args: CommandArgs) -> Result { let input = args.input; diff --git a/src/context.rs b/src/context.rs index 05be6ae0f3..9be7463a0f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,18 +1,45 @@ -use crate::commands::command::{Sink, SinkCommandArgs}; +use crate::commands::command::{CallInfo, Sink, SinkCommandArgs}; use crate::parser::{ registry::{Args, CommandConfig, CommandRegistry}, Span, }; use crate::prelude::*; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; use indexmap::IndexMap; +use std::collections::HashMap; use std::error::Error; use std::sync::Arc; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum SpanSource { + Url(String), + File(String), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SourceMap(HashMap); + +impl SourceMap { + pub fn insert(&mut self, uuid: Uuid, span_source: SpanSource) { + self.0.insert(uuid, span_source); + } + + pub fn get(&self, uuid: &Uuid) -> Option<&SpanSource> { + self.0.get(uuid) + } + + pub fn new() -> SourceMap { + SourceMap(HashMap::new()) + } +} + #[derive(Clone)] pub struct Context { commands: IndexMap>, sinks: IndexMap>, + crate source_map: SourceMap, crate host: Arc>, crate env: Arc>, } @@ -22,6 +49,7 @@ impl Context { Ok(Context { commands: indexmap::IndexMap::new(), sinks: indexmap::IndexMap::new(), + source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), env: Arc::new(Mutex::new(Environment::basic()?)), }) @@ -39,6 +67,10 @@ impl Context { } } + pub fn add_span_source(&mut self, uuid: Uuid, span_source: SpanSource) { + self.source_map.insert(uuid, span_source); + } + crate fn has_sink(&self, name: &str) -> bool { self.sinks.contains_key(name) } @@ -56,8 +88,11 @@ impl Context { ) -> Result<(), ShellError> { let command_args = SinkCommandArgs { ctx: self.clone(), - name_span, - args, + call_info: CallInfo { + name_span, + source_map: self.source_map.clone(), + args, + }, input, }; @@ -80,14 +115,18 @@ impl Context { &mut self, command: Arc, name_span: Option, + source_map: SourceMap, args: Args, input: InputStream, ) -> Result { let command_args = CommandArgs { host: self.host.clone(), env: self.env.clone(), - name_span, - args, + call_info: CallInfo { + name_span, + source_map, + args, + }, input, }; diff --git a/src/errors.rs b/src/errors.rs index 3b84eba147..8d9f44fd97 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -17,7 +17,12 @@ impl Description { pub fn from(item: Spanned>) -> Description { match item { Spanned { - span: Span { start: 0, end: 0 }, + span: + Span { + start: 0, + end: 0, + source: None, + }, item, } => Description::Synthetic(item.into()), Spanned { span, item } => Description::Source(Spanned::from_item(item.into(), span)), diff --git a/src/lib.rs b/src/lib.rs index 4528be06b6..f5f7f90f79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ #![feature(bind_by_move_pattern_guards)] #![feature(box_syntax)] #![feature(type_ascription)] +#![feature(option_flattening)] #[macro_use] mod prelude; @@ -23,7 +24,8 @@ mod plugin; mod shell; mod stream; -pub use crate::commands::command::{ReturnSuccess, ReturnValue}; +pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; +pub use crate::context::SpanSource; pub use crate::env::host::BasicHost; pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::Spanned; diff --git a/src/main.rs b/src/main.rs index afb0bb32a3..9ad29a625a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,8 @@ use log::LevelFilter; use std::error::Error; fn main() -> Result<(), Box> { - let matches = App::new("nu shell") - .version("0.5") + let matches = App::new("nushell") + .version("0.1.3") .arg( Arg::with_name("loglevel") .short("l") diff --git a/src/parser/parse/span.rs b/src/parser/parse/span.rs index d82f43468f..7d6aa7334d 100644 --- a/src/parser/parse/span.rs +++ b/src/parser/parse/span.rs @@ -3,6 +3,7 @@ use derive_new::new; use getset::Getters; use serde::Serialize; use serde_derive::Deserialize; +use uuid::Uuid; #[derive( new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, @@ -75,13 +76,17 @@ impl Spanned { pub struct Span { crate start: usize, crate end: usize, - // source: &'source str, + pub source: Option, } impl From> for Span { fn from(input: Option) -> Span { match input { - None => Span { start: 0, end: 0 }, + None => Span { + start: 0, + end: 0, + source: None, + }, Some(span) => span, } } @@ -104,6 +109,7 @@ impl From> for Span { Span { start: input.offset, end: input.offset + input.fragment.len(), + source: None, } } } @@ -113,6 +119,7 @@ impl From<(nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)> for Spa Span { start: input.0.offset, end: input.1.offset, + source: None, } } } @@ -122,6 +129,7 @@ impl From<(usize, usize)> for Span { Span { start: input.0, end: input.1, + source: None, } } } @@ -131,13 +139,26 @@ impl From<&std::ops::Range> for Span { Span { start: input.start, end: input.end, + source: None, } } } impl Span { pub fn unknown() -> Span { - Span { start: 0, end: 0 } + Span { + start: 0, + end: 0, + source: None, + } + } + + pub fn unknown_with_uuid(uuid: Uuid) -> Span { + Span { + start: 0, + end: 0, + source: Some(uuid), + } } pub fn is_unknown(&self) -> bool { @@ -154,6 +175,7 @@ impl language_reporting::ReportingSpan for Span { Span { start, end: self.end, + source: None, } } @@ -161,6 +183,7 @@ impl language_reporting::ReportingSpan for Span { Span { start: self.start, end, + source: None, } } diff --git a/src/plugin.rs b/src/plugin.rs index 4126b9d08f..fdc6c8a6c0 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,11 +1,11 @@ -use crate::{Args, CommandConfig, ReturnValue, ShellError, Spanned, Value}; +use crate::{CallInfo, CommandConfig, ReturnValue, ShellError, Spanned, Value}; use serde::{Deserialize, Serialize}; use std::io; pub trait Plugin { fn config(&mut self) -> Result; #[allow(unused)] - fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { + fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> { Err(ShellError::string( "`begin_filter` not implemented in plugin", )) @@ -15,7 +15,7 @@ pub trait Plugin { Err(ShellError::string("`filter` not implemented in plugin")) } #[allow(unused)] - fn sink(&mut self, args: Args, input: Vec>) {} + fn sink(&mut self, call_info: CallInfo, input: Vec>) {} fn quit(&mut self) { return; @@ -134,8 +134,14 @@ fn send_response(result: T) { #[allow(non_camel_case_types)] pub enum NuCommand { config, - begin_filter { params: Args }, - filter { params: Spanned }, - sink { params: (Args, Vec>) }, + begin_filter { + params: CallInfo, + }, + filter { + params: Spanned, + }, + sink { + params: (CallInfo, Vec>), + }, quit, } diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index 8c525f3886..b71a8a88a0 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,6 +1,10 @@ +#![feature(option_flattening)] use crossterm::{cursor, terminal, Attribute, RawScreen}; use indexmap::IndexMap; -use nu::{serve_plugin, Args, CommandConfig, NamedType, Plugin, ShellError, Spanned, Value}; +use nu::{ + serve_plugin, CallInfo, CommandConfig, NamedType, Plugin, ShellError, SpanSource, Spanned, + Value, +}; use pretty_hex::*; struct BinaryView; @@ -25,14 +29,15 @@ impl Plugin for BinaryView { }) } - fn sink(&mut self, args: Args, input: Vec>) { + fn sink(&mut self, call_info: CallInfo, input: Vec>) { for v in input { match v { Spanned { item: Value::Binary(b), - .. + span, } => { - let _ = view_binary(&b, args.has("lores")); + let source = span.source.map(|x| call_info.source_map.get(&x)).flatten(); + let _ = view_binary(&b, source, call_info.args.has("lores")); } _ => {} } @@ -40,17 +45,21 @@ impl Plugin for BinaryView { } } -fn view_binary(b: &[u8], lores_mode: bool) -> Result<(), Box> { +fn view_binary( + b: &[u8], + source: Option<&SpanSource>, + lores_mode: bool, +) -> Result<(), Box> { if b.len() > 3 { match (b[0], b[1], b[2]) { (0x4e, 0x45, 0x53) => { - view_contents_interactive(b, lores_mode)?; + view_contents_interactive(b, source, lores_mode)?; return Ok(()); } _ => {} } } - view_contents(b, lores_mode)?; + view_contents(b, source, lores_mode)?; Ok(()) } @@ -247,7 +256,11 @@ fn load_from_jpg_buffer(buffer: &[u8]) -> Option<(RawImageBuffer)> { }) } -pub fn view_contents(buffer: &[u8], lores_mode: bool) -> Result<(), Box> { +pub fn view_contents( + buffer: &[u8], + _source: Option<&SpanSource>, + lores_mode: bool, +) -> Result<(), Box> { let mut raw_image_buffer = load_from_png_buffer(buffer); if raw_image_buffer.is_none() { @@ -332,14 +345,29 @@ pub fn view_contents(buffer: &[u8], lores_mode: bool) -> Result<(), Box, lores_mode: bool, ) -> Result<(), Box> { use rawkey::{KeyCode, RawKey}; + let sav_path = if let Some(SpanSource::File(f)) = source { + let mut path = std::path::PathBuf::from(f); + path.set_extension("sav"); + Some(path) + } else { + None + }; + let mut nes = neso::Nes::new(48000.0); let rawkey = RawKey::new(); nes.load_rom(&buffer); + if let Some(ref sav_path) = sav_path { + if let Ok(contents) = std::fs::read(sav_path) { + let _ = nes.load_state(&contents); + } + } + nes.reset(); if let Ok(_raw) = RawScreen::into_raw_mode() { @@ -403,6 +431,13 @@ pub fn view_contents_interactive( } } + if let Some(ref sav_path) = sav_path { + let buffer = nes.save_state(); + if let Ok(buffer) = buffer { + let _ = std::fs::write(sav_path, buffer); + } + } + let cursor = cursor(); let _ = cursor.show(); diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 8d69f91d5d..f03edc985f 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,7 +1,7 @@ use indexmap::IndexMap; use nu::{ - serve_plugin, Args, CommandConfig, NamedType, Plugin, PositionalType, Primitive, ReturnSuccess, - ReturnValue, ShellError, Spanned, SpannedItem, Value, + serve_plugin, CallInfo, CommandConfig, NamedType, Plugin, PositionalType, Primitive, + ReturnSuccess, ReturnValue, ShellError, Spanned, SpannedItem, Value, }; struct Inc { @@ -99,18 +99,18 @@ impl Plugin for Inc { rest_positional: true, }) } - fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { - if args.has("major") { + fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> { + if call_info.args.has("major") { self.major = true; } - if args.has("minor") { + if call_info.args.has("minor") { self.minor = true; } - if args.has("patch") { + if call_info.args.has("patch") { self.patch = true; } - if let Some(args) = args.positional { + if let Some(args) = call_info.args.positional { for arg in args { match arg { Spanned { diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index 0a1fdf7a9f..e7a4f6003a 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,7 +1,7 @@ use indexmap::IndexMap; use nu::{ - serve_plugin, Args, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Spanned, Value, + serve_plugin, CallInfo, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue, + ShellError, Spanned, Value, }; struct NewSkip { @@ -24,8 +24,8 @@ impl Plugin for NewSkip { rest_positional: true, }) } - fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { - if let Some(args) = args.positional { + fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> { + if let Some(args) = call_info.args.positional { for arg in args { match arg { Spanned { @@ -34,7 +34,13 @@ impl Plugin for NewSkip { } => { self.skip_amount = i; } - _ => return Err(ShellError::labeled_error("Unrecognized type in params", "expected an integer", arg.span)), + _ => { + return Err(ShellError::labeled_error( + "Unrecognized type in params", + "expected an integer", + arg.span, + )) + } } } } diff --git a/src/plugins/tree.rs b/src/plugins/tree.rs index cfaf269909..b2bc9e1ca5 100644 --- a/src/plugins/tree.rs +++ b/src/plugins/tree.rs @@ -1,6 +1,6 @@ use derive_new::new; use indexmap::IndexMap; -use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value}; +use nu::{serve_plugin, CallInfo, CommandConfig, Plugin, ShellError, Spanned, Value}; use ptree::item::StringItem; use ptree::output::print_tree_with; use ptree::print_config::PrintConfig; @@ -91,7 +91,7 @@ impl Plugin for TreeViewer { }) } - fn sink(&mut self, _args: Args, input: Vec>) { + fn sink(&mut self, _call_info: CallInfo, input: Vec>) { if input.len() > 0 { for i in input.iter() { let view = TreeView::from_value(&i); diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 30aa847998..52c8b2017c 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -1,7 +1,7 @@ mod helpers; -use helpers as h; use h::in_directory as cwd; +use helpers as h; #[test] fn lines() { @@ -14,18 +14,22 @@ fn lines() { #[test] fn open_csv() { - nu!(output, + nu!( + output, cwd("tests/fixtures/formats"), - "open caco3_plastics.csv | get root | first 1 | get origin | echo $it"); + "open caco3_plastics.csv | get root | first 1 | get origin | echo $it" + ); assert_eq!(output, "SPAIN"); } #[test] fn open_toml() { - nu!(output, - cwd("tests/fixtures/formats"), - "open cargo_sample.toml | get package.edition | echo $it"); + nu!( + output, + cwd("tests/fixtures/formats"), + "open cargo_sample.toml | get package.edition | echo $it" + ); assert_eq!(output, "2018"); } @@ -41,32 +45,40 @@ fn open_json() { #[test] fn open_xml() { - nu!(output, + nu!( + output, cwd("tests/fixtures/formats"), - "open jonathan.xml | get rss.channel.item.link | echo $it"); + "open jonathan.xml | get rss.channel.item.link | echo $it" + ); - assert_eq!(output, "http://www.jonathanturner.org/2015/10/off-to-new-adventures.html") + assert_eq!( + output, + "http://www.jonathanturner.org/2015/10/off-to-new-adventures.html" + ) } #[test] fn open_ini() { - nu!(output, + nu!( + output, cwd("tests/fixtures/formats"), - "open sample.ini | get SectionOne.integer | echo $it"); + "open sample.ini | get SectionOne.integer | echo $it" + ); assert_eq!(output, "1234") } #[test] fn open_error_if_file_not_found() { - nu_error!(output, + nu_error!( + output, cwd("tests/fixtures/formats"), - "open i_dont_exist.txt | echo $it"); + "open i_dont_exist.txt | echo $it" + ); assert!(output.contains("File cound not be opened")); } - #[test] fn rm() { let directory = "tests/fixtures/nuplayground"; @@ -74,9 +86,7 @@ fn rm() { h::create_file_at(&file); - nu!(_output, - cwd(directory), - "rm rm_test.txt"); + nu!(_output, cwd(directory), "rm rm_test.txt"); assert!(!h::file_exists_at(&file)); } @@ -85,30 +95,34 @@ fn rm() { fn can_remove_directory_contents_with_recursive_flag() { let path = "tests/fixtures/nuplayground/rm_test"; - if h::file_exists_at(&path) { h::delete_directory_at(path) } + if h::file_exists_at(&path) { + h::delete_directory_at(path) + } h::create_directory_at(path); for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() { h::create_file_at(&format!("{}/{}", path, f)); - }; + } - nu!(_output, + nu!( + _output, cwd("tests/fixtures/nuplayground"), - "rm rm_test --recursive"); + "rm rm_test --recursive" + ); assert!(!h::file_exists_at(&path)); } #[test] fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() { - let path = "tests/fixtures/nuplayground/rm_test"; + let path = "tests/fixtures/nuplayground/rm_test_2"; - if h::file_exists_at(&path) { h::delete_directory_at(path) } + if h::file_exists_at(&path) { + h::delete_directory_at(path) + } h::create_directory_at(path); - nu_error!(output, - cwd("tests/fixtures/nuplayground"), - "rm rm_test"); + nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm rm_test_2"); assert!(h::file_exists_at(&path)); assert!(output.contains("is a directory")); @@ -117,18 +131,14 @@ fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() { #[test] fn rm_error_if_attempting_to_delete_single_dot_as_argument() { - nu_error!(output, - cwd("tests/fixtures/nuplayground"), - "rm ."); + nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm ."); assert!(output.contains("may not be removed")); } #[test] fn rm_error_if_attempting_to_delete_two_dot_as_argument() { - nu_error!(output, - cwd("tests/fixtures/nuplayground"), - "rm .."); + nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm .."); assert!(output.contains("may not be removed")); }