Merge pull request #198 from jonathandturner/source_spans

Source spans
This commit is contained in:
Jonathan Turner 2019-07-20 14:46:50 +12:00 committed by GitHub
commit 58e09e2295
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 436 additions and 177 deletions

3
Cargo.lock generated
View File

@ -1702,6 +1702,7 @@ dependencies = [
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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 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)", "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)", "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]] [[package]]
@ -3202,6 +3204,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]

View File

@ -76,6 +76,8 @@ crossterm = "0.9.6"
tempfile = "3.1.0" tempfile = "3.1.0"
image = "0.21.2" image = "0.21.2"
semver = "0.9.0" semver = "0.9.0"
uuid = {version = "0.7.4", features = [ "v4", "serde" ]}
syntect = "3.2.0"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6.1" pretty_assertions = "0.6.1"

BIN
assets/themes.bin Normal file

Binary file not shown.

View File

@ -495,6 +495,7 @@ fn classify_command(
Ok(ClassifiedCommand::Internal(InternalCommand { Ok(ClassifiedCommand::Internal(InternalCommand {
command, command,
name_span: Some(head.span().clone()), name_span: Some(head.span().clone()),
source_map: context.source_map.clone(),
args, args,
})) }))
} }

View File

@ -1,7 +1,9 @@
use crate::commands::command::SinkCommandArgs; use crate::commands::command::SinkCommandArgs;
use crate::context::{SourceMap, SpanSource};
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::format::GenericView; use crate::format::GenericView;
use crate::prelude::*; use crate::prelude::*;
use std::path::Path;
pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> {
if args.input.len() > 0 { if args.input.len() > 0 {
@ -11,6 +13,8 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> {
} = args.input[0] } = args.input[0]
{ {
args.ctx.get_sink("binaryview").run(args)?; 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) { } else if equal_shapes(&args.input) {
args.ctx.get_sink("table").run(args)?; args.ctx.get_sink("table").run(args)?;
} else { } else {
@ -44,3 +48,76 @@ fn equal_shapes(input: &Vec<Spanned<Value>>) -> bool {
true true
} }
fn is_single_text_value(input: &Vec<Spanned<Value>>) -> 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<Value>, 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);
}
}
_ => {}
}
}

View File

@ -13,7 +13,7 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Can not change to home directory", "Can not change to home directory",
"can not go to home", "can not go to home",
args.name_span, args.call_info.name_span,
)) ))
} }
}, },

View File

@ -1,4 +1,5 @@
use crate::commands::command::Sink; use crate::commands::command::Sink;
use crate::context::SourceMap;
use crate::parser::{registry::Args, Span, Spanned, TokenNode}; use crate::parser::{registry::Args, Span, Spanned, TokenNode};
use crate::prelude::*; use crate::prelude::*;
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
@ -116,6 +117,7 @@ impl SinkCommand {
crate struct InternalCommand { crate struct InternalCommand {
crate command: Arc<dyn Command>, crate command: Arc<dyn Command>,
crate name_span: Option<Span>, crate name_span: Option<Span>,
crate source_map: SourceMap,
crate args: Args, crate args: Args,
} }
@ -134,8 +136,13 @@ impl InternalCommand {
let objects: InputStream = let objects: InputStream =
trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects);
let result = let result = context.run_command(
context.run_command(self.command, self.name_span.clone(), self.args, objects)?; self.command,
self.name_span.clone(),
self.source_map,
self.args,
objects,
)?;
let mut result = result.values; let mut result = result.values;
@ -146,6 +153,9 @@ impl InternalCommand {
CommandAction::ChangePath(path) => { CommandAction::ChangePath(path) => {
context.env.lock().unwrap().path = 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), CommandAction::Exit => std::process::exit(0),
}, },

View File

@ -16,7 +16,7 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> {
} }
let string = i.as_string().map_err(labelled( let string = i.as_string().map_err(labelled(
args.name_span, args.call_info.name_span,
"Given non-string data", "Given non-string data",
"expected strings from pipeline", "expected strings from pipeline",
))?; ))?;

View File

