Update config directly at assignment (#13332)

# Description

Allows `Stack` to have a modified local `Config`, which is updated
immediately when `$env.config` is assigned to. This means that even
within a script, commands that come after `$env.config` changes will
always see those changes in `Stack::get_config()`.

Also fixed a lot of cases where `engine_state.get_config()` was used
even when `Stack` was available.

Closes #13324.

# User-Facing Changes
- Config changes apply immediately after the assignment is executed,
rather than whenever config is read by a command that needs it.
- Potentially slower performance when executing a lot of lines that
change `$env.config` one after another. Recommended to get `$env.config`
into a `mut` variable first and do modifications, then assign it back.
- Much faster performance when executing a script that made
modifications to `$env.config`, as the changes are only parsed once.

# Tests + Formatting
All passing.

# After Submitting
- [ ] release notes
This commit is contained in:
Devyn Cairns
2024-07-11 06:09:33 -07:00
committed by GitHub
parent deaa711ca6
commit f65bc97a54
46 changed files with 327 additions and 222 deletions

View File

@ -3,8 +3,8 @@ use nu_protocol::{
ast::{Argument, Call, Expr, Expression, RecordItem},
debugger::WithoutDebug,
engine::{Command, EngineState, Stack, UNKNOWN_SPAN_ID},
record, Category, Example, IntoPipelineData, PipelineData, Signature, Span, SpanId, Spanned,
SyntaxShape, Type, Value,
record, Category, Config, Example, IntoPipelineData, PipelineData, Signature, Span, SpanId,
Spanned, SyntaxShape, Type, Value,
};
use std::{collections::HashMap, fmt::Write};
@ -13,7 +13,7 @@ pub fn get_full_help(
engine_state: &EngineState,
stack: &mut Stack,
) -> String {
let config = engine_state.get_config();
let config = stack.get_config(engine_state);
let doc_config = DocumentationConfig {
no_subcommands: false,
no_color: !config.use_ansi_coloring,
@ -70,16 +70,30 @@ fn get_documentation(
config: &DocumentationConfig,
is_parser_keyword: bool,
) -> String {
let nu_config = stack.get_config(engine_state);
// Create ansi colors
//todo make these configurable -- pull from enginestate.config
let help_section_name: String =
get_ansi_color_for_component_or_default(engine_state, "shape_string", "\x1b[32m"); // default: green
let help_section_name: String = get_ansi_color_for_component_or_default(
engine_state,
&nu_config,
"shape_string",
"\x1b[32m",
); // default: green
let help_subcolor_one: String =
get_ansi_color_for_component_or_default(engine_state, "shape_external", "\x1b[36m"); // default: cyan
// was const bb: &str = "\x1b[1;34m"; // bold blue
let help_subcolor_two: String =
get_ansi_color_for_component_or_default(engine_state, "shape_block", "\x1b[94m"); // default: light blue (nobold, should be bolding the *names*)
let help_subcolor_one: String = get_ansi_color_for_component_or_default(
engine_state,
&nu_config,
"shape_external",
"\x1b[36m",
); // default: cyan
// was const bb: &str = "\x1b[1;34m"; // bold blue
let help_subcolor_two: String = get_ansi_color_for_component_or_default(
engine_state,
&nu_config,
"shape_block",
"\x1b[94m",
); // default: light blue (nobold, should be bolding the *names*)
const RESET: &str = "\x1b[0m"; // reset
@ -139,13 +153,12 @@ fn get_documentation(
}
if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(Some(engine_state), sig, |v| {
nu_highlight_string(
&v.to_parsable_string(", ", &engine_state.config),
engine_state,
stack,
)
}))
long_desc.push_str(&get_flags_section(
Some(engine_state),
Some(&nu_config),
sig,
|v| nu_highlight_string(&v.to_parsable_string(", ", &nu_config), engine_state, stack),
))
}
if !sig.required_positional.is_empty()
@ -189,7 +202,7 @@ fn get_documentation(
format!(
" (optional, default: {})",
nu_highlight_string(
&value.to_parsable_string(", ", &engine_state.config),
&value.to_parsable_string(", ", &nu_config),
engine_state,
stack
)
@ -339,7 +352,7 @@ fn get_documentation(
let _ = writeln!(
long_desc,
" {}",
item.to_expanded_string("", engine_state.get_config())
item.to_expanded_string("", &nu_config)
.replace('\n', "\n ")
.trim()
);
@ -358,15 +371,16 @@ fn get_documentation(
fn get_ansi_color_for_component_or_default(
engine_state: &EngineState,
nu_config: &Config,
theme_component: &str,
default: &str,
) -> String {
if let Some(color) = &engine_state.get_config().color_config.get(theme_component) {
if let Some(color) = &nu_config.color_config.get(theme_component) {
let caller_stack = &mut Stack::new().capture();
let span = Span::unknown();
let span_id = UNKNOWN_SPAN_ID;
let argument_opt = get_argument_for_color_value(engine_state, color, span, span_id);
let argument_opt = get_argument_for_color_value(nu_config, color, span, span_id);
// Call ansi command using argument
if let Some(argument) = argument_opt {
@ -394,8 +408,8 @@ fn get_ansi_color_for_component_or_default(
}
fn get_argument_for_color_value(
engine_state: &EngineState,
color: &&Value,
nu_config: &Config,
color: &Value,
span: Span,
span_id: SpanId,
) -> Option<Argument> {
@ -412,9 +426,7 @@ fn get_argument_for_color_value(
Type::String,
),
Expression::new_existing(
Expr::String(
v.clone().to_expanded_string("", engine_state.get_config()),
),
Expr::String(v.clone().to_expanded_string("", nu_config)),
span,
span_id,
Type::String,
@ -456,6 +468,7 @@ pub fn document_shape(shape: SyntaxShape) -> SyntaxShape {
pub fn get_flags_section<F>(
engine_state_opt: Option<&EngineState>,
nu_config_opt: Option<&Config>,
signature: &Signature,
mut value_formatter: F, // format default Value (because some calls cant access config or nu-highlight)
) -> String
@ -470,13 +483,26 @@ where
// Sometimes we want to get the flags without engine_state
// For example, in nu-plugin. In that case, we fall back on default values
if let Some(engine_state) = engine_state_opt {
help_section_name =
get_ansi_color_for_component_or_default(engine_state, "shape_string", "\x1b[32m"); // default: green
help_subcolor_one =
get_ansi_color_for_component_or_default(engine_state, "shape_external", "\x1b[36m"); // default: cyan
// was const bb: &str = "\x1b[1;34m"; // bold blue
help_subcolor_two =
get_ansi_color_for_component_or_default(engine_state, "shape_block", "\x1b[94m");
let nu_config = nu_config_opt.unwrap_or_else(|| engine_state.get_config());
help_section_name = get_ansi_color_for_component_or_default(
engine_state,
nu_config,
"shape_string",
"\x1b[32m",
); // default: green
help_subcolor_one = get_ansi_color_for_component_or_default(
engine_state,
nu_config,
"shape_external",
"\x1b[36m",
); // default: cyan
// was const bb: &str = "\x1b[1;34m"; // bold blue
help_subcolor_two = get_ansi_color_for_component_or_default(
engine_state,
nu_config,
"shape_block",
"\x1b[94m",
);
// default: light blue (nobold, should be bolding the *names*)
} else {
help_section_name = "\x1b[32m".to_string();

View File

@ -3,7 +3,7 @@ use nu_path::canonicalize_with;
use nu_protocol::{
ast::Expr,
engine::{Call, EngineState, Stack, StateWorkingSet},
Config, ShellError, Span, Value, VarId,
ShellError, Span, Value, VarId,
};
use std::{
collections::HashMap,
@ -323,18 +323,6 @@ pub fn find_in_dirs_env(
Ok(check_dir(lib_dirs).or_else(|| check_dir(lib_dirs_fallback)))
}
/// Get config
///
/// This combines config stored in permanent state and any runtime updates to the environment. This
/// is the canonical way to fetch config at runtime when you have Stack available.
pub fn get_config(engine_state: &EngineState, stack: &Stack) -> Config {
if let Some(mut config_record) = stack.get_env_var(engine_state, "config") {
config_record.parse_as_config(engine_state.get_config()).0
} else {
engine_state.get_config().clone()
}
}
fn get_converted_value(
engine_state: &EngineState,
stack: &Stack,

View File

@ -1,6 +1,6 @@
use crate::eval_ir_block;
#[allow(deprecated)]
use crate::{current_dir, get_config, get_full_help};
use crate::{current_dir, get_full_help};
use nu_path::{expand_path_with, AbsolutePathBuf};
use nu_protocol::{
ast::{
@ -14,7 +14,7 @@ use nu_protocol::{
Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
};
use nu_utils::IgnoreCaseExt;
use std::{borrow::Cow, fs::OpenOptions, path::PathBuf};
use std::{fs::OpenOptions, path::PathBuf, sync::Arc};
pub fn eval_call<D: DebugContext>(
engine_state: &EngineState,
@ -196,6 +196,9 @@ pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee
for (var, value) in callee_stack.get_stack_env_vars() {
caller_stack.add_env_var(var, value);
}
// set config to callee config, to capture any updates to that
caller_stack.config = callee_stack.config.clone();
}
fn eval_external(
@ -652,8 +655,8 @@ impl Eval for EvalRuntime {
type MutState = Stack;
fn get_config<'a>(engine_state: Self::State<'a>, stack: &mut Stack) -> Cow<'a, Config> {
Cow::Owned(get_config(engine_state, stack))
fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
stack.get_config(engine_state)
}
fn eval_filepath(
@ -843,7 +846,14 @@ impl Eval for EvalRuntime {
});
}
let is_config = original_key == "config";
stack.add_env_var(original_key, value);
// Trigger the update to config, if we modified that.
if is_config {
stack.update_config(engine_state)?;
}
} else {
lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
stack.add_var(*var_id, lhs);