Merge pull request #479 from nushell/expand-tilde

Add support for ~ expansion
This commit is contained in:
Jonathan Turner 2019-08-27 16:48:56 +12:00 committed by GitHub
commit 1d64d90419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 422 additions and 213 deletions

7
Cargo.lock generated
View File

@ -1733,6 +1733,7 @@ dependencies = [
"serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"shellexpand 1.0.0 (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)",
"surf 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "surf 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2646,6 +2647,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "shellexpand"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
@ -3568,6 +3574,7 @@ dependencies = [
"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582" "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
"checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a" "checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a"
"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" "checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c"
"checksum shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de7a5b5a9142fd278a10e0209b021a1b85849352e6951f4f914735c976737564"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec70d7c3b17c262d4a18f7291c6ce62bf47170915f3b795434d3c5c49a4e59b7" "checksum sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec70d7c3b17c262d4a18f7291c6ce62bf47170915f3b795434d3c5c49a4e59b7"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"

View File

@ -78,6 +78,7 @@ battery = "0.7.4"
textwrap = {version = "0.11.0", features = ["term_size"]} textwrap = {version = "0.11.0", features = ["term_size"]}
rawkey = {version = "0.1.2", optional = true } rawkey = {version = "0.1.2", optional = true }
clipboard = {version = "0.5", optional = true } clipboard = {version = "0.5", optional = true }
shellexpand = "1.0.0"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6.1" pretty_assertions = "0.6.1"

View File

@ -58,12 +58,17 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel
let mut input = String::new(); let mut input = String::new();
match reader.read_line(&mut input) { match reader.read_line(&mut input) {
Ok(_) => { Ok(count) => {
trace!("processing response ({} bytes)", count);
let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input); let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input);
match response { match response {
Ok(jrpc) => match jrpc.params { Ok(jrpc) => match jrpc.params {
Ok(params) => { Ok(params) => {
let fname = path.to_string_lossy(); let fname = path.to_string_lossy();
trace!("processing {:?}", params);
if params.is_filter { if params.is_filter {
let fname = fname.to_string(); let fname = fname.to_string();
let name = params.name.clone(); let name = params.name.clone();
@ -93,14 +98,18 @@ fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Resu
let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?; let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?;
let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?; let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?;
trace!("Looking for plugins in {:?}", path);
match std::fs::read_dir(path) { match std::fs::read_dir(path) {
Ok(p) => { Ok(p) => {
for entry in p { for entry in p {
let entry = entry?; let entry = entry?;
let filename = entry.file_name(); let filename = entry.file_name();
let f_name = filename.to_string_lossy(); let f_name = filename.to_string_lossy();
if re_bin.is_match(&f_name) || re_exe.is_match(&f_name) { if re_bin.is_match(&f_name) || re_exe.is_match(&f_name) {
let mut load_path = path.clone(); let mut load_path = path.clone();
trace!("Found {:?}", f_name);
load_path.push(f_name.to_string()); load_path.push(f_name.to_string());
load_plugin(&load_path, context)?; load_plugin(&load_path, context)?;
} }
@ -513,7 +522,9 @@ fn classify_command(
trace!(target: "nu::build_pipeline", "classifying {:?}", config); trace!(target: "nu::build_pipeline", "classifying {:?}", config);
let args: hir::Call = config.parse_args(call, context.registry(), source)?; let args: hir::Call = config.parse_args(call, &context, source)?;
trace!(target: "nu::build_pipeline", "args :: {}", args.debug(source));
Ok(ClassifiedCommand::Internal(InternalCommand { Ok(ClassifiedCommand::Internal(InternalCommand {
command, command,

View File

@ -122,10 +122,11 @@ impl InternalCommand {
self.name_span.clone(), self.name_span.clone(),
context.source_map.clone(), context.source_map.clone(),
self.args, self.args,
source, &source,
objects, objects,
); );
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result);
let mut result = result.values; let mut result = result.values;
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();

View File

@ -422,6 +422,25 @@ pub enum CommandAction {
LeaveShell, LeaveShell,
} }
impl ToDebug for CommandAction {
fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result {
match self {
CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s),
CommandAction::AddSpanSource(u, source) => {
write!(f, "action:add-span-source={}@{:?}", u, source)
}
CommandAction::Exit => write!(f, "action:exit"),
CommandAction::EnterShell(s) => write!(f, "action:enter-shell={}", s),
CommandAction::EnterValueShell(t) => {
write!(f, "action:enter-value-shell={:?}", t.debug())
}
CommandAction::PreviousShell => write!(f, "action:previous-shell"),
CommandAction::NextShell => write!(f, "action:next-shell"),
CommandAction::LeaveShell => write!(f, "action:leave-shell"),
}
}
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum ReturnSuccess { pub enum ReturnSuccess {
Value(Tagged<Value>), Value(Tagged<Value>),
@ -430,6 +449,16 @@ pub enum ReturnSuccess {
pub type ReturnValue = Result<ReturnSuccess, ShellError>; pub type ReturnValue = Result<ReturnSuccess, ShellError>;
impl ToDebug for ReturnValue {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self {
Err(err) => write!(f, "{}", err.debug(source)),
Ok(ReturnSuccess::Value(v)) => write!(f, "{:?}", v.debug()),
Ok(ReturnSuccess::Action(a)) => write!(f, "{}", a.debug(source)),
}
}
}
impl From<Tagged<Value>> for ReturnValue { impl From<Tagged<Value>> for ReturnValue {
fn from(input: Tagged<Value>) -> ReturnValue { fn from(input: Tagged<Value>) -> ReturnValue {
Ok(ReturnSuccess::Value(input)) Ok(ReturnSuccess::Value(input))
@ -469,7 +498,7 @@ pub trait WholeStreamCommand: Send + Sync {
Signature { Signature {
name: self.name().to_string(), name: self.name().to_string(),
positional: vec![], positional: vec![],
rest_positional: true, rest_positional: None,
named: indexmap::IndexMap::new(), named: indexmap::IndexMap::new(),
is_filter: true, is_filter: true,
} }
@ -491,7 +520,7 @@ pub trait PerItemCommand: Send + Sync {
Signature { Signature {
name: self.name().to_string(), name: self.name().to_string(),
positional: vec![], positional: vec![],
rest_positional: true, rest_positional: None,
named: indexmap::IndexMap::new(), named: indexmap::IndexMap::new(),
is_filter: true, is_filter: true,
} }

View File

@ -22,18 +22,21 @@ impl WholeStreamCommand for Get {
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
args.process(registry, get)?.run() args.process(registry, get)?.run()
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("get").rest() Signature::build("get").rest(SyntaxType::Member)
} }
} }
fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value>, ShellError> { fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
let mut current = obj; let mut current = Some(obj);
for p in path.split(".") { for p in path.split(".") {
match current.get_data_by_key(p) { if let Some(obj) = current {
Some(v) => current = v, current = match obj.get_data_by_key(p) {
None => { Some(v) => Some(v),
None =>
// Before we give up, see if they gave us a path that matches a field name by itself // Before we give up, see if they gave us a path that matches a field name by itself
{
match obj.get_data_by_key(&path.item) { match obj.get_data_by_key(&path.item) {
Some(v) => return Ok(v.clone()), Some(v) => return Ok(v.clone()),
None => { None => {
@ -47,8 +50,13 @@ fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value
} }
} }
} }
}
Ok(current.clone()) match current {
Some(v) => Ok(v.clone()),
None => Ok(Value::nothing().tagged(obj.tag)),
}
// Ok(current.clone())
} }
pub fn get( pub fn get(

View File

@ -27,7 +27,7 @@ impl PerItemCommand for Mkdir {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("mkdir").rest() Signature::build("mkdir").rest(SyntaxType::Path)
} }
} }

View File

@ -44,7 +44,8 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
{ {
file => file, file => file,
}; };
let path_str = path.as_string()?; let path_buf = path.as_path()?;
let path_str = path_buf.display().to_string();
let path_span = path.span(); let path_span = path.span();
let name_span = call_info.name_span; let name_span = call_info.name_span;
let has_raw = call_info.args.has("raw"); let has_raw = call_info.args.has("raw");

View File

@ -17,7 +17,7 @@ impl WholeStreamCommand for Pick {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("pick").rest() Signature::build("pick").rest(SyntaxType::Any)
} }
fn run( fn run(

View File

@ -3,6 +3,7 @@ use crate::errors::ShellError;
use crate::parser::registry; use crate::parser::registry;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
use log::trace;
use serde::{self, Deserialize, Serialize}; use serde::{self, Deserialize, Serialize};
use std::io::prelude::*; use std::io::prelude::*;
use std::io::BufReader; use std::io::BufReader;
@ -64,6 +65,8 @@ pub fn filter_plugin(
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
trace!("filter_plugin :: {}", path);
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let mut child = std::process::Command::new(path) let mut child = std::process::Command::new(path)
@ -80,6 +83,8 @@ pub fn filter_plugin(
let call_info = args.call_info.clone(); let call_info = args.call_info.clone();
trace!("filtering :: {:?}", call_info);
let stream = bos let stream = bos
.chain(args.input.values) .chain(args.input.values)
.chain(eos) .chain(eos)
@ -95,7 +100,14 @@ pub fn filter_plugin(
let request = JsonRpc::new("begin_filter", call_info.clone()); let request = JsonRpc::new("begin_filter", call_info.clone());
let request_raw = serde_json::to_string(&request).unwrap(); let request_raw = serde_json::to_string(&request).unwrap();
let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error match stdin.write(format!("{}\n", request_raw).as_bytes()) {
Ok(_) => {}
Err(err) => {
let mut result = VecDeque::new();
result.push_back(Err(ShellError::unexpected(format!("{}", err))));
return result;
}
}
let mut input = String::new(); let mut input = String::new();
match reader.read_line(&mut input) { match reader.read_line(&mut input) {
@ -140,7 +152,15 @@ pub fn filter_plugin(
let mut reader = BufReader::new(stdout); let mut reader = BufReader::new(stdout);
let request: JsonRpc<std::vec::Vec<Value>> = JsonRpc::new("end_filter", vec![]); let request: JsonRpc<std::vec::Vec<Value>> = JsonRpc::new("end_filter", vec![]);
let request_raw = serde_json::to_string(&request).unwrap(); let request_raw = match serde_json::to_string(&request) {
Ok(req) => req,
Err(err) => {
let mut result = VecDeque::new();
result.push_back(Err(ShellError::unexpected(format!("{}", err))));
return result;
}
};
let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error
let mut input = String::new(); let mut input = String::new();

View File

@ -24,7 +24,7 @@ impl WholeStreamCommand for Reject {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("reject").rest() Signature::build("reject").rest(SyntaxType::Member)
} }
} }

View File

@ -4,13 +4,19 @@ use crate::prelude::*;
pub struct SortBy; pub struct SortBy;
#[derive(Deserialize)]
pub struct SortByArgs {
rest: Vec<Tagged<String>>,
reverse: bool,
}
impl WholeStreamCommand for SortBy { impl WholeStreamCommand for SortBy {
fn run( fn run(
&self, &self,
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
sort_by(args, registry) args.process(registry, sort_by)?.run()
} }
fn name(&self) -> &str { fn name(&self) -> &str {
@ -18,30 +24,21 @@ impl WholeStreamCommand for SortBy {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("sort-by").switch("reverse") Signature::build("sort-by")
.rest(SyntaxType::String)
.switch("reverse")
} }
} }
fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { fn sort_by(
let args = args.evaluate_once(registry)?; SortByArgs { reverse, rest }: SortByArgs,
let (input, args) = args.parts(); mut context: RunnableContext,
) -> Result<OutputStream, ShellError> {
Ok(OutputStream::new(async_stream_block! {
let mut vec = context.input.drain_vec().await;
let fields: Result<Vec<_>, _> = args
.positional
.iter()
.flatten()
.map(|a| a.as_string())
.collect();
let fields = fields?;
let output = input.values.collect::<Vec<_>>();
let reverse = args.has("reverse");
let output = output.map(move |mut vec| {
let calc_key = |item: &Tagged<Value>| { let calc_key = |item: &Tagged<Value>| {
fields rest.iter()
.iter()
.map(|f| item.get_data_by_key(f).map(|i| i.clone())) .map(|f| item.get_data_by_key(f).map(|i| i.clone()))
.collect::<Vec<Option<Tagged<Value>>>>() .collect::<Vec<Option<Tagged<Value>>>>()
}; };
@ -49,10 +46,10 @@ fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
vec.sort_by_cached_key(|item| std::cmp::Reverse(calc_key(item))); vec.sort_by_cached_key(|item| std::cmp::Reverse(calc_key(item)));
} else { } else {
vec.sort_by_cached_key(calc_key); vec.sort_by_cached_key(calc_key);
};
for item in vec {
yield item.into();
} }
}))
vec.into_iter().collect::<VecDeque<_>>()
});
Ok(output.flatten_stream().from_input_stream())
} }

View File

@ -28,7 +28,7 @@ impl WholeStreamCommand for SplitColumn {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("split-column") Signature::build("split-column")
.required("separator", SyntaxType::Any) .required("separator", SyntaxType::Any)
.rest() .rest(SyntaxType::Member)
} }
} }

View File

@ -121,7 +121,7 @@ impl Context {
name_span: Span, name_span: Span,
source_map: SourceMap, source_map: SourceMap,
args: hir::Call, args: hir::Call,
source: Text, source: &Text,
input: InputStream, input: InputStream,
) -> OutputStream { ) -> OutputStream {
let command_args = self.command_args(args, input, source, source_map, name_span); let command_args = self.command_args(args, input, source, source_map, name_span);
@ -131,13 +131,13 @@ impl Context {
fn call_info( fn call_info(
&self, &self,
args: hir::Call, args: hir::Call,
source: Text, source: &Text,
source_map: SourceMap, source_map: SourceMap,
name_span: Span, name_span: Span,
) -> UnevaluatedCallInfo { ) -> UnevaluatedCallInfo {
UnevaluatedCallInfo { UnevaluatedCallInfo {
args, args,
source, source: source.clone(),
source_map, source_map,
name_span, name_span,
} }
@ -147,7 +147,7 @@ impl Context {
&self, &self,
args: hir::Call, args: hir::Call,
input: InputStream, input: InputStream,
source: Text, source: &Text,
source_map: SourceMap, source_map: SourceMap,
name_span: Span, name_span: Span,
) -> CommandArgs { ) -> CommandArgs {

View File

@ -5,6 +5,7 @@ use ansi_term::Color;
use derive_new::new; use derive_new::new;
use language_reporting::{Diagnostic, Label, Severity}; use language_reporting::{Diagnostic, Label, Severity};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Description { pub enum Description {
@ -56,6 +57,12 @@ pub struct ShellError {
cause: Option<Box<ProximateShellError>>, cause: Option<Box<ProximateShellError>>,
} }
impl ToDebug for ShellError {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
self.error.fmt_debug(f, source)
}
}
impl serde::de::Error for ShellError { impl serde::de::Error for ShellError {
fn custom<T>(msg: T) -> Self fn custom<T>(msg: T) -> Self
where where
@ -335,6 +342,7 @@ pub enum ProximateShellError {
right: Tagged<String>, right: Tagged<String>,
}, },
} }
impl ProximateShellError { impl ProximateShellError {
fn start(self) -> ShellError { fn start(self) -> ShellError {
ShellError { ShellError {
@ -344,6 +352,13 @@ impl ProximateShellError {
} }
} }
impl ToDebug for ProximateShellError {
fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result {
// TODO: Custom debug for inner spans
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShellDiagnostic { pub struct ShellDiagnostic {
crate diagnostic: Diagnostic<Span>, crate diagnostic: Diagnostic<Span>,

View File

@ -39,6 +39,7 @@ crate fn evaluate_baseline_expr(
) -> Result<Tagged<Value>, ShellError> { ) -> Result<Tagged<Value>, ShellError> {
match &expr.item { match &expr.item {
RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)), RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)),
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())),
RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()), RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()),
RawExpression::Variable(var) => evaluate_reference(var, scope, source), RawExpression::Variable(var) => evaluate_reference(var, scope, source),
RawExpression::ExternalCommand(external) => evaluate_external(external, scope, source), RawExpression::ExternalCommand(external) => evaluate_external(external, scope, source),

View File

@ -79,7 +79,7 @@ impl TableView {
let mut current_col_max = 0; let mut current_col_max = 0;
for row in 0..values.len() { for row in 0..values.len() {
let value_length = entries[row][head].0.len(); let value_length = entries[row][head].0.len();
if value_length > current_col_max { if head > entries[row].len() && value_length > current_col_max {
current_col_max = value_length; current_col_max = value_length;
} }
} }

View File

@ -29,6 +29,7 @@ pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
pub use crate::context::{SourceMap, SpanSource}; pub use crate::context::{SourceMap, SpanSource};
pub use crate::env::host::BasicHost; pub use crate::env::host::BasicHost;
pub use crate::object::base::OF64; pub use crate::object::base::OF64;
pub use crate::parser::hir::SyntaxType;
pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsolutePath, RelativePath}; pub use crate::utils::{AbsolutePath, RelativePath};
pub use cli::cli; pub use cli::cli;

View File

@ -335,6 +335,7 @@ impl Value {
} }
} }
// TODO: This is basically a legacy construct, I think
pub fn data_descriptors(&self) -> Vec<String> { pub fn data_descriptors(&self) -> Vec<String> {
match self { match self {
Value::Primitive(_) => vec![], Value::Primitive(_) => vec![],
@ -599,6 +600,10 @@ impl Value {
Value::Primitive(Primitive::String(s.into())) Value::Primitive(Primitive::String(s.into()))
} }
pub fn path(s: impl Into<PathBuf>) -> Value {
Value::Primitive(Primitive::Path(s.into()))
}
pub fn bytes(s: impl Into<u64>) -> Value { pub fn bytes(s: impl Into<u64>) -> Value {
Value::Primitive(Primitive::Bytes(s.into())) Value::Primitive(Primitive::Bytes(s.into()))
} }
@ -634,6 +639,18 @@ impl Value {
} }
} }
impl Tagged<Value> {
crate fn as_path(&self) -> Result<PathBuf, ShellError> {
match self.item() {
Value::Primitive(Primitive::Path(path)) => Ok(path.clone()),
other => Err(ShellError::type_error(
"Path",
other.type_name().tagged(self.span()),
)),
}
}
}
crate fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> { crate fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> {
let mut out = TaggedDictBuilder::new(tag); let mut out = TaggedDictBuilder::new(tag);

View File

@ -2,7 +2,6 @@ use crate::object::base as value;
use crate::parser::hir; use crate::parser::hir;
use crate::prelude::*; use crate::prelude::*;
use log::trace; use log::trace;
use std::path::PathBuf;
pub trait ExtractType: Sized { pub trait ExtractType: Sized {
fn extract(value: &Tagged<Value>) -> Result<Self, ShellError>; fn extract(value: &Tagged<Value>) -> Result<Self, ShellError>;
@ -196,9 +195,9 @@ impl ExtractType for std::path::PathBuf {
match &value { match &value {
Tagged { Tagged {
item: Value::Primitive(Primitive::String(p)), item: Value::Primitive(Primitive::Path(p)),
.. ..
} => Ok(PathBuf::from(p)), } => Ok(p.clone()),
other => Err(ShellError::type_error("Path", other.tagged_type_name())), other => Err(ShellError::type_error("Path", other.tagged_type_name())),
} }
} }

View File

@ -11,16 +11,22 @@ use derive_new::new;
use getset::Getters; use getset::Getters;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::path::PathBuf;
use crate::evaluate::Scope; use crate::evaluate::Scope;
crate use self::baseline_parse::{baseline_parse_single_token, baseline_parse_token_as_string}; crate use self::baseline_parse::{
crate use self::baseline_parse_tokens::{baseline_parse_next_expr, SyntaxType, TokensIterator}; baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path,
baseline_parse_token_as_string,
};
crate use self::baseline_parse_tokens::{baseline_parse_next_expr, TokensIterator};
crate use self::binary::Binary; crate use self::binary::Binary;
crate use self::external_command::ExternalCommand; crate use self::external_command::ExternalCommand;
crate use self::named::NamedArguments; crate use self::named::NamedArguments;
crate use self::path::Path; crate use self::path::Path;
pub use self::baseline_parse_tokens::SyntaxType;
pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path { pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path {
Path::new( Path::new(
head.into(), head.into(),
@ -68,6 +74,8 @@ impl ToDebug for Call {
write!(f, "{}", named.debug(source))?; write!(f, "{}", named.debug(source))?;
} }
write!(f, ")")?;
Ok(()) Ok(())
} }
} }
@ -81,6 +89,7 @@ pub enum RawExpression {
Block(Vec<Expression>), Block(Vec<Expression>),
List(Vec<Expression>), List(Vec<Expression>),
Path(Box<Path>), Path(Box<Path>),
FilePath(PathBuf),
ExternalCommand(ExternalCommand), ExternalCommand(ExternalCommand),
#[allow(unused)] #[allow(unused)]
@ -105,6 +114,7 @@ impl RawExpression {
match self { match self {
RawExpression::Literal(literal) => literal.type_name(), RawExpression::Literal(literal) => literal.type_name(),
RawExpression::Synthetic(synthetic) => synthetic.type_name(), RawExpression::Synthetic(synthetic) => synthetic.type_name(),
RawExpression::FilePath(..) => "filepath",
RawExpression::Variable(..) => "variable", RawExpression::Variable(..) => "variable",
RawExpression::List(..) => "list", RawExpression::List(..) => "list",
RawExpression::Binary(..) => "binary", RawExpression::Binary(..) => "binary",
@ -141,6 +151,10 @@ impl Expression {
) )
} }
crate fn file_path(path: impl Into<PathBuf>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into())
}
crate fn bare(span: impl Into<Span>) -> Expression { crate fn bare(span: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into())
} }
@ -170,7 +184,8 @@ impl Expression {
impl ToDebug for Expression { impl ToDebug for Expression {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self.item() { match self.item() {
RawExpression::Literal(l) => write!(f, "{:?}", l), RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source),
RawExpression::FilePath(p) => write!(f, "{}", p.display()),
RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s), RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s),
RawExpression::Variable(Variable::It(_)) => write!(f, "$it"), RawExpression::Variable(Variable::It(_)) => write!(f, "$it"),
RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)), RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)),

View File

@ -1,5 +1,7 @@
use crate::context::Context;
use crate::parser::{hir, RawToken, Token}; use crate::parser::{hir, RawToken, Token};
use crate::Text; use crate::Text;
use std::path::PathBuf;
pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression { pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression {
match *token.item() { match *token.item() {
@ -15,6 +17,20 @@ pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Express
} }
} }
pub fn baseline_parse_token_as_number(token: &Token, source: &Text) -> hir::Expression {
match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span())
}
RawToken::External(span) => hir::Expression::external_command(span, token.span()),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
RawToken::Integer(int) => hir::Expression::int(int, token.span()),
RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()),
RawToken::Bare => hir::Expression::bare(token.span()),
RawToken::String(span) => hir::Expression::string(span, token.span()),
}
}
pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expression { pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expression {
match *token.item() { match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(span) if span.slice(source) == "it" => {
@ -28,3 +44,32 @@ pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expr
RawToken::String(span) => hir::Expression::string(span, token.span()), RawToken::String(span) => hir::Expression::string(span, token.span()),
} }
} }
pub fn baseline_parse_token_as_path(
token: &Token,
context: &Context,
source: &Text,
) -> hir::Expression {
match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span())
}
RawToken::External(span) => hir::Expression::external_command(span, token.span()),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
RawToken::Integer(_) => hir::Expression::bare(token.span()),
RawToken::Size(_, _) => hir::Expression::bare(token.span()),
RawToken::Bare => hir::Expression::file_path(
expand_path(token.span().slice(source), context),
token.span(),
),
RawToken::String(span) => {
hir::Expression::file_path(expand_path(span.slice(source), context), token.span())
}
}
}
pub fn expand_path(string: &str, context: &Context) -> PathBuf {
let expanded = shellexpand::tilde_with_context(string, || context.shell_manager.homedir());
PathBuf::from(expanded.as_ref())
}

View File

@ -1,8 +1,11 @@
use crate::context::Context;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::registry::CommandRegistry;
use crate::parser::{ use crate::parser::{
hir, hir,
hir::{baseline_parse_single_token, baseline_parse_token_as_string}, hir::{
baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path,
baseline_parse_token_as_string,
},
DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode,
}; };
use crate::{Span, Tag, Tagged, TaggedItem, Text}; use crate::{Span, Tag, Tagged, TaggedItem, Text};
@ -12,8 +15,9 @@ use serde::{Deserialize, Serialize};
pub fn baseline_parse_tokens( pub fn baseline_parse_tokens(
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
syntax_type: SyntaxType,
) -> Result<Vec<hir::Expression>, ShellError> { ) -> Result<Vec<hir::Expression>, ShellError> {
let mut exprs: Vec<hir::Expression> = vec![]; let mut exprs: Vec<hir::Expression> = vec![];
@ -22,7 +26,7 @@ pub fn baseline_parse_tokens(
break; break;
} }
let expr = baseline_parse_next_expr(token_nodes, registry, source, SyntaxType::Any)?; let expr = baseline_parse_next_expr(token_nodes, context, source, syntax_type)?;
exprs.push(expr); exprs.push(expr);
} }
@ -35,7 +39,10 @@ pub enum SyntaxType {
Any, Any,
List, List,
Literal, Literal,
String,
Member,
Variable, Variable,
Number,
Path, Path,
Binary, Binary,
Block, Block,
@ -44,7 +51,7 @@ pub enum SyntaxType {
pub fn baseline_parse_next_expr( pub fn baseline_parse_next_expr(
tokens: &mut TokensIterator, tokens: &mut TokensIterator,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
syntax_type: SyntaxType, syntax_type: SyntaxType,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
@ -56,7 +63,7 @@ pub fn baseline_parse_next_expr(
match (syntax_type, next) { match (syntax_type, next) {
(SyntaxType::Path, TokenNode::Token(token)) => { (SyntaxType::Path, TokenNode::Token(token)) => {
return Ok(baseline_parse_token_as_string(token, source)) return Ok(baseline_parse_token_as_path(token, context, source))
} }
(SyntaxType::Path, token) => { (SyntaxType::Path, token) => {
@ -66,10 +73,50 @@ pub fn baseline_parse_next_expr(
)) ))
} }
_ => {} (SyntaxType::String, TokenNode::Token(token)) => {
return Ok(baseline_parse_token_as_string(token, source));
}
(SyntaxType::String, token) => {
return Err(ShellError::type_error(
"String",
token.type_name().simple_spanned(token.span()),
))
}
(SyntaxType::Number, TokenNode::Token(token)) => {
return Ok(baseline_parse_token_as_number(token, source));
}
(SyntaxType::Number, token) => {
return Err(ShellError::type_error(
"Numeric",
token.type_name().simple_spanned(token.span()),
))
}
// TODO: More legit member processing
(SyntaxType::Member, TokenNode::Token(token)) => {
return Ok(baseline_parse_token_as_string(token, source));
}
(SyntaxType::Member, token) => {
return Err(ShellError::type_error(
"member",
token.type_name().simple_spanned(token.span()),
))
}
(SyntaxType::Any, _) => {}
(SyntaxType::List, _) => {}
(SyntaxType::Literal, _) => {}
(SyntaxType::Variable, _) => {}
(SyntaxType::Binary, _) => {}
(SyntaxType::Block, _) => {}
(SyntaxType::Boolean, _) => {}
}; };
let first = baseline_parse_semantic_token(next, registry, source)?; let first = baseline_parse_semantic_token(next, context, source)?;
let possible_op = tokens.peek(); let possible_op = tokens.peek();
@ -88,7 +135,7 @@ pub fn baseline_parse_next_expr(
op.span(), op.span(),
)) ))
} }
Some(token) => baseline_parse_semantic_token(token, registry, source)?, Some(token) => baseline_parse_semantic_token(token, context, source)?,
}; };
// We definitely have a binary expression here -- let's see if we should coerce it into a block // We definitely have a binary expression here -- let's see if we should coerce it into a block
@ -176,13 +223,13 @@ pub fn baseline_parse_next_expr(
pub fn baseline_parse_semantic_token( pub fn baseline_parse_semantic_token(
token: &TokenNode, token: &TokenNode,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
match token { match token {
TokenNode::Token(token) => Ok(baseline_parse_single_token(token, source)), TokenNode::Token(token) => Ok(baseline_parse_single_token(token, source)),
TokenNode::Call(_call) => unimplemented!(), TokenNode::Call(_call) => unimplemented!(),
TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, registry, source), TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, context, source),
TokenNode::Pipeline(_pipeline) => unimplemented!(), TokenNode::Pipeline(_pipeline) => unimplemented!(),
TokenNode::Operator(_op) => unreachable!(), TokenNode::Operator(_op) => unreachable!(),
TokenNode::Flag(_flag) => Err(ShellError::unimplemented( TokenNode::Flag(_flag) => Err(ShellError::unimplemented(
@ -191,20 +238,24 @@ pub fn baseline_parse_semantic_token(
TokenNode::Member(_span) => unreachable!(), TokenNode::Member(_span) => unreachable!(),
TokenNode::Whitespace(_span) => unreachable!(), TokenNode::Whitespace(_span) => unreachable!(),
TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Error(error) => Err(*error.item.clone()),
TokenNode::Path(path) => baseline_parse_path(path, registry, source), TokenNode::Path(path) => baseline_parse_path(path, context, source),
} }
} }
pub fn baseline_parse_delimited( pub fn baseline_parse_delimited(
token: &Tagged<DelimitedNode>, token: &Tagged<DelimitedNode>,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
match token.delimiter() { match token.delimiter() {
Delimiter::Brace => { Delimiter::Brace => {
let children = token.children(); let children = token.children();
let exprs = let exprs = baseline_parse_tokens(
baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?; &mut TokensIterator::new(children),
context,
source,
SyntaxType::Any,
)?;
let expr = hir::RawExpression::Block(exprs); let expr = hir::RawExpression::Block(exprs);
Ok(Tagged::from_simple_spanned_item(expr, token.span())) Ok(Tagged::from_simple_spanned_item(expr, token.span()))
@ -212,8 +263,12 @@ pub fn baseline_parse_delimited(
Delimiter::Paren => unimplemented!(), Delimiter::Paren => unimplemented!(),
Delimiter::Square => { Delimiter::Square => {
let children = token.children(); let children = token.children();
let exprs = let exprs = baseline_parse_tokens(
baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?; &mut TokensIterator::new(children),
context,
source,
SyntaxType::Any,
)?;
let expr = hir::RawExpression::List(exprs); let expr = hir::RawExpression::List(exprs);
Ok(expr.tagged(Tag::unknown_origin(token.span()))) Ok(expr.tagged(Tag::unknown_origin(token.span())))
@ -223,10 +278,10 @@ pub fn baseline_parse_delimited(
pub fn baseline_parse_path( pub fn baseline_parse_path(
token: &Tagged<PathNode>, token: &Tagged<PathNode>,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
let head = baseline_parse_semantic_token(token.head(), registry, source)?; let head = baseline_parse_semantic_token(token.head(), context, source)?;
let mut tail = vec![]; let mut tail = vec![];

View File

@ -27,7 +27,7 @@ impl ToDebug for NamedArguments {
for (name, value) in &self.named { for (name, value) in &self.named {
match value { match value {
NamedValue::AbsentSwitch => continue, NamedValue::AbsentSwitch => continue,
NamedValue::PresentSwitch(span) => write!(f, " {}", span.slice(source))?, NamedValue::PresentSwitch(span) => write!(f, " --{}", span.slice(source))?,
NamedValue::AbsentValue => continue, NamedValue::AbsentValue => continue,
NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?,
} }

View File

@ -540,6 +540,8 @@ fn is_start_bare_char(c: char) -> bool {
'@' => true, '@' => true,
'*' => true, '*' => true,
'?' => true, '?' => true,
'~' => true,
'+' => true,
_ => false, _ => false,
} }
} }
@ -557,6 +559,8 @@ fn is_bare_char(c: char) -> bool {
'*' => true, '*' => true,
'?' => true, '?' => true,
'=' => true, '=' => true,
'~' => true,
'+' => true,
_ => false, _ => false,
} }
} }

View File

@ -1,5 +1,6 @@
use crate::context::Context;
use crate::errors::{ArgumentError, ShellError}; use crate::errors::{ArgumentError, ShellError};
use crate::parser::registry::{CommandRegistry, NamedType, PositionalType, Signature}; use crate::parser::registry::{NamedType, PositionalType, Signature};
use crate::parser::{baseline_parse_tokens, CallNode}; use crate::parser::{baseline_parse_tokens, CallNode};
use crate::parser::{ use crate::parser::{
hir::{self, NamedArguments}, hir::{self, NamedArguments},
@ -10,7 +11,7 @@ use log::trace;
pub fn parse_command( pub fn parse_command(
config: &Signature, config: &Signature,
registry: &CommandRegistry, context: &Context,
call: &Tagged<CallNode>, call: &Tagged<CallNode>,
source: &Text, source: &Text,
) -> Result<hir::Call, ShellError> { ) -> Result<hir::Call, ShellError> {
@ -31,7 +32,7 @@ pub fn parse_command(
.collect() .collect()
}); });
match parse_command_tail(&config, registry, children, source, call.span())? { match parse_command_tail(&config, context, children, source, call.span())? {
None => Ok(hir::Call::new(Box::new(head), None, None)), None => Ok(hir::Call::new(Box::new(head), None, None)),
Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)), Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)),
} }
@ -63,7 +64,7 @@ fn parse_command_head(head: &TokenNode) -> Result<hir::Expression, ShellError> {
fn parse_command_tail( fn parse_command_tail(
config: &Signature, config: &Signature,
registry: &CommandRegistry, context: &Context,
tail: Option<Vec<TokenNode>>, tail: Option<Vec<TokenNode>>,
source: &Text, source: &Text,
command_span: Span, command_span: Span,
@ -101,7 +102,7 @@ fn parse_command_tail(
} }
let expr = let expr =
hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?;
tail.restart(); tail.restart();
named.insert_mandatory(name, expr); named.insert_mandatory(name, expr);
@ -121,7 +122,7 @@ fn parse_command_tail(
)); ));
} }
let expr = hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; let expr = hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?;
tail.restart(); tail.restart();
named.insert_optional(name, Some(expr)); named.insert_optional(name, Some(expr));
@ -160,16 +161,17 @@ fn parse_command_tail(
} }
} }
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.syntax_type())?; let result = hir::baseline_parse_next_expr(tail, context, source, arg.syntax_type())?;
positional.push(result); positional.push(result);
} }
trace_remaining("after positional", tail.clone(), source); trace_remaining("after positional", tail.clone(), source);
// TODO: Only do this if rest params are specified if let Some(syntax_type) = config.rest_positional {
let remainder = baseline_parse_tokens(tail, registry, source)?; let remainder = baseline_parse_tokens(tail, context, source, syntax_type)?;
positional.extend(remainder); positional.extend(remainder);
}
trace_remaining("after rest", tail.clone(), source); trace_remaining("after rest", tail.clone(), source);
@ -180,6 +182,8 @@ fn parse_command_tail(
positional => Some(positional), positional => Some(positional),
}; };
// TODO: Error if extra unconsumed positional arguments
let named = match named { let named = match named {
named if named.named.is_empty() => None, named if named.named.is_empty() => None,
named => Some(named), named => Some(named),

View File

@ -74,8 +74,8 @@ pub struct Signature {
pub name: String, pub name: String,
#[new(default)] #[new(default)]
pub positional: Vec<PositionalType>, pub positional: Vec<PositionalType>,
#[new(value = "false")] #[new(value = "None")]
pub rest_positional: bool, pub rest_positional: Option<SyntaxType>,
#[new(default)] #[new(default)]
pub named: IndexMap<String, NamedType>, pub named: IndexMap<String, NamedType>,
#[new(value = "false")] #[new(value = "false")]
@ -130,8 +130,8 @@ impl Signature {
self self
} }
pub fn rest(mut self) -> Signature { pub fn rest(mut self, ty: SyntaxType) -> Signature {
self.rest_positional = true; self.rest_positional = Some(ty);
self self
} }
} }
@ -279,10 +279,10 @@ impl Signature {
crate fn parse_args( crate fn parse_args(
&self, &self,
call: &Tagged<CallNode>, call: &Tagged<CallNode>,
registry: &CommandRegistry, context: &Context,
source: &Text, source: &Text,
) -> Result<hir::Call, ShellError> { ) -> Result<hir::Call, ShellError> {
let args = parse_command(self, registry, call, source)?; let args = parse_command(self, context, call, source)?;
trace!("parsed args: {:?}", args); trace!("parsed args: {:?}", args);

View File

@ -1,7 +1,6 @@
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
ShellError, Signature, Tagged, Value, SyntaxType, Tagged, Value,
}; };
struct Add { struct Add {
@ -43,17 +42,12 @@ impl Add {
impl Plugin for Add { impl Plugin for Add {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("add")
name: "add".to_string(), .required("Field", SyntaxType::String)
positional: vec![ .required("Value", SyntaxType::String)
PositionalType::mandatory_any("Field"), .rest(SyntaxType::String).filter())
PositionalType::mandatory_any("Value"),
],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
} }
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional { if let Some(args) = call_info.args.positional {
match &args[0] { match &args[0] {

View File

@ -1,8 +1,5 @@
use crossterm::{cursor, terminal, Attribute, RawScreen}; use crossterm::{cursor, terminal, Attribute, RawScreen};
use indexmap::IndexMap; use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, SpanSource, Tagged, Value};
use nu::{
serve_plugin, CallInfo, NamedType, Plugin, ShellError, Signature, SpanSource, Tagged, Value,
};
use pretty_hex::*; use pretty_hex::*;
struct BinaryView; struct BinaryView;
@ -15,15 +12,7 @@ impl BinaryView {
impl Plugin for BinaryView { impl Plugin for BinaryView {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
let mut named = IndexMap::new(); Ok(Signature::build("binaryview").switch("lores"))
named.insert("lores".to_string(), NamedType::Switch);
Ok(Signature {
name: "binaryview".to_string(),
positional: vec![],
is_filter: false,
named,
rest_positional: false,
})
} }
fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) { fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) {

View File

@ -1,7 +1,6 @@
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
ShellError, Signature, Tagged, Value, SyntaxType, Tagged, Value,
}; };
struct Edit { struct Edit {
@ -42,17 +41,12 @@ impl Edit {
impl Plugin for Edit { impl Plugin for Edit {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("edit")
name: "edit".to_string(), .required("Field", SyntaxType::String)
positional: vec![ .required("Value", SyntaxType::String)
PositionalType::mandatory_any("Field"), .filter())
PositionalType::mandatory_any("Value"),
],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
} }
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional { if let Some(args) = call_info.args.positional {
match &args[0] { match &args[0] {

View File

@ -1,7 +1,6 @@
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, NamedType, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Signature, Tagged, TaggedItem, Value, SyntaxType, Tagged, TaggedItem, Value,
}; };
enum Action { enum Action {
@ -116,19 +115,14 @@ impl Inc {
impl Plugin for Inc { impl Plugin for Inc {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
let mut named = IndexMap::new(); Ok(Signature::build("inc")
named.insert("major".to_string(), NamedType::Switch); .switch("major")
named.insert("minor".to_string(), NamedType::Switch); .switch("minor")
named.insert("patch".to_string(), NamedType::Switch); .switch("patch")
.rest(SyntaxType::String)
Ok(Signature { .filter())
name: "inc".to_string(),
positional: vec![],
is_filter: true,
named,
rest_positional: true,
})
} }
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if call_info.args.has("major") { if call_info.args.has("major") {
self.for_semver(SemVerAction::Major); self.for_semver(SemVerAction::Major);

View File

@ -1,7 +1,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tagged, Value, SyntaxType, Tagged, Value,
}; };
struct Skip { struct Skip {
@ -20,7 +20,7 @@ impl Plugin for Skip {
positional: vec![], positional: vec![],
is_filter: true, is_filter: true,
named: IndexMap::new(), named: IndexMap::new(),
rest_positional: true, rest_positional: Some(SyntaxType::Number),
}) })
} }
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {

View File

@ -1,7 +1,6 @@
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, NamedType, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Signature, Tagged, Value, SyntaxType, Tagged, Value,
}; };
use regex::Regex; use regex::Regex;
@ -40,10 +39,12 @@ impl Str {
let applied = match self.action.as_ref() { let applied = match self.action.as_ref() {
Some(Action::Downcase) => Value::string(input.to_ascii_lowercase()), Some(Action::Downcase) => Value::string(input.to_ascii_lowercase()),
Some(Action::Upcase) => Value::string(input.to_ascii_uppercase()), Some(Action::Upcase) => Value::string(input.to_ascii_uppercase()),
Some(Action::ToInteger) => match input.trim().parse::<i64>() { Some(Action::ToInteger) => match input.trim() {
other => match other.parse::<i64>() {
Ok(v) => Value::int(v), Ok(v) => Value::int(v),
Err(_) => Value::string(input), Err(_) => Value::string(input),
}, },
},
Some(Action::Replace(ref mode)) => match mode { Some(Action::Replace(ref mode)) => match mode {
ReplaceAction::Direct => Value::string(self.first_param()), ReplaceAction::Direct => Value::string(self.first_param()),
ReplaceAction::FindAndReplace => { ReplaceAction::FindAndReplace => {
@ -138,9 +139,7 @@ impl Str {
Some(ref f) => { Some(ref f) => {
let replacement = match value.item.get_data_by_path(value.tag(), f) { let replacement = match value.item.get_data_by_path(value.tag(), f) {
Some(result) => self.strutils(result.map(|x| x.clone()))?, Some(result) => self.strutils(result.map(|x| x.clone()))?,
None => { None => return Ok(Tagged::from_item(Value::nothing(), value.tag)),
return Err(ShellError::string("str could not find field to replace"))
}
}; };
match value match value
.item .item
@ -168,20 +167,14 @@ impl Str {
impl Plugin for Str { impl Plugin for Str {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
let mut named = IndexMap::new(); Ok(Signature::build("str")
named.insert("downcase".to_string(), NamedType::Switch); .switch("downcase")
named.insert("upcase".to_string(), NamedType::Switch); .switch("upcase")
named.insert("to-int".to_string(), NamedType::Switch); .switch("to-int")
named.insert("replace".to_string(), NamedType::Switch); .switch("replace")
named.insert("find-replace".to_string(), NamedType::Switch); .switch("find-replace")
.rest(SyntaxType::Member)
Ok(Signature { .filter())
name: "str".to_string(),
positional: vec![],
is_filter: true,
named,
rest_positional: true,
})
} }
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {

View File

@ -1,4 +1,3 @@
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tag, Tagged, Value, Tag, Tagged, Value,
@ -14,6 +13,7 @@ impl Sum {
fn sum(&mut self, value: Tagged<Value>) -> Result<(), ShellError> { fn sum(&mut self, value: Tagged<Value>) -> Result<(), ShellError> {
match value.item { match value.item {
Value::Primitive(Primitive::Nothing) => Ok(()),
Value::Primitive(Primitive::Int(i)) => { Value::Primitive(Primitive::Int(i)) => {
match self.total { match self.total {
Some(Tagged { Some(Tagged {
@ -64,14 +64,9 @@ impl Sum {
impl Plugin for Sum { impl Plugin for Sum {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("sum").filter())
name: "sum".to_string(),
positional: vec![],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
} }
fn begin_filter(&mut self, _: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, _: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![]) Ok(vec![])
} }

View File

@ -1,10 +1,9 @@
use futures::executor::block_on; use futures::executor::block_on;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use heim::{disk, memory, net}; use heim::{disk, memory, net};
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tag, Tagged, TaggedDictBuilder, Value, SyntaxType, Tag, Tagged, TaggedDictBuilder, Value,
}; };
use std::ffi::OsStr; use std::ffi::OsStr;
@ -251,14 +250,9 @@ async fn sysinfo(tag: Tag) -> Vec<Tagged<Value>> {
impl Plugin for Sys { impl Plugin for Sys {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("sys").rest(SyntaxType::Any))
name: "sys".to_string(),
positional: vec![],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
} }
fn begin_filter(&mut self, callinfo: CallInfo) -> Result<Vec<ReturnValue>, ShellError> { fn begin_filter(&mut self, callinfo: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span))) Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span)))
.into_iter() .into_iter()

View File

@ -1,6 +1,5 @@
use crossterm::{cursor, terminal, RawScreen}; use crossterm::{cursor, terminal, RawScreen};
use crossterm::{InputEvent, KeyEvent}; use crossterm::{InputEvent, KeyEvent};
use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, SpanSource, serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, SpanSource,
Tagged, Value, Tagged, Value,
@ -27,13 +26,7 @@ impl TextView {
impl Plugin for TextView { impl Plugin for TextView {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("textview"))
name: "textview".to_string(),
positional: vec![],
is_filter: false,
named: IndexMap::new(),
rest_positional: false,
})
} }
fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) { fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) {

View File

@ -1,5 +1,4 @@
use derive_new::new; use derive_new::new;
use indexmap::IndexMap;
use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Tagged, Value}; use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Tagged, Value};
use ptree::item::StringItem; use ptree::item::StringItem;
use ptree::output::print_tree_with; use ptree::output::print_tree_with;
@ -81,13 +80,7 @@ struct TreeViewer;
impl Plugin for TreeViewer { impl Plugin for TreeViewer {
fn config(&mut self) -> Result<Signature, ShellError> { fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature { Ok(Signature::build("tree"))
name: "tree".to_string(),
positional: vec![],
is_filter: false,
named: IndexMap::new(),
rest_positional: true,
})
} }
fn sink(&mut self, _call_info: CallInfo, input: Vec<Tagged<Value>>) { fn sink(&mut self, _call_info: CallInfo, input: Vec<Tagged<Value>>) {

View File

@ -32,6 +32,25 @@ macro_rules! trace_stream {
}}; }};
} }
#[macro_export]
macro_rules! trace_out_stream {
(target: $target:tt, source: $source:expr, $desc:tt = $expr:expr) => {{
if log::log_enabled!(target: $target, log::Level::Trace) {
use futures::stream::StreamExt;
let source = $source.clone();
let objects = $expr.values.inspect(move |o| {
trace!(target: $target, "{} = {}", $desc, o.debug(&source));
});
$crate::stream::OutputStream::new(objects)
} else {
$expr
}
}};
}
crate use crate::cli::MaybeOwned; crate use crate::cli::MaybeOwned;
crate use crate::commands::command::{ crate use crate::commands::command::{
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext, CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,

View File

@ -72,12 +72,16 @@ impl Shell for FilesystemShell {
"filesystem".to_string() "filesystem".to_string()
} }
fn homedir(&self) -> Option<PathBuf> {
dirs::home_dir()
}
fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
let cwd = self.path(); let cwd = self.path();
let mut full_path = PathBuf::from(self.path()); let mut full_path = PathBuf::from(self.path());
match &args.nth(0) { match &args.nth(0) {
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)), Some(value) => full_path.push(Path::new(&value.as_path()?)),
_ => {} _ => {}
} }
@ -176,7 +180,7 @@ impl Shell for FilesystemShell {
} }
}, },
Some(v) => { Some(v) => {
let target = v.as_string()?; let target = v.as_path()?;
let path = PathBuf::from(self.path()); let path = PathBuf::from(self.path());
match dunce::canonicalize(path.join(target).as_path()) { match dunce::canonicalize(path.join(target).as_path()) {
Ok(p) => p, Ok(p) => p,

View File

@ -7,9 +7,11 @@ use crate::context::SourceMap;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::OutputStream; use crate::stream::OutputStream;
use std::path::PathBuf;
pub trait Shell: std::fmt::Debug { pub trait Shell: std::fmt::Debug {
fn name(&self, source_map: &SourceMap) -> String; fn name(&self, source_map: &SourceMap) -> String;
fn homedir(&self) -> Option<PathBuf>;
fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError>; fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError>;
fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError>; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError>;
fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result<OutputStream, ShellError>; fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result<OutputStream, ShellError>;

View File

@ -9,6 +9,7 @@ use crate::shell::filesystem_shell::FilesystemShell;
use crate::shell::shell::Shell; use crate::shell::shell::Shell;
use crate::stream::OutputStream; use crate::stream::OutputStream;
use std::error::Error; use std::error::Error;
use std::path::PathBuf;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -102,16 +103,24 @@ impl ShellManager {
self.set_path(self.path()); self.set_path(self.path());
} }
pub fn homedir(&self) -> Option<PathBuf> {
let env = self.shells.lock().unwrap();
env[self.current_shell].homedir()
}
pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
let env = self.shells.lock().unwrap(); let env = self.shells.lock().unwrap();
env[self.current_shell].ls(args) env[self.current_shell].ls(args)
} }
pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
let env = self.shells.lock().unwrap(); let env = self.shells.lock().unwrap();
env[self.current_shell].cd(args) env[self.current_shell].cd(args)
} }
pub fn cp( pub fn cp(
&self, &self,
args: CopyArgs, args: CopyArgs,

View File

@ -69,6 +69,10 @@ impl Shell for ValueShell {
) )
} }
fn homedir(&self) -> Option<PathBuf> {
dirs::home_dir()
}
fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
Ok(self Ok(self
.members() .members()

View File

@ -158,6 +158,7 @@ impl FileStructure {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq;
use super::{FileStructure, Res}; use super::{FileStructure, Res};
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -18,7 +18,7 @@ fn ls_lists_regular_files() {
nu!( nu!(
output, output,
cwd(&full_path), cwd(&full_path),
"ls | get name | lines| split-column \".\" | get Column2 | str Column2 --to-int | sum | echo $it" r#"ls | get name | lines | split-column "." | get Column2 | str Column2 --to-int | sum | echo $it"#
); );
assert_eq!(output, "30"); assert_eq!(output, "30");