@ -1,3 +1,5 @@
use crate::context::SourceMap;
use crate::context::SpanSource;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
use crate::parser::{ use crate::parser::{
@ -8,54 +10,61 @@ use crate::prelude::*;
use getset::Getters; use getset::Getters;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; 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<Span>,
}
#[derive(Getters)] #[derive(Getters)]
#[get = "crate"] #[get = "crate"]
pub struct CommandArgs { pub struct CommandArgs {
pub host: Arc<Mutex<dyn Host + Send>>, pub host: Arc<Mutex<dyn Host + Send>>,
pub env: Arc<Mutex<Environment>>, pub env: Arc<Mutex<Environment>>,
pub name_span: Option<Span>, pub call_info: CallInfo,
pub args: Args,
pub input: InputStream, pub input: InputStream,
} }
impl CommandArgs { impl CommandArgs {
pub fn nth(&self, pos: usize) -> Option<&Spanned<Value>> { pub fn nth(&self, pos: usize) -> Option<&Spanned<Value>> {
self.args.nth(pos) self.call_info.args.nth(pos)
} }
pub fn positional_iter(&self) -> impl Iterator<Item = &Spanned<Value>> { pub fn positional_iter(&self) -> impl Iterator<Item = &Spanned<Value>> {
self.args.positional_iter() self.call_info.args.positional_iter()
} }
pub fn expect_nth(&self, pos: usize) -> Result<&Spanned<Value>, ShellError> { pub fn expect_nth(&self, pos: usize) -> Result<&Spanned<Value>, ShellError> {
self.args.expect_nth(pos) self.call_info.args.expect_nth(pos)
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.args.len() self.call_info.args.len()
} }
pub fn get(&self, name: &str) -> Option<&Spanned<Value>> { pub fn get(&self, name: &str) -> Option<&Spanned<Value>> {
self.args.get(name) self.call_info.args.get(name)
} }
#[allow(unused)] #[allow(unused)]
pub fn has(&self, name: &str) -> bool { pub fn has(&self, name: &str) -> bool {
self.args.has(name) self.call_info.args.has(name)
} }
} }
pub struct SinkCommandArgs { pub struct SinkCommandArgs {
pub ctx: Context, pub ctx: Context,
pub name_span: Option<Span>, pub call_info: CallInfo,
pub args: Args,
pub input: Vec<Spanned<Value>>, pub input: Vec<Spanned<Value>>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum CommandAction { pub enum CommandAction {
ChangePath(PathBuf), ChangePath(PathBuf),
AddSpanSource(Uuid, SpanSource),
Exit, Exit,
} }
@ -82,6 +91,10 @@ impl ReturnSuccess {
Ok(ReturnSuccess::Value(input.into())) Ok(ReturnSuccess::Value(input.into()))
} }
pub fn action(input: CommandAction) -> ReturnValue {
Ok(ReturnSuccess::Action(input))
}
pub fn spanned_value(input: Value, span: Span) -> ReturnValue { pub fn spanned_value(input: Value, span: Span) -> ReturnValue {
Ok(ReturnSuccess::Value(Spanned::from_item(input, span))) Ok(ReturnSuccess::Value(Spanned::from_item(input, span)))
} }

View File

@ -39,10 +39,10 @@ impl Command for Config {
} }
pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
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.call_info.args.positional);
trace!("{:#?}", args.args.named); trace!("{:#?}", args.call_info.args.named);
if let Some(v) = args.get("get") { if let Some(v) = args.get("get") {
let key = v.as_string()?; let key = v.as_string()?;
@ -95,7 +95,7 @@ pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
if args.len() == 0 { 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"))) Err(ShellError::string(format!("Unimplemented")))

View File

@ -8,7 +8,7 @@ pub fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"First requires an amount", "First requires an amount",
"needs parameter", "needs parameter",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -6,8 +6,9 @@ pub fn from_csv_string_to_value(
s: String, s: String,
span: impl Into<Span>, span: impl Into<Span>,
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> { ) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
let mut reader = ReaderBuilder::new()
let mut reader = ReaderBuilder::new().has_headers(false).from_reader(s.as_bytes()); .has_headers(false)
.from_reader(s.as_bytes());
let span = span.into(); let span = span.into();
let mut fields: VecDeque<String> = VecDeque::new(); let mut fields: VecDeque<String> = VecDeque::new();
@ -28,9 +29,12 @@ pub fn from_csv_string_to_value(
let row_values = row_values?; let row_values = row_values?;
let mut row = SpannedDictBuilder::new(span); let mut row = SpannedDictBuilder::new(span);
for (idx, entry) in row_values.iter().enumerate() { 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()); 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<OutputStream, ShellError> { pub fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values

View File

@ -39,7 +39,7 @@ pub fn from_ini_string_to_value(
pub fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map(move |a| match a.item { .map(move |a| match a.item {

View File

@ -45,7 +45,7 @@ pub fn from_json_string_to_value(
pub fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map(move |a| match a.item { .map(move |a| match a.item {

View File

@ -43,7 +43,7 @@ pub fn from_toml_string_to_value(
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map(move |a| match a.item { .map(move |a| match a.item {

View File

@ -61,7 +61,7 @@ pub fn from_xml_string_to_value(
pub fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map(move |a| match a.item { .map(move |a| match a.item {

View File

@ -50,7 +50,7 @@ pub fn from_yaml_string_to_value(
pub fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map(move |a| match a.item { .map(move |a| match a.item {

View File

@ -26,7 +26,7 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Get requires a field or field path", "Get requires a field or field path",
"needs parameter", "needs parameter",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -7,7 +7,7 @@ use log::trace;
pub fn lines(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn lines(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
let span = args.name_span; let span = args.call_info.name_span;
let stream = input let stream = input
.values .values

View File

@ -30,7 +30,7 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
e.to_string(), e.to_string(),
e.to_string(), e.to_string(),
args.name_span, args.call_info.name_span,
)); ));
} }
} }
@ -40,7 +40,7 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut shell_entries = VecDeque::new(); let mut shell_entries = VecDeque::new();
for entry in entries { 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)) shell_entries.push_back(ReturnSuccess::value(value))
} }
Ok(shell_entries.to_output_stream()) Ok(shell_entries.to_output_stream())

View File

@ -1,3 +1,4 @@
use crate::context::SpanSource;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Switch, Value}; use crate::object::{Primitive, Switch, Value};
use crate::parser::parse::span::Span; use crate::parser::parse::span::Span;
@ -5,10 +6,11 @@ use crate::prelude::*;
use mime::Mime; use mime::Mime;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use uuid::Uuid;
command! { command! {
Open as open(args, path: Spanned<PathBuf>, --raw: Switch,) { Open as open(args, path: Spanned<PathBuf>, --raw: Switch,) {
let span = args.name_span; let span = args.call_info.name_span;
let cwd = args let cwd = args
.env .env
@ -21,19 +23,7 @@ command! {
let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".spanned(path.span)))?; 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, span_source) = 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 = if raw.is_present() { let file_extension = if raw.is_present() {
None None
@ -41,6 +31,13 @@ command! {
file_extension 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 { match contents {
Value::Primitive(Primitive::String(string)) => Value::Primitive(Primitive::String(string)) =>
stream.push_back(ReturnSuccess::value(parse_as_value( 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 stream
@ -62,7 +59,7 @@ pub fn fetch(
cwd: &PathBuf, cwd: &PathBuf,
location: &str, location: &str,
span: Span, span: Span,
) -> Result<(Option<String>, Value, Span), ShellError> { ) -> Result<(Option<String>, Value, Span, SpanSource), ShellError> {
let mut cwd = cwd.clone(); let mut cwd = cwd.clone();
if location.starts_with("http:") || location.starts_with("https:") { if location.starts_with("http:") || location.starts_with("https:") {
let response = reqwest::get(location); let response = reqwest::get(location);
@ -74,12 +71,14 @@ pub fn fetch(
(mime::APPLICATION, mime::XML) => Ok(( (mime::APPLICATION, mime::XML) => Ok((
Some("xml".to_string()), Some("xml".to_string()),
Value::string(r.text().unwrap()), Value::string(r.text().unwrap()),
span, Span::unknown_with_uuid(Uuid::new_v4()),
SpanSource::Url(r.url().to_string()),
)), )),
(mime::APPLICATION, mime::JSON) => Ok(( (mime::APPLICATION, mime::JSON) => Ok((
Some("json".to_string()), Some("json".to_string()),
Value::string(r.text().unwrap()), Value::string(r.text().unwrap()),
span, Span::unknown_with_uuid(Uuid::new_v4()),
SpanSource::Url(r.url().to_string()),
)), )),
(mime::APPLICATION, mime::OCTET_STREAM) => { (mime::APPLICATION, mime::OCTET_STREAM) => {
let mut buf: Vec<u8> = vec![]; let mut buf: Vec<u8> = vec![];
@ -90,7 +89,12 @@ pub fn fetch(
span, 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) => { (mime::IMAGE, image_ty) => {
let mut buf: Vec<u8> = vec![]; let mut buf: Vec<u8> = vec![];
@ -101,12 +105,18 @@ pub fn fetch(
span, 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(( (mime::TEXT, mime::HTML) => Ok((
Some("html".to_string()), Some("html".to_string()),
Value::string(r.text().unwrap()), Value::string(r.text().unwrap()),
span, Span::unknown_with_uuid(Uuid::new_v4()),
SpanSource::Url(r.url().to_string()),
)), )),
(mime::TEXT, mime::PLAIN) => { (mime::TEXT, mime::PLAIN) => {
let path_extension = r let path_extension = r
@ -120,16 +130,27 @@ pub fn fetch(
.map(|name| name.to_string_lossy().to_string()) .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(( (ty, sub_ty) => Ok((
None, None,
Value::string(format!("Not yet support MIME type: {} {}", ty, sub_ty)), 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(_) => { Err(_) => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
@ -147,9 +168,15 @@ pub fn fetch(
cwd.extension() cwd.extension()
.map(|name| name.to_string_lossy().to_string()), .map(|name| name.to_string_lossy().to_string()),
Value::string(s), 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(_) => { Err(_) => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(

View File

@ -7,7 +7,7 @@ pub fn pick(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Pick requires fields", "Pick requires fields",
"needs parameter", "needs parameter",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -84,7 +84,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
let mut reader = BufReader::new(stdout); let mut reader = BufReader::new(stdout);
let request = JsonRpc::new("begin_filter", args.args); let request = JsonRpc::new("begin_filter", args.call_info);
let request_raw = serde_json::to_string(&request).unwrap(); let request_raw = serde_json::to_string(&request).unwrap();
stdin.write(format!("{}\n", request_raw).as_bytes())?; stdin.write(format!("{}\n", request_raw).as_bytes())?;
let mut input = String::new(); let mut input = String::new();
@ -183,7 +183,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> { pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> {
//use subprocess::Exec; //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 request_raw = serde_json::to_string(&request).unwrap();
let mut tmpfile = tempfile::NamedTempFile::new()?; let mut tmpfile = tempfile::NamedTempFile::new()?;
let _ = writeln!(tmpfile, "{}", request_raw); let _ = writeln!(tmpfile, "{}", request_raw);

View File

@ -10,7 +10,7 @@ pub fn ps(args: CommandArgs) -> Result<OutputStream, ShellError> {
let list = list let list = list
.into_iter() .into_iter()
.map(|(_, process)| process_dict(process, args.name_span)) .map(|(_, process)| process_dict(process, args.call_info.name_span))
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
Ok(list.from_input_stream()) Ok(list.from_input_stream())

View File

@ -3,13 +3,13 @@ use crate::object::base::reject_fields;
use crate::prelude::*; use crate::prelude::*;
pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.name_span; let name_span = args.call_info.name_span;
if args.len() == 0 { if args.len() == 0 {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Reject requires fields", "Reject requires fields",
"needs parameter", "needs parameter",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -1,7 +1,7 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*;
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxType;
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
use crate::prelude::*;
use indexmap::IndexMap; use indexmap::IndexMap;
pub struct Remove; pub struct Remove;
@ -31,28 +31,25 @@ impl Command for Remove {
} }
pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut full_path = args.env let mut full_path = args.env.lock().unwrap().path().to_path_buf();
.lock()
.unwrap()
.path()
.to_path_buf();
match args
match args.nth(0) .nth(0)
.ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))?
.as_string()? .as_string()?
.as_str() { .as_str()
"." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")), {
file => full_path.push(file), "." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")),
file => full_path.push(file),
} }
if full_path.is_dir() { if full_path.is_dir() {
if !args.has("recursive") { if !args.has("recursive") {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"is a directory", "is a directory",
"", "",
args.name_span.unwrap())); args.call_info.name_span.unwrap(),
));
} }
std::fs::remove_dir_all(&full_path).expect("can not remove directory"); std::fs::remove_dir_all(&full_path).expect("can not remove directory");
} else if full_path.is_file() { } else if full_path.is_file() {

View File

@ -8,15 +8,15 @@ use crate::parser::Spanned;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { 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( return Err(ShellError::maybe_labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "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")), None => return Err(ShellError::string("save requires a filepath")),
Some(p) => p, Some(p) => p,
}; };

View File

@ -30,7 +30,7 @@ pub fn skip_while(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Where requires a condition", "Where requires a condition",
"needs condition", "needs condition",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -5,13 +5,13 @@ use log::trace;
pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
let positional: Vec<_> = args.positional_iter().cloned().collect(); let positional: Vec<_> = args.positional_iter().cloned().collect();
let span = args.name_span; let span = args.call_info.name_span;
if positional.len() == 0 { if positional.len() == 0 {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Split-column needs more information", "Split-column needs more information",
"needs parameter (eg split-column \",\")", "needs parameter (eg split-column \",\")",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -6,13 +6,13 @@ use log::trace;
pub fn split_row(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn split_row(args: CommandArgs) -> Result<OutputStream, ShellError> {
let positional: Vec<Spanned<Value>> = args.positional_iter().cloned().collect(); let positional: Vec<Spanned<Value>> = args.positional_iter().cloned().collect();
let span = args.name_span; let span = args.call_info.name_span;
if positional.len() == 0 { if positional.len() == 0 {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Split-row needs more information", "Split-row needs more information",
"needs parameter (eg split-row \"\\n\")", "needs parameter (eg split-row \"\\n\")",
args.name_span, args.call_info.name_span,
)); ));
} }

View File

@ -7,10 +7,11 @@ use sys_info::*;
use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt};
pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
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()) { 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("name", Primitive::String(name));
os_idx.insert("version", Primitive::String(version)); os_idx.insert("version", Primitive::String(version));
@ -18,7 +19,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
if let (Ok(num_cpu), Ok(cpu_speed)) = (cpu_num(), cpu_speed()) { 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("num", Primitive::Int(num_cpu as i64));
cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64)); cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64));
@ -26,7 +27,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
if let Ok(x) = loadavg() { 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("1min", Primitive::Float(OF64::from(x.one)));
load_idx.insert("5min", Primitive::Float(OF64::from(x.five))); load_idx.insert("5min", Primitive::Float(OF64::from(x.five)));
@ -36,7 +37,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
if let Ok(x) = mem_info() { 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("total", Primitive::Bytes(x.total as u64 * 1024));
mem_idx.insert("free", Primitive::Bytes(x.free as u64 * 1024)); mem_idx.insert("free", Primitive::Bytes(x.free as u64 * 1024));
@ -70,7 +71,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
if let Ok(x) = boottime() { 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("days", Primitive::Int(x.tv_sec / (24 * 3600)));
boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24)); boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24));
boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60)); boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60));
@ -84,7 +85,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
if components_list.len() > 0 { if components_list.len() > 0 {
let mut v: Vec<Spanned<Value>> = vec![]; let mut v: Vec<Spanned<Value>> = vec![];
for component in components_list { 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("name", Primitive::String(component.get_label().to_string()));
component_idx.insert( component_idx.insert(
"temp", "temp",
@ -107,7 +108,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut v = vec![]; let mut v = vec![];
for disk in disks { 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("name", Value::string(disk.get_name().to_string_lossy()));
disk_idx.insert("available", Value::bytes(disk.get_available_space())); disk_idx.insert("available", Value::bytes(disk.get_available_space()));
disk_idx.insert("total", Value::bytes(disk.get_total_space())); disk_idx.insert("total", Value::bytes(disk.get_total_space()));
@ -121,7 +122,7 @@ pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
let incoming = network.get_income(); let incoming = network.get_income();
let outgoing = network.get_outcome(); 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("incoming", Value::bytes(incoming));
network_idx.insert("outgoing", Value::bytes(outgoing)); network_idx.insert("outgoing", Value::bytes(outgoing));
idx.insert_spanned("network", network_idx); idx.insert_spanned("network", network_idx);

View File

@ -42,7 +42,7 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value {
pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let name_span = args.name_span; let name_span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map( .map(

View File

@ -32,7 +32,7 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let name_span = args.name_span; let name_span = args.call_info.name_span;
Ok(out Ok(out
.values .values

View File

@ -40,7 +40,7 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value {
pub fn to_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let name_span = args.name_span; let name_span = args.call_info.name_span;
Ok(out Ok(out
.values .values
.map( .map(

View File

@ -2,8 +2,6 @@ use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
// TODO: "Amount remaining" wrapper
pub fn trim(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn trim(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;

View File

@ -1,18 +1,45 @@
use crate::commands::command::{Sink, SinkCommandArgs}; use crate::commands::command::{CallInfo, Sink, SinkCommandArgs};
use crate::parser::{ use crate::parser::{
registry::{Args, CommandConfig, CommandRegistry}, registry::{Args, CommandConfig, CommandRegistry},
Span, Span,
}; };
use crate::prelude::*; use crate::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::sync::Arc; 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<Uuid, SpanSource>);
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)] #[derive(Clone)]
pub struct Context { pub struct Context {
commands: IndexMap<String, Arc<dyn Command>>, commands: IndexMap<String, Arc<dyn Command>>,
sinks: IndexMap<String, Arc<dyn Sink>>, sinks: IndexMap<String, Arc<dyn Sink>>,
crate source_map: SourceMap,
crate host: Arc<Mutex<dyn Host + Send>>, crate host: Arc<Mutex<dyn Host + Send>>,
crate env: Arc<Mutex<Environment>>, crate env: Arc<Mutex<Environment>>,
} }
@ -22,6 +49,7 @@ impl Context {
Ok(Context { Ok(Context {
commands: indexmap::IndexMap::new(), commands: indexmap::IndexMap::new(),
sinks: indexmap::IndexMap::new(), sinks: indexmap::IndexMap::new(),
source_map: SourceMap::new(),
host: Arc::new(Mutex::new(crate::env::host::BasicHost)), host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
env: Arc::new(Mutex::new(Environment::basic()?)), 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 { crate fn has_sink(&self, name: &str) -> bool {
self.sinks.contains_key(name) self.sinks.contains_key(name)
} }
@ -56,8 +88,11 @@ impl Context {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let command_args = SinkCommandArgs { let command_args = SinkCommandArgs {
ctx: self.clone(), ctx: self.clone(),
name_span, call_info: CallInfo {
args, name_span,
source_map: self.source_map.clone(),
args,
},
input, input,
}; };
@ -80,14 +115,18 @@ impl Context {
&mut self, &mut self,
command: Arc<dyn Command>, command: Arc<dyn Command>,
name_span: Option<Span>, name_span: Option<Span>,
source_map: SourceMap,
args: Args, args: Args,
input: InputStream, input: InputStream,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let command_args = CommandArgs { let command_args = CommandArgs {
host: self.host.clone(), host: self.host.clone(),
env: self.env.clone(), env: self.env.clone(),
name_span, call_info: CallInfo {
args, name_span,
source_map,
args,
},
input, input,
}; };

View File

@ -17,7 +17,12 @@ impl Description {
pub fn from(item: Spanned<impl Into<String>>) -> Description { pub fn from(item: Spanned<impl Into<String>>) -> Description {
match item { match item {
Spanned { Spanned {
span: Span { start: 0, end: 0 }, span:
Span {
start: 0,
end: 0,
source: None,
},
item, item,
} => Description::Synthetic(item.into()), } => Description::Synthetic(item.into()),
Spanned { span, item } => Description::Source(Spanned::from_item(item.into(), span)), Spanned { span, item } => Description::Source(Spanned::from_item(item.into(), span)),

View File

@ -5,6 +5,7 @@
#![feature(bind_by_move_pattern_guards)] #![feature(bind_by_move_pattern_guards)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(type_ascription)] #![feature(type_ascription)]
#![feature(option_flattening)]
#[macro_use] #[macro_use]
mod prelude; mod prelude;
@ -23,7 +24,8 @@ mod plugin;
mod shell; mod shell;
mod stream; 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::env::host::BasicHost;
pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::parse::span::SpannedItem;
pub use crate::parser::Spanned; pub use crate::parser::Spanned;

View File

@ -9,8 +9,8 @@ use log::LevelFilter;
use std::error::Error; use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let matches = App::new("nu shell") let matches = App::new("nushell")
.version("0.5") .version("0.1.3")
.arg( .arg(
Arg::with_name("loglevel") Arg::with_name("loglevel")
.short("l") .short("l")

View File

@ -3,6 +3,7 @@ use derive_new::new;
use getset::Getters; use getset::Getters;
use serde::Serialize; use serde::Serialize;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use uuid::Uuid;
#[derive( #[derive(
new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
@ -75,13 +76,17 @@ impl<T> Spanned<T> {
pub struct Span { pub struct Span {
crate start: usize, crate start: usize,
crate end: usize, crate end: usize,
// source: &'source str, pub source: Option<Uuid>,
} }
impl From<Option<Span>> for Span { impl From<Option<Span>> for Span {
fn from(input: Option<Span>) -> Span { fn from(input: Option<Span>) -> Span {
match input { match input {
None => Span { start: 0, end: 0 }, None => Span {
start: 0,
end: 0,
source: None,
},
Some(span) => span, Some(span) => span,
} }
} }
@ -104,6 +109,7 @@ impl From<nom5_locate::LocatedSpan<&str>> for Span {
Span { Span {
start: input.offset, start: input.offset,
end: input.offset + input.fragment.len(), end: input.offset + input.fragment.len(),
source: None,
} }
} }
} }
@ -113,6 +119,7 @@ impl<T> From<(nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)> for Spa
Span { Span {
start: input.0.offset, start: input.0.offset,
end: input.1.offset, end: input.1.offset,
source: None,
} }
} }
} }
@ -122,6 +129,7 @@ impl From<(usize, usize)> for Span {
Span { Span {
start: input.0, start: input.0,
end: input.1, end: input.1,
source: None,
} }
} }
} }
@ -131,13 +139,26 @@ impl From<&std::ops::Range<usize>> for Span {
Span { Span {
start: input.start, start: input.start,
end: input.end, end: input.end,
source: None,
} }
} }
} }
impl Span { impl Span {
pub fn unknown() -> 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 { pub fn is_unknown(&self) -> bool {
@ -154,6 +175,7 @@ impl language_reporting::ReportingSpan for Span {
Span { Span {
start, start,
end: self.end, end: self.end,
source: None,
} }
} }
@ -161,6 +183,7 @@ impl language_reporting::ReportingSpan for Span {
Span { Span {
start: self.start, start: self.start,
end, end,
source: None,
} }
} }

View File

@ -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 serde::{Deserialize, Serialize};
use std::io; use std::io;
pub trait Plugin { pub trait Plugin {
fn config(&mut self) -> Result<CommandConfig, ShellError>; fn config(&mut self) -> Result<CommandConfig, ShellError>;
#[allow(unused)] #[allow(unused)]
fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
Err(ShellError::string( Err(ShellError::string(
"`begin_filter` not implemented in plugin", "`begin_filter` not implemented in plugin",
)) ))
@ -15,7 +15,7 @@ pub trait Plugin {
Err(ShellError::string("`filter` not implemented in plugin")) Err(ShellError::string("`filter` not implemented in plugin"))
} }
#[allow(unused)] #[allow(unused)]
fn sink(&mut self, args: Args, input: Vec<Spanned<Value>>) {} fn sink(&mut self, call_info: CallInfo, input: Vec<Spanned<Value>>) {}
fn quit(&mut self) { fn quit(&mut self) {
return; return;
@ -134,8 +134,14 @@ fn send_response<T: Serialize>(result: T) {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum NuCommand { pub enum NuCommand {
config, config,
begin_filter { params: Args }, begin_filter {
filter { params: Spanned<Value> }, params: CallInfo,
sink { params: (Args, Vec<Spanned<Value>>) }, },
filter {
params: Spanned<Value>,
},
sink {
params: (CallInfo, Vec<Spanned<Value>>),
},
quit, quit,
} }

View File

@ -1,6 +1,10 @@
#![feature(option_flattening)]
use crossterm::{cursor, terminal, Attribute, RawScreen}; use crossterm::{cursor, terminal, Attribute, RawScreen};
use indexmap::IndexMap; 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::*; use pretty_hex::*;
struct BinaryView; struct BinaryView;
@ -25,14 +29,15 @@ impl Plugin for BinaryView {
}) })
} }
fn sink(&mut self, args: Args, input: Vec<Spanned<Value>>) { fn sink(&mut self, call_info: CallInfo, input: Vec<Spanned<Value>>) {
for v in input { for v in input {
match v { match v {
Spanned { Spanned {
item: Value::Binary(b), 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<dyn std::error::Error>> { fn view_binary(
b: &[u8],
source: Option<&SpanSource>,
lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
if b.len() > 3 { if b.len() > 3 {
match (b[0], b[1], b[2]) { match (b[0], b[1], b[2]) {
(0x4e, 0x45, 0x53) => { (0x4e, 0x45, 0x53) => {
view_contents_interactive(b, lores_mode)?; view_contents_interactive(b, source, lores_mode)?;
return Ok(()); return Ok(());
} }
_ => {} _ => {}
} }
} }
view_contents(b, lores_mode)?; view_contents(b, source, lores_mode)?;
Ok(()) 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<dyn std::error::Error>> { pub fn view_contents(
buffer: &[u8],
_source: Option<&SpanSource>,
lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let mut raw_image_buffer = load_from_png_buffer(buffer); let mut raw_image_buffer = load_from_png_buffer(buffer);
if raw_image_buffer.is_none() { if raw_image_buffer.is_none() {
@ -332,14 +345,29 @@ pub fn view_contents(buffer: &[u8], lores_mode: bool) -> Result<(), Box<dyn std:
pub fn view_contents_interactive( pub fn view_contents_interactive(
buffer: &[u8], buffer: &[u8],
source: Option<&SpanSource>,
lores_mode: bool, lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
use rawkey::{KeyCode, RawKey}; 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 mut nes = neso::Nes::new(48000.0);
let rawkey = RawKey::new(); let rawkey = RawKey::new();
nes.load_rom(&buffer); 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(); nes.reset();
if let Ok(_raw) = RawScreen::into_raw_mode() { 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 = cursor();
let _ = cursor.show(); let _ = cursor.show();

View File

@ -1,7 +1,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, Args, CommandConfig, NamedType, Plugin, PositionalType, Primitive, ReturnSuccess, serve_plugin, CallInfo, CommandConfig, NamedType, Plugin, PositionalType, Primitive,
ReturnValue, ShellError, Spanned, SpannedItem, Value, ReturnSuccess, ReturnValue, ShellError, Spanned, SpannedItem, Value,
}; };
struct Inc { struct Inc {
@ -99,18 +99,18 @@ impl Plugin for Inc {
rest_positional: true, rest_positional: true,
}) })
} }
fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
if args.has("major") { if call_info.args.has("major") {
self.major = true; self.major = true;
} }
if args.has("minor") { if call_info.args.has("minor") {
self.minor = true; self.minor = true;
} }
if args.has("patch") { if call_info.args.has("patch") {
self.patch = true; self.patch = true;
} }
if let Some(args) = args.positional { if let Some(args) = call_info.args.positional {
for arg in args { for arg in args {
match arg { match arg {
Spanned { Spanned {

View File

@ -1,7 +1,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, Args, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, serve_plugin, CallInfo, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue,
Spanned, Value, ShellError, Spanned, Value,
}; };
struct NewSkip { struct NewSkip {
@ -24,8 +24,8 @@ impl Plugin for NewSkip {
rest_positional: true, rest_positional: true,
}) })
} }
fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
if let Some(args) = args.positional { if let Some(args) = call_info.args.positional {
for arg in args { for arg in args {
match arg { match arg {
Spanned { Spanned {
@ -34,7 +34,13 @@ impl Plugin for NewSkip {
} => { } => {
self.skip_amount = i; 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,
))
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
use derive_new::new; use derive_new::new;
use indexmap::IndexMap; 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::item::StringItem;
use ptree::output::print_tree_with; use ptree::output::print_tree_with;
use ptree::print_config::PrintConfig; use ptree::print_config::PrintConfig;
@ -91,7 +91,7 @@ impl Plugin for TreeViewer {
}) })
} }
fn sink(&mut self, _args: Args, input: Vec<Spanned<Value>>) { fn sink(&mut self, _call_info: CallInfo, input: Vec<Spanned<Value>>) {
if input.len() > 0 { if input.len() > 0 {
for i in input.iter() { for i in input.iter() {
let view = TreeView::from_value(&i); let view = TreeView::from_value(&i);

View File

@ -1,7 +1,7 @@
mod helpers; mod helpers;
use helpers as h;
use h::in_directory as cwd; use h::in_directory as cwd;
use helpers as h;
#[test] #[test]
fn lines() { fn lines() {
@ -14,18 +14,22 @@ fn lines() {
#[test] #[test]
fn open_csv() { fn open_csv() {
nu!(output, nu!(
output,
cwd("tests/fixtures/formats"), 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"); assert_eq!(output, "SPAIN");
} }
#[test] #[test]
fn open_toml() { fn open_toml() {
nu!(output, nu!(
cwd("tests/fixtures/formats"), output,
"open cargo_sample.toml | get package.edition | echo $it"); cwd("tests/fixtures/formats"),
"open cargo_sample.toml | get package.edition | echo $it"
);
assert_eq!(output, "2018"); assert_eq!(output, "2018");
} }
@ -41,32 +45,40 @@ fn open_json() {
#[test] #[test]
fn open_xml() { fn open_xml() {
nu!(output, nu!(
output,
cwd("tests/fixtures/formats"), 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] #[test]
fn open_ini() { fn open_ini() {
nu!(output, nu!(
output,
cwd("tests/fixtures/formats"), cwd("tests/fixtures/formats"),
"open sample.ini | get SectionOne.integer | echo $it"); "open sample.ini | get SectionOne.integer | echo $it"
);
assert_eq!(output, "1234") assert_eq!(output, "1234")
} }
#[test] #[test]
fn open_error_if_file_not_found() { fn open_error_if_file_not_found() {
nu_error!(output, nu_error!(
output,
cwd("tests/fixtures/formats"), 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")); assert!(output.contains("File cound not be opened"));
} }
#[test] #[test]
fn rm() { fn rm() {
let directory = "tests/fixtures/nuplayground"; let directory = "tests/fixtures/nuplayground";
@ -74,9 +86,7 @@ fn rm() {
h::create_file_at(&file); h::create_file_at(&file);
nu!(_output, nu!(_output, cwd(directory), "rm rm_test.txt");
cwd(directory),
"rm rm_test.txt");
assert!(!h::file_exists_at(&file)); assert!(!h::file_exists_at(&file));
} }
@ -85,30 +95,34 @@ fn rm() {
fn can_remove_directory_contents_with_recursive_flag() { fn can_remove_directory_contents_with_recursive_flag() {
let path = "tests/fixtures/nuplayground/rm_test"; 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); h::create_directory_at(path);
for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() { for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() {
h::create_file_at(&format!("{}/{}", path, f)); h::create_file_at(&format!("{}/{}", path, f));
}; }
nu!(_output, nu!(
_output,
cwd("tests/fixtures/nuplayground"), cwd("tests/fixtures/nuplayground"),
"rm rm_test --recursive"); "rm rm_test --recursive"
);
assert!(!h::file_exists_at(&path)); assert!(!h::file_exists_at(&path));
} }
#[test] #[test]
fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() { 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); h::create_directory_at(path);
nu_error!(output, nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm rm_test_2");
cwd("tests/fixtures/nuplayground"),
"rm rm_test");
assert!(h::file_exists_at(&path)); assert!(h::file_exists_at(&path));
assert!(output.contains("is a directory")); assert!(output.contains("is a directory"));
@ -117,18 +131,14 @@ fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() {
#[test] #[test]
fn rm_error_if_attempting_to_delete_single_dot_as_argument() { fn rm_error_if_attempting_to_delete_single_dot_as_argument() {
nu_error!(output, nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm .");
cwd("tests/fixtures/nuplayground"),
"rm .");
assert!(output.contains("may not be removed")); assert!(output.contains("may not be removed"));
} }
#[test] #[test]
fn rm_error_if_attempting_to_delete_two_dot_as_argument() { fn rm_error_if_attempting_to_delete_two_dot_as_argument() {
nu_error!(output, nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm ..");
cwd("tests/fixtures/nuplayground"),
"rm ..");
assert!(output.contains("may not be removed")); assert!(output.contains("may not be removed"));
} }