move scope variable into nu variable (#4725)

This commit is contained in:
JT 2022-03-04 11:36:11 -05:00 committed by GitHub
parent eef3de2d05
commit e64ca97fe2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 349 additions and 355 deletions

View File

@ -86,7 +86,7 @@ impl NuCompleter {
) -> Vec<(reedline::Span, String)> { ) -> Vec<(reedline::Span, String)> {
let mut output = vec![]; let mut output = vec![];
let builtins = ["$nu", "$scope", "$in", "$config", "$env", "$nothing"]; let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
for builtin in builtins { for builtin in builtins {
if builtin.as_bytes().starts_with(prefix) { if builtin.as_bytes().starts_with(prefix) {

View File

@ -672,6 +672,343 @@ pub fn eval_subexpression(
Ok(input) Ok(input)
} }
pub fn create_scope(
engine_state: &EngineState,
stack: &Stack,
span: Span,
) -> Result<Value, ShellError> {
let mut output_cols = vec![];
let mut output_vals = vec![];
let mut vars = vec![];
let mut commands = vec![];
let mut aliases = vec![];
let mut overlays = vec![];
for frame in &engine_state.scope {
for var in &frame.vars {
let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span);
let var_type = Value::string(engine_state.get_var(*var.1).to_string(), span);
let var_value = if let Ok(val) = stack.get_var(*var.1, span) {
val
} else {
Value::nothing(span)
};
vars.push(Value::Record {
cols: vec!["name".to_string(), "type".to_string(), "value".to_string()],
vals: vec![var_name, var_type, var_value],
span,
})
}
for command in &frame.decls {
let mut cols = vec![];
let mut vals = vec![];
cols.push("command".into());
vals.push(Value::String {
val: String::from_utf8_lossy(command.0).to_string(),
span,
});
let decl = engine_state.get_decl(*command.1);
let signature = decl.signature();
cols.push("category".to_string());
vals.push(Value::String {
val: signature.category.to_string(),
span,
});
// signature
let mut sig_records = vec![];
{
let sig_cols = vec![
"command".to_string(),
"parameter_name".to_string(),
"parameter_type".to_string(),
"syntax_shape".to_string(),
"is_optional".to_string(),
"short_flag".to_string(),
"description".to_string(),
];
// required_positional
for req in signature.required_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(req.name, span),
Value::string("positional", span),
Value::string(req.shape.to_string(), span),
Value::boolean(false, span),
Value::nothing(span),
Value::string(req.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
// optional_positional
for opt in signature.optional_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(opt.name, span),
Value::string("positional", span),
Value::string(opt.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(opt.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
{
// rest_positional
if let Some(rest) = signature.rest_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(rest.name, span),
Value::string("rest", span),
Value::string(rest.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(rest.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
// named flags
for named in signature.named {
let flag_type;
// Skip the help flag
if named.long == "help" {
continue;
}
let shape = if let Some(arg) = named.arg {
flag_type = Value::string("named", span);
Value::string(arg.to_string(), span)
} else {
flag_type = Value::string("switch", span);
Value::nothing(span)
};
let short_flag = if let Some(c) = named.short {
Value::string(c, span)
} else {
Value::nothing(span)
};
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(named.long, span),
flag_type,
shape,
Value::boolean(!named.required, span),
short_flag,
Value::string(named.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
cols.push("signature".to_string());
vals.push(Value::List {
vals: sig_records,
span,
});
cols.push("usage".to_string());
vals.push(Value::String {
val: decl.usage().into(),
span,
});
cols.push("examples".to_string());
vals.push(Value::List {
vals: decl
.examples()
.into_iter()
.map(|x| Value::Record {
cols: vec!["description".into(), "example".into()],
vals: vec![
Value::String {
val: x.description.to_string(),
span,
},
Value::String {
val: x.example.to_string(),
span,
},
],
span,
})
.collect(),
span,
});
cols.push("is_binary".to_string());
vals.push(Value::Bool {
val: decl.is_binary(),
span,
});
cols.push("is_private".to_string());
vals.push(Value::Bool {
val: decl.is_private(),
span,
});
cols.push("is_builtin".to_string());
vals.push(Value::Bool {
val: decl.is_builtin(),
span,
});
cols.push("is_sub".to_string());
vals.push(Value::Bool {
val: decl.is_sub(),
span,
});
cols.push("is_plugin".to_string());
vals.push(Value::Bool {
val: decl.is_plugin().is_some(),
span,
});
cols.push("is_custom".to_string());
vals.push(Value::Bool {
val: decl.get_block_id().is_some(),
span,
});
cols.push("is_extern".to_string());
vals.push(Value::Bool {
val: decl.is_known_external(),
span,
});
cols.push("creates_scope".to_string());
vals.push(Value::Bool {
val: signature.creates_scope,
span,
});
cols.push("extra_usage".to_string());
vals.push(Value::String {
val: decl.extra_usage().into(),
span,
});
commands.push(Value::Record { cols, vals, span })
}
for (alias_name, alias_id) in &frame.aliases {
let alias = engine_state.get_alias(*alias_id);
let mut alias_text = String::new();
for span in alias {
let contents = engine_state.get_span_contents(span);
if !alias_text.is_empty() {
alias_text.push(' ');
}
alias_text.push_str(&String::from_utf8_lossy(contents).to_string());
}
aliases.push((
Value::String {
val: String::from_utf8_lossy(alias_name).to_string(),
span,
},
Value::string(alias_text, span),
));
}
for overlay in &frame.overlays {
overlays.push(Value::String {
val: String::from_utf8_lossy(overlay.0).to_string(),
span,
});
}
}
output_cols.push("vars".to_string());
output_vals.push(Value::List { vals: vars, span });
commands.sort_by(|a, b| match (a, b) {
(Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => {
// Comparing the first value from the record
// It is expected that the first value is the name of the column
// The names of the commands should be a value string
match (rec_a.get(0), rec_b.get(0)) {
(Some(val_a), Some(val_b)) => match (val_a, val_b) {
(Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
str_a.cmp(str_b)
}
_ => Ordering::Equal,
},
_ => Ordering::Equal,
}
}
_ => Ordering::Equal,
});
output_cols.push("commands".to_string());
output_vals.push(Value::List {
vals: commands,
span,
});
aliases.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("aliases".to_string());
output_vals.push(Value::List {
vals: aliases
.into_iter()
.map(|(alias, value)| Value::Record {
cols: vec!["alias".into(), "expansion".into()],
vals: vec![alias, value],
span,
})
.collect(),
span,
});
overlays.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("overlays".to_string());
output_vals.push(Value::List {
vals: overlays,
span,
});
Ok(Value::Record {
cols: output_cols,
vals: output_vals,
span,
})
}
pub fn eval_variable( pub fn eval_variable(
engine_state: &EngineState, engine_state: &EngineState,
stack: &Stack, stack: &Stack,
@ -723,6 +1060,9 @@ pub fn eval_variable(
output_cols.push("cwd".into()); output_cols.push("cwd".into());
output_vals.push(Value::String { val: cwd, span }); output_vals.push(Value::String { val: cwd, span });
output_cols.push("scope".into());
output_vals.push(create_scope(engine_state, stack, span)?);
if let Some(home_path) = nu_path::home_dir() { if let Some(home_path) = nu_path::home_dir() {
if let Some(home_path_str) = home_path.to_str() { if let Some(home_path_str) = home_path.to_str() {
output_cols.push("home-path".into()); output_cols.push("home-path".into());
@ -748,339 +1088,6 @@ pub fn eval_variable(
span, span,
}) })
} }
nu_protocol::SCOPE_VARIABLE_ID => {
let mut output_cols = vec![];
let mut output_vals = vec![];
let mut vars = vec![];
let mut commands = vec![];
let mut aliases = vec![];
let mut overlays = vec![];
for frame in &engine_state.scope {
for var in &frame.vars {
let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span);
let var_type = Value::string(engine_state.get_var(*var.1).to_string(), span);
let var_value = if let Ok(val) = stack.get_var(*var.1, span) {
val
} else {
Value::nothing(span)
};
vars.push(Value::Record {
cols: vec!["name".to_string(), "type".to_string(), "value".to_string()],
vals: vec![var_name, var_type, var_value],
span,
})
}
for command in &frame.decls {
let mut cols = vec![];
let mut vals = vec![];
cols.push("command".into());
vals.push(Value::String {
val: String::from_utf8_lossy(command.0).to_string(),
span,
});
let decl = engine_state.get_decl(*command.1);
let signature = decl.signature();
cols.push("category".to_string());
vals.push(Value::String {
val: signature.category.to_string(),
span,
});
// signature
let mut sig_records = vec![];
{
let sig_cols = vec![
"command".to_string(),
"parameter_name".to_string(),
"parameter_type".to_string(),
"syntax_shape".to_string(),
"is_optional".to_string(),
"short_flag".to_string(),
"description".to_string(),
];
// required_positional
for req in signature.required_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(req.name, span),
Value::string("positional", span),
Value::string(req.shape.to_string(), span),
Value::boolean(false, span),
Value::nothing(span),
Value::string(req.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
// optional_positional
for opt in signature.optional_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(opt.name, span),
Value::string("positional", span),
Value::string(opt.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(opt.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
{
// rest_positional
if let Some(rest) = signature.rest_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(rest.name, span),
Value::string("rest", span),
Value::string(rest.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(rest.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
// named flags
for named in signature.named {
let flag_type;
// Skip the help flag
if named.long == "help" {
continue;
}
let shape = if let Some(arg) = named.arg {
flag_type = Value::string("named", span);
Value::string(arg.to_string(), span)
} else {
flag_type = Value::string("switch", span);
Value::nothing(span)
};
let short_flag = if let Some(c) = named.short {
Value::string(c, span)
} else {
Value::nothing(span)
};
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(named.long, span),
flag_type,
shape,
Value::boolean(!named.required, span),
short_flag,
Value::string(named.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
cols.push("signature".to_string());
vals.push(Value::List {
vals: sig_records,
span,
});
cols.push("usage".to_string());
vals.push(Value::String {
val: decl.usage().into(),
span,
});
cols.push("examples".to_string());
vals.push(Value::List {
vals: decl
.examples()
.into_iter()
.map(|x| Value::Record {
cols: vec!["description".into(), "example".into()],
vals: vec![
Value::String {
val: x.description.to_string(),
span,
},
Value::String {
val: x.example.to_string(),
span,
},
],
span,
})
.collect(),
span,
});
cols.push("is_binary".to_string());
vals.push(Value::Bool {
val: decl.is_binary(),
span,
});
cols.push("is_private".to_string());
vals.push(Value::Bool {
val: decl.is_private(),
span,
});
cols.push("is_builtin".to_string());
vals.push(Value::Bool {
val: decl.is_builtin(),
span,
});
cols.push("is_sub".to_string());
vals.push(Value::Bool {
val: decl.is_sub(),
span,
});
cols.push("is_plugin".to_string());
vals.push(Value::Bool {
val: decl.is_plugin().is_some(),
span,
});
cols.push("is_custom".to_string());
vals.push(Value::Bool {
val: decl.get_block_id().is_some(),
span,
});
cols.push("is_extern".to_string());
vals.push(Value::Bool {
val: decl.is_known_external(),
span,
});
cols.push("creates_scope".to_string());
vals.push(Value::Bool {
val: signature.creates_scope,
span,
});
cols.push("extra_usage".to_string());
vals.push(Value::String {
val: decl.extra_usage().into(),
span,
});
commands.push(Value::Record { cols, vals, span })
}
for (alias_name, alias_id) in &frame.aliases {
let alias = engine_state.get_alias(*alias_id);
let mut alias_text = String::new();
for span in alias {
let contents = engine_state.get_span_contents(span);
if !alias_text.is_empty() {
alias_text.push(' ');
}
alias_text.push_str(&String::from_utf8_lossy(contents).to_string());
}
aliases.push((
Value::String {
val: String::from_utf8_lossy(alias_name).to_string(),
span,
},
Value::string(alias_text, span),
));
}
for overlay in &frame.overlays {
overlays.push(Value::String {
val: String::from_utf8_lossy(overlay.0).to_string(),
span,
});
}
}
output_cols.push("vars".to_string());
output_vals.push(Value::List { vals: vars, span });
commands.sort_by(|a, b| match (a, b) {
(Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => {
// Comparing the first value from the record
// It is expected that the first value is the name of the column
// The names of the commands should be a value string
match (rec_a.get(0), rec_b.get(0)) {
(Some(val_a), Some(val_b)) => match (val_a, val_b) {
(
Value::String { val: str_a, .. },
Value::String { val: str_b, .. },
) => str_a.cmp(str_b),
_ => Ordering::Equal,
},
_ => Ordering::Equal,
}
}
_ => Ordering::Equal,
});
output_cols.push("commands".to_string());
output_vals.push(Value::List {
vals: commands,
span,
});
aliases.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("aliases".to_string());
output_vals.push(Value::List {
vals: aliases
.into_iter()
.map(|(alias, value)| Value::Record {
cols: vec!["alias".into(), "expansion".into()],
vals: vec![alias, value],
span,
})
.collect(),
span,
});
overlays.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("overlays".to_string());
output_vals.push(Value::List {
vals: overlays,
span,
});
Ok(Value::Record {
cols: output_cols,
vals: output_vals,
span,
})
}
ENV_VARIABLE_ID => { ENV_VARIABLE_ID => {
let env_vars = stack.get_env_vars(engine_state); let env_vars = stack.get_env_vars(engine_state);
let env_columns = env_vars.keys(); let env_columns = env_vars.keys();

View File

@ -1556,16 +1556,6 @@ pub fn parse_variable_expr(
}, },
None, None,
); );
} else if contents == b"$scope" {
return (
Expression {
expr: Expr::Var(nu_protocol::SCOPE_VARIABLE_ID),
span,
ty: Type::Unknown,
custom_completion: None,
},
None,
);
} else if contents == b"$in" { } else if contents == b"$in" {
return ( return (
Expression { Expression {

View File

@ -175,10 +175,9 @@ 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 IN_VARIABLE_ID: usize = 1;
pub const IN_VARIABLE_ID: usize = 2; pub const CONFIG_VARIABLE_ID: usize = 2;
pub const CONFIG_VARIABLE_ID: usize = 3; pub const ENV_VARIABLE_ID: usize = 3;
pub const ENV_VARIABLE_ID: usize = 4;
// NOTE: If you add more to this list, make sure to update the > checks based on the last in the list // NOTE: If you add more to this list, make sure to update the > checks based on the last in the list
impl EngineState { impl EngineState {

View File

@ -15,9 +15,7 @@ mod value;
pub use value::Value; pub use value::Value;
pub use config::*; pub use config::*;
pub use engine::{ pub use engine::{CONFIG_VARIABLE_ID, ENV_VARIABLE_ID, IN_VARIABLE_ID, NU_VARIABLE_ID};
CONFIG_VARIABLE_ID, ENV_VARIABLE_ID, IN_VARIABLE_ID, NU_VARIABLE_ID, SCOPE_VARIABLE_ID,
};
pub use example::*; pub use example::*;
pub use exportable::*; pub use exportable::*;
pub use id::*; pub use id::*;

View File

@ -25,7 +25,7 @@ You can say that the module `greetings` exports an overlay which consists of two
By itself, the module does not do anything. By itself, the module does not do anything.
We can verify its existence by printing all available overlays: We can verify its existence by printing all available overlays:
``` ```
> $scope.overlays > $nu.scope.overlays
╭───┬───────────╮ ╭───┬───────────╮
│ 0 │ greetings │ │ 0 │ greetings │
╰───┴───────────╯ ╰───┴───────────╯
@ -306,7 +306,7 @@ It creates the `$config` variable using the module system.
## Known Issues ## Known Issues
* It might be more appropriate to use `$scope.modules` instead of `$scope.overlays` * It might be more appropriate to use `$nu.scope.modules` instead of `$nu.scope.overlays`
## Future Design Ideas ## Future Design Ideas

View File

@ -1,6 +1,6 @@
let vers = (version).version let vers = (version).version
for command in ($scope.commands | where is_custom == false && is_extern == false) { for command in ($nu.scope.commands | where is_custom == false && is_extern == false) {
let top = $"--- let top = $"---
title: ($command.command) title: ($command.command)
layout: command layout: command

View File

@ -78,7 +78,7 @@ fn help_works_with_missing_requirements() -> TestResult {
#[test] #[test]
fn scope_variable() -> TestResult { fn scope_variable() -> TestResult {
run_test( run_test(
r#"let x = 3; $scope.vars | where name == "$x" | get type.0"#, r#"let x = 3; $nu.scope.vars | where name == "$x" | get type.0"#,
"int", "int",
) )
} }