mirror of
https://github.com/nushell/nushell.git
synced 2024-11-26 18:33:50 +01:00
Add a config variable with engine support (#332)
* Add a config variable with engine support * Add a config variable with engine support * Oops, cleanup
This commit is contained in:
parent
e76451866d
commit
0f107b2830
2
TODO.md
2
TODO.md
@ -35,7 +35,7 @@
|
|||||||
- [x] ctrl-c support
|
- [x] ctrl-c support
|
||||||
- [x] operator overflow
|
- [x] operator overflow
|
||||||
- [x] Support for `$in`
|
- [x] Support for `$in`
|
||||||
- [ ] config system
|
- [x] config system
|
||||||
- [ ] shells
|
- [ ] shells
|
||||||
- [ ] plugins
|
- [ ] plugins
|
||||||
- [ ] dataframes
|
- [ ] dataframes
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Config, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
||||||
@ -136,6 +136,7 @@ fn string_helper(
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
|
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
if decimals && decimals_value.is_some() && decimals_value.unwrap().is_negative() {
|
if decimals && decimals_value.is_some() && decimals_value.unwrap().is_negative() {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
@ -147,13 +148,16 @@ fn string_helper(
|
|||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
action(&v, head, decimals, decimals_value, false)
|
action(&v, head, decimals, decimals_value, false, &config)
|
||||||
} else {
|
} else {
|
||||||
let mut ret = v;
|
let mut ret = v;
|
||||||
for path in &column_paths {
|
for path in &column_paths {
|
||||||
|
let config = config.clone();
|
||||||
let r = ret.update_cell_path(
|
let r = ret.update_cell_path(
|
||||||
&path.members,
|
&path.members,
|
||||||
Box::new(move |old| action(old, head, decimals, decimals_value, false)),
|
Box::new(move |old| {
|
||||||
|
action(old, head, decimals, decimals_value, false, &config)
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
if let Err(error) = r {
|
if let Err(error) = r {
|
||||||
return Value::Error { error };
|
return Value::Error { error };
|
||||||
@ -173,6 +177,7 @@ pub fn action(
|
|||||||
decimals: bool,
|
decimals: bool,
|
||||||
digits: Option<i64>,
|
digits: Option<i64>,
|
||||||
group_digits: bool,
|
group_digits: bool,
|
||||||
|
config: &Config,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val, .. } => {
|
Value::Int { val, .. } => {
|
||||||
@ -212,7 +217,7 @@ pub fn action(
|
|||||||
},
|
},
|
||||||
|
|
||||||
Value::Filesize { val: _, .. } => Value::String {
|
Value::Filesize { val: _, .. } => Value::String {
|
||||||
val: input.clone().into_string(", "),
|
val: input.clone().into_string(", ", config),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Nothing { .. } => Value::String {
|
Value::Nothing { .. } => Value::String {
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Command, EngineState, Stack, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
PipelineData, Span,
|
PipelineData, Span, Value, CONFIG_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::To;
|
use crate::To;
|
||||||
@ -57,6 +57,16 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
|||||||
|
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
// Set up our initial config to start from
|
||||||
|
stack.vars.insert(
|
||||||
|
CONFIG_VARIABLE_ID,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec![],
|
||||||
|
vals: vec![],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
match eval_block(
|
match eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
|
@ -78,6 +78,7 @@ fn from_csv(
|
|||||||
|
|
||||||
let noheaders = call.has_flag("noheaders");
|
let noheaders = call.has_flag("noheaders");
|
||||||
let separator: Option<Value> = call.get_flag(engine_state, stack, "separator")?;
|
let separator: Option<Value> = call.get_flag(engine_state, stack, "separator")?;
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
let sep = match separator {
|
let sep = match separator {
|
||||||
Some(Value::String { val: s, span }) => {
|
Some(Value::String { val: s, span }) => {
|
||||||
@ -97,7 +98,7 @@ fn from_csv(
|
|||||||
_ => ',',
|
_ => ',',
|
||||||
};
|
};
|
||||||
|
|
||||||
from_delimited_data(noheaders, sep, input, name)
|
from_delimited_data(noheaders, sep, input, name, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use csv::ReaderBuilder;
|
use csv::ReaderBuilder;
|
||||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
|
use nu_protocol::{Config, IntoPipelineData, PipelineData, ShellError, Span, Value};
|
||||||
|
|
||||||
fn from_delimited_string_to_value(
|
fn from_delimited_string_to_value(
|
||||||
s: String,
|
s: String,
|
||||||
@ -50,8 +50,9 @@ pub fn from_delimited_data(
|
|||||||
sep: char,
|
sep: char,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
name: Span,
|
name: Span,
|
||||||
|
config: &Config,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let concat_string = input.collect_string("");
|
let concat_string = input.collect_string("", config);
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
from_delimited_string_to_value(concat_string, noheaders, sep, name)
|
from_delimited_string_to_value(concat_string, noheaders, sep, name)
|
||||||
|
@ -4,6 +4,7 @@ use indexmap::map::IndexMap;
|
|||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::Config;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
@ -41,7 +42,8 @@ impl Command for FromEml {
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
let preview_body: Option<Spanned<i64>> =
|
let preview_body: Option<Spanned<i64>> =
|
||||||
call.get_flag(engine_state, stack, "preview-body")?;
|
call.get_flag(engine_state, stack, "preview-body")?;
|
||||||
from_eml(input, preview_body, head)
|
let config = stack.get_config()?;
|
||||||
|
from_eml(input, preview_body, head, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -176,8 +178,9 @@ fn from_eml(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
preview_body: Option<Spanned<i64>>,
|
preview_body: Option<Spanned<i64>>,
|
||||||
head: Span,
|
head: Span,
|
||||||
|
config: &Config,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value = input.collect_string("");
|
let value = input.collect_string("", config);
|
||||||
|
|
||||||
let body_preview = preview_body
|
let body_preview = preview_body
|
||||||
.map(|b| b.item as usize)
|
.map(|b| b.item as usize)
|
||||||
|
@ -72,12 +72,13 @@ impl Command for FromJson {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let mut string_input = input.collect_string("");
|
let config = stack.get_config()?;
|
||||||
|
let mut string_input = input.collect_string("", &config);
|
||||||
string_input.push('\n');
|
string_input.push('\n');
|
||||||
|
|
||||||
// TODO: turn this into a structured underline of the nu_json error
|
// TODO: turn this into a structured underline of the nu_json error
|
||||||
|
@ -2,7 +2,7 @@ use super::delimited::from_delimited_data;
|
|||||||
|
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{PipelineData, ShellError, Signature};
|
use nu_protocol::{Config, PipelineData, ShellError, Signature};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FromTsv;
|
pub struct FromTsv;
|
||||||
@ -27,20 +27,21 @@ impl Command for FromTsv {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
from_tsv(call, input)
|
let config = stack.get_config()?;
|
||||||
|
from_tsv(call, input, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_tsv(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
|
fn from_tsv(call: &Call, input: PipelineData, config: &Config) -> Result<PipelineData, ShellError> {
|
||||||
let name = call.head;
|
let name = call.head;
|
||||||
|
|
||||||
let noheaders = call.has_flag("noheaders");
|
let noheaders = call.has_flag("noheaders");
|
||||||
|
|
||||||
from_delimited_data(noheaders, '\t', input, name)
|
from_delimited_data(noheaders, '\t', input, name, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Value};
|
use nu_protocol::{Config, Example, PipelineData, ShellError, Signature, Span, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FromUrl;
|
pub struct FromUrl;
|
||||||
@ -21,12 +21,13 @@ impl Command for FromUrl {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
from_url(input, head)
|
let config = stack.get_config()?;
|
||||||
|
from_url(input, head, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -52,8 +53,8 @@ impl Command for FromUrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn from_url(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
|
||||||
let concat_string = input.collect_string("");
|
let concat_string = input.collect_string("", config);
|
||||||
|
|
||||||
let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string);
|
let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Value,
|
Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
use serde::de::Deserialize;
|
use serde::de::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -65,12 +65,13 @@ impl Command for FromYaml {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
from_yaml(input, head)
|
let config = stack.get_config()?;
|
||||||
|
from_yaml(input, head, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,12 +94,13 @@ impl Command for FromYml {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
from_yaml(input, head)
|
let config = stack.get_config()?;
|
||||||
|
from_yaml(input, head, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,8 +204,8 @@ pub fn from_yaml_string_to_value(s: String, span: Span) -> Result<Value, ShellEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn from_yaml(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
|
||||||
let concat_string = input.collect_string("");
|
let concat_string = input.collect_string("", config);
|
||||||
|
|
||||||
match from_yaml_string_to_value(concat_string, head) {
|
match from_yaml_string_to_value(concat_string, head) {
|
||||||
Ok(x) => Ok(x.into_pipeline_data()),
|
Ok(x) => Ok(x.into_pipeline_data()),
|
||||||
@ -248,6 +250,7 @@ mod test {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
let config = Config::default();
|
||||||
for tc in tt {
|
for tc in tt {
|
||||||
let actual = from_yaml_string_to_value(tc.input.to_owned(), Span::unknown());
|
let actual = from_yaml_string_to_value(tc.input.to_owned(), Span::unknown());
|
||||||
if actual.is_err() {
|
if actual.is_err() {
|
||||||
@ -259,8 +262,8 @@ mod test {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual.unwrap().into_string(""),
|
actual.unwrap().into_string("", &config),
|
||||||
tc.expected.unwrap().into_string("")
|
tc.expected.unwrap().into_string("", &config)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,13 @@ impl Command for BuildString {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let config = stack.get_config()?;
|
||||||
let output = call
|
let output = call
|
||||||
.positional
|
.positional
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| eval_expression(engine_state, stack, expr).map(|val| val.into_string(", ")))
|
.map(|expr| {
|
||||||
|
eval_expression(engine_state, stack, expr).map(|val| val.into_string(", ", &config))
|
||||||
|
})
|
||||||
.collect::<Result<Vec<String>, ShellError>>()?;
|
.collect::<Result<Vec<String>, ShellError>>()?;
|
||||||
|
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
|
@ -34,12 +34,14 @@ impl Command for StrCollect {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let separator: Option<String> = call.opt(engine_state, stack, 0)?;
|
let separator: Option<String> = call.opt(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
// Hmm, not sure what we actually want. If you don't use debug_string, Date comes out as human readable
|
// Hmm, not sure what we actually want. If you don't use debug_string, Date comes out as human readable
|
||||||
// which feels funny
|
// which feels funny
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
let strings: Vec<String> = input
|
let strings: Vec<String> = input
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|value| value.debug_string("\n"))
|
.map(|value| value.debug_string("\n", &config))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let output = if let Some(separator) = separator {
|
let output = if let Some(separator) = separator {
|
||||||
|
@ -8,7 +8,7 @@ use std::sync::mpsc;
|
|||||||
|
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
||||||
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, Span, Spanned};
|
use nu_protocol::{Config, IntoInterruptiblePipelineData, PipelineData, Span, Spanned};
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
|
|
||||||
@ -44,13 +44,15 @@ impl Command for External {
|
|||||||
let last_expression = call.has_flag("last_expression");
|
let last_expression = call.has_flag("last_expression");
|
||||||
let env_vars = stack.get_env_vars();
|
let env_vars = stack.get_env_vars();
|
||||||
|
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
let command = ExternalCommand {
|
let command = ExternalCommand {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
last_expression,
|
last_expression,
|
||||||
env_vars,
|
env_vars,
|
||||||
};
|
};
|
||||||
command.run_with_input(engine_state, input)
|
command.run_with_input(engine_state, input, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +68,7 @@ impl ExternalCommand {
|
|||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
config: Config,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut process = self.create_command();
|
let mut process = self.create_command();
|
||||||
|
|
||||||
@ -112,7 +115,10 @@ impl ExternalCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
x => {
|
x => {
|
||||||
if stdin_write.write(x.into_string(", ").as_bytes()).is_err() {
|
if stdin_write
|
||||||
|
.write(x.into_string(", ", &config).as_bytes())
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, PathMember},
|
ast::{Call, PathMember},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value,
|
Config, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
||||||
use terminal_size::{Height, Width};
|
use terminal_size::{Height, Width};
|
||||||
@ -57,10 +57,12 @@ prints out the list properly."#
|
|||||||
let color_param: bool = call.has_flag("color");
|
let color_param: bool = call.has_flag("color");
|
||||||
let separator_param: Option<String> = call.get_flag(engine_state, stack, "separator")?;
|
let separator_param: Option<String> = call.get_flag(engine_state, stack, "separator")?;
|
||||||
|
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Value(Value::List { vals, .. }) => {
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
// dbg!("value::list");
|
// dbg!("value::list");
|
||||||
let data = convert_to_list2(vals);
|
let data = convert_to_list2(vals, &config);
|
||||||
if let Some(items) = data {
|
if let Some(items) = data {
|
||||||
Ok(create_grid_output2(
|
Ok(create_grid_output2(
|
||||||
items,
|
items,
|
||||||
@ -75,7 +77,7 @@ prints out the list properly."#
|
|||||||
}
|
}
|
||||||
PipelineData::Stream(stream) => {
|
PipelineData::Stream(stream) => {
|
||||||
// dbg!("value::stream");
|
// dbg!("value::stream");
|
||||||
let data = convert_to_list2(stream);
|
let data = convert_to_list2(stream, &config);
|
||||||
if let Some(items) = data {
|
if let Some(items) = data {
|
||||||
Ok(create_grid_output2(
|
Ok(create_grid_output2(
|
||||||
items,
|
items,
|
||||||
@ -94,7 +96,7 @@ prints out the list properly."#
|
|||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
for (i, (c, v)) in cols.into_iter().zip(vals.into_iter()).enumerate() {
|
for (i, (c, v)) in cols.into_iter().zip(vals.into_iter()).enumerate() {
|
||||||
items.push((i, c, v.into_string(", ")))
|
items.push((i, c, v.into_string(", ", &config)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(create_grid_output2(
|
Ok(create_grid_output2(
|
||||||
@ -171,7 +173,10 @@ fn create_grid_output2(
|
|||||||
.into_pipeline_data()
|
.into_pipeline_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_list2(iter: impl IntoIterator<Item = Value>) -> Option<Vec<(usize, String, String)>> {
|
fn convert_to_list2(
|
||||||
|
iter: impl IntoIterator<Item = Value>,
|
||||||
|
config: &Config,
|
||||||
|
) -> Option<Vec<(usize, String, String)>> {
|
||||||
let mut iter = iter.into_iter().peekable();
|
let mut iter = iter.into_iter().peekable();
|
||||||
|
|
||||||
if let Some(first) = iter.peek() {
|
if let Some(first) = iter.peek() {
|
||||||
@ -187,7 +192,7 @@ fn convert_to_list2(iter: impl IntoIterator<Item = Value>) -> Option<Vec<(usize,
|
|||||||
let mut row = vec![row_num.to_string()];
|
let mut row = vec![row_num.to_string()];
|
||||||
|
|
||||||
if headers.is_empty() {
|
if headers.is_empty() {
|
||||||
row.push(item.into_string(", "))
|
row.push(item.into_string(", ", config))
|
||||||
} else {
|
} else {
|
||||||
for header in headers.iter().skip(1) {
|
for header in headers.iter().skip(1) {
|
||||||
let result = match item {
|
let result = match item {
|
||||||
@ -201,7 +206,7 @@ fn convert_to_list2(iter: impl IntoIterator<Item = Value>) -> Option<Vec<(usize,
|
|||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(value) => row.push(value.into_string(", ")),
|
Ok(value) => row.push(value.into_string(", ", config)),
|
||||||
Err(_) => row.push(String::new()),
|
Err(_) => row.push(String::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::ast::{Call, PathMember};
|
use nu_protocol::ast::{Call, PathMember};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, Span, Value};
|
use nu_protocol::{Config, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value};
|
||||||
use nu_table::StyledString;
|
use nu_table::{StyledString, Theme};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -27,11 +27,12 @@ impl Command for Table {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
||||||
w as usize
|
w as usize
|
||||||
@ -41,7 +42,7 @@ impl Command for Table {
|
|||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Value(Value::List { vals, .. }) => {
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
let table = convert_to_table(vals, ctrlc)?;
|
let table = convert_to_table(vals, ctrlc, &config)?;
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
||||||
@ -56,7 +57,7 @@ impl Command for Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::Stream(stream) => {
|
PipelineData::Stream(stream) => {
|
||||||
let table = convert_to_table(stream, ctrlc)?;
|
let table = convert_to_table(stream, ctrlc, &config)?;
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
||||||
@ -80,7 +81,7 @@ impl Command for Table {
|
|||||||
style: nu_table::TextStyle::default_field(),
|
style: nu_table::TextStyle::default_field(),
|
||||||
},
|
},
|
||||||
StyledString {
|
StyledString {
|
||||||
contents: v.into_string(", "),
|
contents: v.into_string(", ", &config),
|
||||||
style: nu_table::TextStyle::default(),
|
style: nu_table::TextStyle::default(),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
@ -89,7 +90,7 @@ impl Command for Table {
|
|||||||
let table = nu_table::Table {
|
let table = nu_table::Table {
|
||||||
headers: vec![],
|
headers: vec![],
|
||||||
data: output,
|
data: output,
|
||||||
theme: nu_table::Theme::rounded(),
|
theme: load_theme_from_config(&config),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
|
||||||
@ -109,6 +110,7 @@ impl Command for Table {
|
|||||||
fn convert_to_table(
|
fn convert_to_table(
|
||||||
iter: impl IntoIterator<Item = Value>,
|
iter: impl IntoIterator<Item = Value>,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
config: &Config,
|
||||||
) -> Result<Option<nu_table::Table>, ShellError> {
|
) -> Result<Option<nu_table::Table>, ShellError> {
|
||||||
let mut iter = iter.into_iter().peekable();
|
let mut iter = iter.into_iter().peekable();
|
||||||
|
|
||||||
@ -133,7 +135,7 @@ fn convert_to_table(
|
|||||||
let mut row = vec![row_num.to_string()];
|
let mut row = vec![row_num.to_string()];
|
||||||
|
|
||||||
if headers.is_empty() {
|
if headers.is_empty() {
|
||||||
row.push(item.into_string(", "))
|
row.push(item.into_string(", ", config))
|
||||||
} else {
|
} else {
|
||||||
for header in headers.iter().skip(1) {
|
for header in headers.iter().skip(1) {
|
||||||
let result = match item {
|
let result = match item {
|
||||||
@ -147,7 +149,7 @@ fn convert_to_table(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(value) => row.push(value.into_string(", ")),
|
Ok(value) => row.push(value.into_string(", ", config)),
|
||||||
Err(_) => row.push(String::new()),
|
Err(_) => row.push(String::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,9 +187,24 @@ fn convert_to_table(
|
|||||||
.collect::<Vec<StyledString>>()
|
.collect::<Vec<StyledString>>()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
theme: nu_table::Theme::rounded(),
|
theme: load_theme_from_config(config),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_theme_from_config(config: &Config) -> Theme {
|
||||||
|
match config.table_mode.as_str() {
|
||||||
|
"basic" => nu_table::Theme::basic(),
|
||||||
|
"compact" => nu_table::Theme::compact(),
|
||||||
|
"compact_double" => nu_table::Theme::compact_double(),
|
||||||
|
"light" => nu_table::Theme::light(),
|
||||||
|
"with_love" => nu_table::Theme::with_love(),
|
||||||
|
"rounded" => nu_table::Theme::rounded(),
|
||||||
|
"reinforced" => nu_table::Theme::reinforced(),
|
||||||
|
"heavy" => nu_table::Theme::heavy(),
|
||||||
|
"none" => nu_table::Theme::none(),
|
||||||
|
_ => nu_table::Theme::rounded(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -429,7 +429,9 @@ pub fn eval_subexpression(
|
|||||||
// to be used later
|
// to be used later
|
||||||
// FIXME: the trimming of the end probably needs to live in a better place
|
// FIXME: the trimming of the end probably needs to live in a better place
|
||||||
|
|
||||||
let mut s = input.collect_string("");
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
|
let mut s = input.collect_string("", &config);
|
||||||
if s.ends_with('\n') {
|
if s.ends_with('\n') {
|
||||||
s.pop();
|
s.pop();
|
||||||
}
|
}
|
||||||
|
@ -95,14 +95,32 @@ impl FromValue for f64 {
|
|||||||
impl FromValue for String {
|
impl FromValue for String {
|
||||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||||
// FIXME: we may want to fail a little nicer here
|
// FIXME: we may want to fail a little nicer here
|
||||||
Ok(v.clone().into_string(", "))
|
match v {
|
||||||
|
Value::CellPath { val, .. } => Ok(val.into_string()),
|
||||||
|
Value::String { val, .. } => Ok(val.clone()),
|
||||||
|
v => Err(ShellError::CantConvert(
|
||||||
|
"string".into(),
|
||||||
|
v.get_type().to_string(),
|
||||||
|
v.span()?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromValue for Spanned<String> {
|
impl FromValue for Spanned<String> {
|
||||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||||
Ok(Spanned {
|
Ok(Spanned {
|
||||||
item: v.clone().into_string(", "),
|
item: match v {
|
||||||
|
Value::CellPath { val, .. } => val.into_string(),
|
||||||
|
Value::String { val, .. } => val.clone(),
|
||||||
|
v => {
|
||||||
|
return Err(ShellError::CantConvert(
|
||||||
|
"string".into(),
|
||||||
|
v.get_type().to_string(),
|
||||||
|
v.span()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
span: v.span()?,
|
span: v.span()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Block, Call, Expr, Expression, ImportPattern, ImportPatternMember, Pipeline, Statement},
|
ast::{Block, Call, Expr, Expression, ImportPattern, ImportPatternMember, Pipeline, Statement},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
span, DeclId, Span, SyntaxShape, Type,
|
span, DeclId, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -800,8 +800,10 @@ pub fn parse_let(
|
|||||||
.expect("internal error: expected variable");
|
.expect("internal error: expected variable");
|
||||||
let rhs_type = call.positional[1].ty.clone();
|
let rhs_type = call.positional[1].ty.clone();
|
||||||
|
|
||||||
|
if var_id != CONFIG_VARIABLE_ID {
|
||||||
working_set.set_variable_type(var_id, rhs_type);
|
working_set.set_variable_type(var_id, rhs_type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
@ -12,6 +12,7 @@ use nu_protocol::{
|
|||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
span, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
span, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||||
|
CONFIG_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::parse_keywords::{
|
use crate::parse_keywords::{
|
||||||
@ -1201,6 +1202,16 @@ pub fn parse_variable_expr(
|
|||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
} else if contents == b"$config" {
|
||||||
|
return (
|
||||||
|
Expression {
|
||||||
|
expr: Expr::Var(nu_protocol::CONFIG_VARIABLE_ID),
|
||||||
|
span,
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (id, err) = parse_variable(working_set, span);
|
let (id, err) = parse_variable(working_set, span);
|
||||||
@ -1909,6 +1920,16 @@ pub fn parse_var_with_opt_type(
|
|||||||
Some(ParseError::MissingType(spans[*spans_idx])),
|
Some(ParseError::MissingType(spans[*spans_idx])),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if bytes == b"$config" || bytes == b"config" {
|
||||||
|
(
|
||||||
|
Expression {
|
||||||
|
expr: Expr::Var(CONFIG_VARIABLE_ID),
|
||||||
|
span: spans[*spans_idx],
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let id = working_set.add_variable(bytes, Type::Unknown);
|
let id = working_set.add_variable(bytes, Type::Unknown);
|
||||||
|
|
||||||
|
40
crates/nu-protocol/src/config.rs
Normal file
40
crates/nu-protocol/src/config.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{ShellError, Value};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
pub filesize_metric: bool,
|
||||||
|
pub table_mode: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
filesize_metric: false,
|
||||||
|
table_mode: "rounded".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn into_config(self) -> Result<Config, ShellError> {
|
||||||
|
let v = self.as_record()?;
|
||||||
|
|
||||||
|
let mut config = Config::default();
|
||||||
|
|
||||||
|
for (key, value) in v.0.iter().zip(v.1) {
|
||||||
|
match key.as_str() {
|
||||||
|
"filesize_metric" => {
|
||||||
|
config.filesize_metric = value.as_bool()?;
|
||||||
|
}
|
||||||
|
"table_mode" => {
|
||||||
|
config.table_mode = value.as_string()?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
}
|
@ -136,13 +136,14 @@ pub struct EngineState {
|
|||||||
pub const NU_VARIABLE_ID: usize = 0;
|
pub const NU_VARIABLE_ID: usize = 0;
|
||||||
pub const SCOPE_VARIABLE_ID: usize = 1;
|
pub const SCOPE_VARIABLE_ID: usize = 1;
|
||||||
pub const IN_VARIABLE_ID: usize = 2;
|
pub const IN_VARIABLE_ID: usize = 2;
|
||||||
|
pub const CONFIG_VARIABLE_ID: usize = 3;
|
||||||
|
|
||||||
impl EngineState {
|
impl EngineState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
files: im::vector![],
|
files: im::vector![],
|
||||||
file_contents: im::vector![],
|
file_contents: im::vector![],
|
||||||
vars: im::vector![Type::Unknown, Type::Unknown, Type::Unknown],
|
vars: im::vector![Type::Unknown, Type::Unknown, Type::Unknown, Type::Unknown],
|
||||||
decls: im::vector![],
|
decls: im::vector![],
|
||||||
blocks: im::vector![],
|
blocks: im::vector![],
|
||||||
scope: im::vector![ScopeFrame::new()],
|
scope: im::vector![ScopeFrame::new()],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{ShellError, Value, VarId};
|
use crate::{Config, ShellError, Value, VarId, CONFIG_VARIABLE_ID};
|
||||||
|
|
||||||
/// A runtime value stack used during evaluation
|
/// A runtime value stack used during evaluation
|
||||||
///
|
///
|
||||||
@ -42,6 +42,7 @@ impl Stack {
|
|||||||
if let Some(v) = self.vars.get(&var_id) {
|
if let Some(v) = self.vars.get(&var_id) {
|
||||||
return Ok(v.clone());
|
return Ok(v.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ShellError::InternalError("variable not found".into()))
|
Err(ShellError::InternalError("variable not found".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +68,11 @@ impl Stack {
|
|||||||
// FIXME: this is probably slow
|
// FIXME: this is probably slow
|
||||||
output.env_vars = self.env_vars.clone();
|
output.env_vars = self.env_vars.clone();
|
||||||
|
|
||||||
|
let config = self
|
||||||
|
.get_var(CONFIG_VARIABLE_ID)
|
||||||
|
.expect("internal error: config is missing");
|
||||||
|
output.vars.insert(CONFIG_VARIABLE_ID, config);
|
||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +87,18 @@ impl Stack {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_config(&self) -> Result<Config, ShellError> {
|
||||||
|
let config = self.get_var(CONFIG_VARIABLE_ID);
|
||||||
|
|
||||||
|
match config {
|
||||||
|
Ok(config) => config.into_config(),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Can't find {} in {:?}", CONFIG_VARIABLE_ID, self);
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_stack(&self) {
|
pub fn print_stack(&self) {
|
||||||
println!("vars:");
|
println!("vars:");
|
||||||
for (var, val) in &self.vars {
|
for (var, val) in &self.vars {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
mod config;
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
mod example;
|
mod example;
|
||||||
mod id;
|
mod id;
|
||||||
@ -11,7 +12,8 @@ mod ty;
|
|||||||
mod value;
|
mod value;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
|
|
||||||
pub use engine::{IN_VARIABLE_ID, NU_VARIABLE_ID, SCOPE_VARIABLE_ID};
|
pub use config::*;
|
||||||
|
pub use engine::{CONFIG_VARIABLE_ID, IN_VARIABLE_ID, NU_VARIABLE_ID, SCOPE_VARIABLE_ID};
|
||||||
pub use example::*;
|
pub use example::*;
|
||||||
pub use id::*;
|
pub use id::*;
|
||||||
pub use pipeline_data::*;
|
pub use pipeline_data::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::sync::{atomic::AtomicBool, Arc};
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
|
|
||||||
use crate::{ast::PathMember, ShellError, Span, Value, ValueStream};
|
use crate::{ast::PathMember, Config, ShellError, Span, Value, ValueStream};
|
||||||
|
|
||||||
/// The foundational abstraction for input and output to commands
|
/// The foundational abstraction for input and output to commands
|
||||||
///
|
///
|
||||||
@ -51,10 +51,10 @@ impl PipelineData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_string(self, separator: &str) -> String {
|
pub fn collect_string(self, separator: &str, config: &Config) -> String {
|
||||||
match self {
|
match self {
|
||||||
PipelineData::Value(v) => v.into_string(separator),
|
PipelineData::Value(v) => v.into_string(separator, config),
|
||||||
PipelineData::Stream(s) => s.into_string(separator),
|
PipelineData::Stream(s) => s.into_string(separator, config),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ pub enum ShellError {
|
|||||||
#[diagnostic(code(nu::shell::internal_error), url(docsrs))]
|
#[diagnostic(code(nu::shell::internal_error), url(docsrs))]
|
||||||
InternalError(String),
|
InternalError(String),
|
||||||
|
|
||||||
#[error("Variable not found")]
|
#[error("Variable not found!!!")]
|
||||||
#[diagnostic(code(nu::shell::variable_not_found), url(docsrs))]
|
#[diagnostic(code(nu::shell::variable_not_found), url(docsrs))]
|
||||||
VariableNotFoundAtRuntime(#[label = "variable not found"] Span),
|
VariableNotFoundAtRuntime(#[label = "variable not found"] Span),
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use std::collections::HashMap;
|
|||||||
use std::{cmp::Ordering, fmt::Debug};
|
use std::{cmp::Ordering, fmt::Debug};
|
||||||
|
|
||||||
use crate::ast::{CellPath, PathMember};
|
use crate::ast::{CellPath, PathMember};
|
||||||
use crate::{did_you_mean, span, BlockId, Span, Spanned, Type};
|
use crate::{did_you_mean, span, BlockId, Config, Span, Spanned, Type};
|
||||||
|
|
||||||
use crate::ShellError;
|
use crate::ShellError;
|
||||||
|
|
||||||
@ -106,6 +106,28 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_record(&self) -> Result<(&[String], &[Value]), ShellError> {
|
||||||
|
match self {
|
||||||
|
Value::Record { cols, vals, .. } => Ok((cols, vals)),
|
||||||
|
x => Err(ShellError::CantConvert(
|
||||||
|
"record".into(),
|
||||||
|
x.get_type().to_string(),
|
||||||
|
self.span()?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bool(&self) -> Result<bool, ShellError> {
|
||||||
|
match self {
|
||||||
|
Value::Bool { val, .. } => Ok(*val),
|
||||||
|
x => Err(ShellError::CantConvert(
|
||||||
|
"boolean".into(),
|
||||||
|
x.get_type().to_string(),
|
||||||
|
self.span()?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the span for the current value
|
/// Get the span for the current value
|
||||||
pub fn span(&self) -> Result<Span, ShellError> {
|
pub fn span(&self) -> Result<Span, ShellError> {
|
||||||
match self {
|
match self {
|
||||||
@ -174,26 +196,26 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Value into string. Note that Streams will be consumed.
|
/// Convert Value into string. Note that Streams will be consumed.
|
||||||
pub fn into_string(self, separator: &str) -> String {
|
pub fn into_string(self, separator: &str, config: &Config) -> String {
|
||||||
match self {
|
match self {
|
||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format_filesize(val),
|
Value::Filesize { val, .. } => format_filesize(val, config),
|
||||||
Value::Duration { val, .. } => format_duration(val),
|
Value::Duration { val, .. } => format_duration(val),
|
||||||
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
"{}..{}",
|
"{}..{}",
|
||||||
val.from.into_string(", "),
|
val.from.into_string(", ", config),
|
||||||
val.to.into_string(", ")
|
val.to.into_string(", ", config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
Value::List { vals: val, .. } => format!(
|
Value::List { vals: val, .. } => format!(
|
||||||
"[{}]",
|
"[{}]",
|
||||||
val.into_iter()
|
val.into_iter()
|
||||||
.map(|x| x.into_string(", "))
|
.map(|x| x.into_string(", ", config))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
@ -201,7 +223,7 @@ impl Value {
|
|||||||
"{{{}}}",
|
"{{{}}}",
|
||||||
cols.iter()
|
cols.iter()
|
||||||
.zip(vals.iter())
|
.zip(vals.iter())
|
||||||
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ")))
|
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
@ -214,26 +236,26 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Value into string. Note that Streams will be consumed.
|
/// Convert Value into string. Note that Streams will be consumed.
|
||||||
pub fn debug_string(self, separator: &str) -> String {
|
pub fn debug_string(self, separator: &str, config: &Config) -> String {
|
||||||
match self {
|
match self {
|
||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format_filesize(val),
|
Value::Filesize { val, .. } => format_filesize(val, config),
|
||||||
Value::Duration { val, .. } => format_duration(val),
|
Value::Duration { val, .. } => format_duration(val),
|
||||||
Value::Date { val, .. } => format!("{:?}", val),
|
Value::Date { val, .. } => format!("{:?}", val),
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
"{}..{}",
|
"{}..{}",
|
||||||
val.from.into_string(", "),
|
val.from.into_string(", ", config),
|
||||||
val.to.into_string(", ")
|
val.to.into_string(", ", config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
Value::List { vals: val, .. } => format!(
|
Value::List { vals: val, .. } => format!(
|
||||||
"[{}]",
|
"[{}]",
|
||||||
val.into_iter()
|
val.into_iter()
|
||||||
.map(|x| x.into_string(", "))
|
.map(|x| x.into_string(", ", config))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
@ -241,7 +263,7 @@ impl Value {
|
|||||||
"{{{}}}",
|
"{{{}}}",
|
||||||
cols.iter()
|
cols.iter()
|
||||||
.zip(vals.iter())
|
.zip(vals.iter())
|
||||||
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ")))
|
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
@ -1171,14 +1193,14 @@ pub fn format_duration(duration: i64) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_filesize(num_bytes: i64) -> String {
|
fn format_filesize(num_bytes: i64, config: &Config) -> String {
|
||||||
let byte = byte_unit::Byte::from_bytes(num_bytes as u128);
|
let byte = byte_unit::Byte::from_bytes(num_bytes as u128);
|
||||||
|
|
||||||
if byte.get_bytes() == 0u128 {
|
if byte.get_bytes() == 0u128 {
|
||||||
return "—".to_string();
|
return "—".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let byte = byte.get_appropriate_unit(false);
|
let byte = byte.get_appropriate_unit(config.filesize_metric);
|
||||||
|
|
||||||
match byte.get_unit() {
|
match byte.get_unit() {
|
||||||
byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()),
|
byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()),
|
||||||
|
@ -6,7 +6,7 @@ use crate::*;
|
|||||||
pub struct RowStream(Rc<RefCell<dyn Iterator<Item = Vec<Value>>>>);
|
pub struct RowStream(Rc<RefCell<dyn Iterator<Item = Vec<Value>>>>);
|
||||||
|
|
||||||
impl RowStream {
|
impl RowStream {
|
||||||
pub fn into_string(self, headers: Vec<String>) -> String {
|
pub fn into_string(self, headers: Vec<String>, config: &Config) -> String {
|
||||||
format!(
|
format!(
|
||||||
"[{}]\n[{}]",
|
"[{}]\n[{}]",
|
||||||
headers
|
headers
|
||||||
@ -16,7 +16,7 @@ impl RowStream {
|
|||||||
.join(", "),
|
.join(", "),
|
||||||
self.map(|x: Vec<Value>| {
|
self.map(|x: Vec<Value>| {
|
||||||
x.into_iter()
|
x.into_iter()
|
||||||
.map(|x| x.into_string(", "))
|
.map(|x| x.into_string(", ", config))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
})
|
})
|
||||||
|
@ -19,8 +19,8 @@ pub struct ValueStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ValueStream {
|
impl ValueStream {
|
||||||
pub fn into_string(self, separator: &str) -> String {
|
pub fn into_string(self, separator: &str, config: &Config) -> String {
|
||||||
self.map(|x: Value| x.into_string(", "))
|
self.map(|x: Value| x.into_string(", ", config))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
}
|
}
|
||||||
|
54
src/main.rs
54
src/main.rs
@ -19,7 +19,7 @@ use nu_parser::parse;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
IntoPipelineData, PipelineData, ShellError, Span, Value,
|
IntoPipelineData, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
|
use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ impl CompletionActionHandler for FuzzyCompletion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
miette::set_panic_hook();
|
// miette::set_panic_hook();
|
||||||
let miette_hook = std::panic::take_hook();
|
let miette_hook = std::panic::take_hook();
|
||||||
std::panic::set_hook(Box::new(move |x| {
|
std::panic::set_hook(Box::new(move |x| {
|
||||||
crossterm::terminal::disable_raw_mode().unwrap();
|
crossterm::terminal::disable_raw_mode().unwrap();
|
||||||
@ -126,6 +126,16 @@ fn main() -> Result<()> {
|
|||||||
stack.env_vars.insert(k, v);
|
stack.env_vars.insert(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up our initial config to start from
|
||||||
|
stack.vars.insert(
|
||||||
|
CONFIG_VARIABLE_ID,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec![],
|
||||||
|
vals: vec![],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
match eval_block(
|
match eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
@ -133,7 +143,8 @@ fn main() -> Result<()> {
|
|||||||
PipelineData::new(Span::unknown()),
|
PipelineData::new(Span::unknown()),
|
||||||
) {
|
) {
|
||||||
Ok(pipeline_data) => {
|
Ok(pipeline_data) => {
|
||||||
println!("{}", pipeline_data.collect_string("\n"));
|
let config = stack.get_config()?;
|
||||||
|
println!("{}", pipeline_data.collect_string("\n", &config));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(&engine_state);
|
let working_set = StateWorkingSet::new(&engine_state);
|
||||||
@ -159,6 +170,16 @@ fn main() -> Result<()> {
|
|||||||
stack.env_vars.insert(k, v);
|
stack.env_vars.insert(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up our initial config to start from
|
||||||
|
stack.vars.insert(
|
||||||
|
CONFIG_VARIABLE_ID,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec![],
|
||||||
|
vals: vec![],
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Load config startup file
|
// Load config startup file
|
||||||
if let Some(mut config_path) = nu_path::config_dir() {
|
if let Some(mut config_path) = nu_path::config_dir() {
|
||||||
config_path.push("nushell");
|
config_path.push("nushell");
|
||||||
@ -261,21 +282,27 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_value(value: Value, engine_state: &EngineState) -> Result<(), ShellError> {
|
fn print_value(
|
||||||
|
value: Value,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
) -> Result<(), ShellError> {
|
||||||
// If the table function is in the declarations, then we can use it
|
// If the table function is in the declarations, then we can use it
|
||||||
// to create the table value that will be printed in the terminal
|
// to create the table value that will be printed in the terminal
|
||||||
|
|
||||||
|
let config = stack.get_config()?;
|
||||||
|
|
||||||
let output = match engine_state.find_decl("table".as_bytes()) {
|
let output = match engine_state.find_decl("table".as_bytes()) {
|
||||||
Some(decl_id) => {
|
Some(decl_id) => {
|
||||||
let mut stack = Stack::new();
|
|
||||||
let table = engine_state.get_decl(decl_id).run(
|
let table = engine_state.get_decl(decl_id).run(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
stack,
|
||||||
&Call::new(),
|
&Call::new(),
|
||||||
value.into_pipeline_data(),
|
value.into_pipeline_data(),
|
||||||
)?;
|
)?;
|
||||||
table.collect_string("\n")
|
table.collect_string("\n", &config)
|
||||||
}
|
}
|
||||||
None => value.into_string(", "),
|
None => value.into_string(", ", &config),
|
||||||
};
|
};
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
|
|
||||||
@ -323,7 +350,10 @@ fn update_prompt<'prompt>(
|
|||||||
&block,
|
&block,
|
||||||
PipelineData::new(Span::unknown()),
|
PipelineData::new(Span::unknown()),
|
||||||
) {
|
) {
|
||||||
Ok(pipeline_data) => pipeline_data.collect_string(""),
|
Ok(pipeline_data) => {
|
||||||
|
let config = stack.get_config().unwrap_or_default();
|
||||||
|
pipeline_data.collect_string("", &config)
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
@ -366,7 +396,11 @@ fn eval_source(
|
|||||||
PipelineData::new(Span::unknown()),
|
PipelineData::new(Span::unknown()),
|
||||||
) {
|
) {
|
||||||
Ok(pipeline_data) => {
|
Ok(pipeline_data) => {
|
||||||
if let Err(err) = print_value(pipeline_data.into_value(Span::unknown()), engine_state) {
|
if let Err(err) = print_value(
|
||||||
|
pipeline_data.into_value(Span::unknown()),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
|
18
src/tests.rs
18
src/tests.rs
@ -899,3 +899,21 @@ fn record_1() -> TestResult {
|
|||||||
fn record_2() -> TestResult {
|
fn record_2() -> TestResult {
|
||||||
run_test(r#"{'b': 'c'}.b"#, "c")
|
run_test(r#"{'b': 'c'}.b"#, "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_var_1() -> TestResult {
|
||||||
|
// Note: this tests both the config variable and that it is properly captured into a block
|
||||||
|
run_test(
|
||||||
|
r#"let config = {"filesize_metric": $true }; do { 40kb | into string } "#,
|
||||||
|
"39.1 KiB",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_var_2() -> TestResult {
|
||||||
|
// Note: this tests both the config variable and that it is properly captured into a block
|
||||||
|
run_test(
|
||||||
|
r#"let config = {"filesize_metric": $false }; do { 40kb | into string } "#,
|
||||||
|
"40.0 KB",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user