Make env var eval order during "use" deterministic (#742)

* Make env var eval order during "use" deterministic

Fixes #726.

* Merge delta after getting config

To make sure env vars are all in the engine state and not in the stack.
This commit is contained in:
Jakub Žádník 2022-01-14 23:06:32 +02:00 committed by GitHub
parent 7c23ae5cb0
commit 40484966c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 36 deletions

View File

@ -109,8 +109,6 @@ impl<'call> ExternalCommand<'call> {
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
// TODO. We don't have a way to know the current directory
// This should be information from the EvaluationContex or EngineState
let mut process = if let Some(d) = self.env_vars.get("PWD") { let mut process = if let Some(d) = self.env_vars.get("PWD") {
let mut process = self.create_command(d); let mut process = self.create_command(d);
process.current_dir(d); process.current_dir(d);

View File

@ -7,7 +7,7 @@ use nu_protocol::{
engine::StateWorkingSet, engine::StateWorkingSet,
span, Exportable, Overlay, PositionalArg, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID, span, Exportable, Overlay, PositionalArg, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID,
}; };
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
use crate::{ use crate::{
lex, lite_parse, lex, lite_parse,
@ -1074,25 +1074,13 @@ pub fn parse_hide(
} else if import_pattern.members.is_empty() { } else if import_pattern.members.is_empty() {
// The pattern head can be e.g. a function name, not just a module // The pattern head can be e.g. a function name, not just a module
if let Some(id) = working_set.find_decl(&import_pattern.head.name) { if let Some(id) = working_set.find_decl(&import_pattern.head.name) {
let mut decls = HashMap::new(); let mut overlay = Overlay::new();
decls.insert(import_pattern.head.name.clone(), id); overlay.add_decl(&import_pattern.head.name, id);
( (false, overlay)
false,
Overlay {
decls,
env_vars: HashMap::new(),
},
)
} else { } else {
// Or it could be an env var // Or it could be an env var
( (false, Overlay::new())
false,
Overlay {
decls: HashMap::new(),
env_vars: HashMap::new(),
},
)
} }
} else { } else {
return ( return (

View File

@ -586,7 +586,27 @@ pub struct StateDelta {
plugins_changed: bool, // marks whether plugin file should be updated plugins_changed: bool, // marks whether plugin file should be updated
} }
impl Default for StateDelta {
fn default() -> Self {
Self::new()
}
}
impl StateDelta { impl StateDelta {
pub fn new() -> Self {
StateDelta {
files: vec![],
file_contents: vec![],
vars: vec![],
decls: vec![],
blocks: vec![],
overlays: vec![],
scope: vec![ScopeFrame::new()],
#[cfg(feature = "plugin")]
plugins_changed: false,
}
}
pub fn num_files(&self) -> usize { pub fn num_files(&self) -> usize {
self.files.len() self.files.len()
} }
@ -615,17 +635,7 @@ impl StateDelta {
impl<'a> StateWorkingSet<'a> { impl<'a> StateWorkingSet<'a> {
pub fn new(permanent_state: &'a EngineState) -> Self { pub fn new(permanent_state: &'a EngineState) -> Self {
Self { Self {
delta: StateDelta { delta: StateDelta::new(),
files: vec![],
file_contents: vec![],
vars: vec![],
decls: vec![],
blocks: vec![],
overlays: vec![],
scope: vec![ScopeFrame::new()],
#[cfg(feature = "plugin")]
plugins_changed: false,
},
permanent_state, permanent_state,
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{BlockId, DeclId}; use crate::{BlockId, DeclId};
use std::collections::HashMap; use indexmap::IndexMap;
// TODO: Move the import pattern matching logic here from use/hide commands and // TODO: Move the import pattern matching logic here from use/hide commands and
// parse_use/parse_hide // parse_use/parse_hide
@ -8,15 +8,15 @@ use std::collections::HashMap;
/// Collection of definitions that can be exported from a module /// Collection of definitions that can be exported from a module
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Overlay { pub struct Overlay {
pub decls: HashMap<Vec<u8>, DeclId>, pub decls: IndexMap<Vec<u8>, DeclId>,
pub env_vars: HashMap<Vec<u8>, BlockId>, pub env_vars: IndexMap<Vec<u8>, BlockId>,
} }
impl Overlay { impl Overlay {
pub fn new() -> Self { pub fn new() -> Self {
Overlay { Overlay {
decls: HashMap::new(), decls: IndexMap::new(),
env_vars: HashMap::new(), env_vars: IndexMap::new(),
} }
} }

View File

@ -15,7 +15,7 @@ use nu_engine::{convert_env_values, eval_block};
use nu_parser::{lex, parse, trim_quotes, Token, TokenContents}; use nu_parser::{lex, parse, trim_quotes, Token, TokenContents};
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateDelta, StateWorkingSet},
Config, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, Config, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID,
}; };
use reedline::{ use reedline::{
@ -167,6 +167,20 @@ fn main() -> Result<()> {
} }
}; };
// Merge the delta in case env vars changed in the config
match nu_engine::env::current_dir(&engine_state, &stack) {
Ok(cwd) => {
if let Err(e) = engine_state.merge_delta(StateDelta::new(), Some(&mut stack), cwd) {
let working_set = StateWorkingSet::new(&engine_state);
report_error(&working_set, &e);
}
}
Err(e) => {
let working_set = StateWorkingSet::new(&engine_state);
report_error(&working_set, &e);
}
}
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(&mut engine_state, &stack, &config) { if let Some(e) = convert_env_values(&mut engine_state, &stack, &config) {
let working_set = StateWorkingSet::new(&engine_state); let working_set = StateWorkingSet::new(&engine_state);
@ -314,6 +328,23 @@ fn main() -> Result<()> {
if let Ok(contents) = std::fs::read_to_string(&config_path) { if let Ok(contents) = std::fs::read_to_string(&config_path) {
eval_source(&mut engine_state, &mut stack, &contents, &config_filename); eval_source(&mut engine_state, &mut stack, &contents, &config_filename);
// Merge the delta in case env vars changed in the config
match nu_engine::env::current_dir(&engine_state, &stack) {
Ok(cwd) => {
if let Err(e) = engine_state.merge_delta(
StateDelta::new(),
Some(&mut stack),
cwd,
) {
let working_set = StateWorkingSet::new(&engine_state);
report_error(&working_set, &e);
}
}
Err(e) => {
let working_set = StateWorkingSet::new(&engine_state);
report_error(&working_set, &e);
}
}
} }
} }
} }