mirror of
https://github.com/nushell/nushell.git
synced 2025-04-27 14:48:21 +02:00
Add support for ~ expansion
This ended up being a bit of a yak shave. The basic idea in this commit is to expand `~` in paths, but only in paths. The way this is accomplished is by doing the expansion inside of the code that parses literal syntax for `SyntaxType::Path`. As a quick refresher: every command is entitled to expand its arguments in a custom way. While this could in theory be used for general-purpose macros, today the expansion facility is limited to syntactic hints. For example, the syntax `where cpu > 0` expands under the hood to `where { $it.cpu > 0 }`. This happens because the first argument to `where` is defined as a `SyntaxType::Block`, and the parser coerces binary expressions whose left-hand-side looks like a member into a block when the command is expecting one. This is mildly more magical than what most programming languages would do, but we believe that it makes sense to allow commands to fine-tune the syntax because of the domain nushell is in (command-line shells). The syntactic expansions supported by this facility are relatively limited. For example, we don't allow `$it` to become a bare word, simply because the command asks for a string in the relevant position. That would quickly become more confusing than it's worth. This PR adds a new `SyntaxType` rule: `SyntaxType::Path`. When a command declares a parameter as a `SyntaxType::Path`, string literals and bare words passed as an argument to that parameter are processed using the path expansion rules. Right now, that only means that `~` is expanded into the home directory, but additional rules are possible in the future. By restricting this expansion to a syntactic expansion when passed as an argument to a command expecting a path, we avoid making `~` a generally reserved character. This will also allow us to give good tab completion for paths with `~` characters in them when a command is expecting a path. In order to accomplish the above, this commit changes the parsing functions to take a `Context` instead of just a `CommandRegistry`. From the perspective of macro expansion, you can think of the `CommandRegistry` as a dictionary of in-scope macros, and the `Context` as the compile-time state used in expansion. This could gain additional functionality over time as we find more uses for the expansion system.
This commit is contained in:
parent
8da82c79b8
commit
34292b282a
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"
|
||||||
|
15
src/cli.rs
15
src/cli.rs
@ -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$")?;
|
let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.exe$")?;
|
||||||
|
|
||||||
|
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)?;
|
||||||
}
|
}
|
||||||
@ -504,7 +513,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,
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
@ -426,14 +427,16 @@ pub fn parse_string_as_value(
|
|||||||
name_span: Span,
|
name_span: Span,
|
||||||
) -> Result<Tagged<Value>, ShellError> {
|
) -> Result<Tagged<Value>, ShellError> {
|
||||||
match extension {
|
match extension {
|
||||||
Some(x) if x == "csv" => crate::commands::from_csv::from_csv_string_to_value(
|
Some(x) if x == "csv" => {
|
||||||
contents,
|
crate::commands::from_csv::from_csv_string_to_value(contents, false, contents_tag)
|
||||||
false,
|
|
||||||
contents_tag,
|
|
||||||
)
|
|
||||||
.map_err(move |_| {
|
.map_err(move |_| {
|
||||||
ShellError::labeled_error("Could not open as CSV", "could not open as CSV", name_span)
|
ShellError::labeled_error(
|
||||||
}),
|
"Could not open as CSV",
|
||||||
|
"could not open as CSV",
|
||||||
|
name_span,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
Some(x) if x == "toml" => {
|
Some(x) if x == "toml" => {
|
||||||
crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err(
|
crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err(
|
||||||
move |_| {
|
move |_| {
|
||||||
|
@ -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(
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,43 +24,32 @@ 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>>>>()
|
||||||
};
|
};
|
||||||
if reverse {
|
if reverse {
|
||||||
vec.sort_by_cached_key(|item| {
|
vec.sort_by_cached_key(|item| std::cmp::Reverse(calc_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())
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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>,
|
||||||
|
@ -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),
|
||||||
|
@ -68,7 +68,7 @@ impl TableView {
|
|||||||
for head in 0..headers.len() {
|
for head in 0..headers.len() {
|
||||||
let mut current_row_max = 0;
|
let mut current_row_max = 0;
|
||||||
for row in 0..values.len() {
|
for row in 0..values.len() {
|
||||||
if entries[row][head].len() > current_row_max {
|
if head > entries[row].len() && entries[row][head].len() > current_row_max {
|
||||||
current_row_max = entries[row][head].len();
|
current_row_max = entries[row][head].len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,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;
|
||||||
|
@ -285,6 +285,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![],
|
||||||
@ -542,6 +543,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()))
|
||||||
}
|
}
|
||||||
@ -577,6 +582,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);
|
||||||
|
|
||||||
|
@ -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())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)),
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
@ -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![];
|
||||||
|
|
||||||
|
@ -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))?,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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] {
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
#![feature(option_flattening)]
|
#![feature(option_flattening)]
|
||||||
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;
|
||||||
@ -16,15 +13,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>>) {
|
||||||
|
@ -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] {
|
||||||
|
@ -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);
|
||||||
|
@ -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> {
|
||||||
|
@ -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> {
|
||||||
|
@ -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![])
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
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,
|
||||||
@ -29,13 +28,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>>) {
|
||||||
|
@ -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>>) {
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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>;
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
|
@ -105,7 +105,10 @@ impl FileStructure {
|
|||||||
self.root = path.to_path_buf();
|
self.root = path.to_path_buf();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paths_applying_with<F>(&mut self, to: F) -> Result<Vec<(PathBuf, PathBuf)>, Box<dyn std::error::Error>>
|
pub fn paths_applying_with<F>(
|
||||||
|
&mut self,
|
||||||
|
to: F,
|
||||||
|
) -> Result<Vec<(PathBuf, PathBuf)>, Box<dyn std::error::Error>>
|
||||||
where
|
where
|
||||||
F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>>,
|
F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>>,
|
||||||
{
|
{
|
||||||
@ -155,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;
|
||||||
@ -175,7 +179,8 @@ mod tests {
|
|||||||
fn prepares_and_decorates_source_files_for_copying() {
|
fn prepares_and_decorates_source_files_for_copying() {
|
||||||
let mut res = FileStructure::new();
|
let mut res = FileStructure::new();
|
||||||
|
|
||||||
res.walk_decorate(fixtures().as_path()).expect("Can not decorate files traversal.");
|
res.walk_decorate(fixtures().as_path())
|
||||||
|
.expect("Can not decorate files traversal.");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.resources,
|
res.resources,
|
||||||
|
@ -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");
|
||||||
|
Loading…
Reference in New Issue
Block a user