Devyn Cairns f65bc97a54
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
2024-07-11 06:09:33 -07:00

111 lines
3.2 KiB
Rust

use std::sync::Arc;
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
use nu_protocol::Config;
pub struct Arguments {
cell_paths: Option<Vec<CellPath>>,
config: Arc<Config>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"ansi strip"
}
fn signature(&self) -> Signature {
Signature::build("ansi strip")
.input_output_types(vec![
(Type::String, Type::String),
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String))),
(Type::table(), Type::table()),
(Type::record(), Type::record()),
])
.rest(
"cell path",
SyntaxShape::CellPath,
"For a data structure input, remove ANSI sequences from strings at the given cell paths.",
)
.allow_variants_without_examples(true)
.category(Category::Platform)
}
fn usage(&self) -> &str {
"Strip ANSI escape sequences from a string."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let config = stack.get_config(engine_state);
let args = Arguments { cell_paths, config };
operate(action, args, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Strip ANSI escape sequences from a string",
example: r#"$'(ansi green)(ansi cursor_on)hello' | ansi strip"#,
result: Some(Value::test_string("hello")),
}]
}
}
fn action(input: &Value, args: &Arguments, _span: Span) -> Value {
let span = input.span();
match input {
Value::String { val, .. } => {
Value::string(nu_utils::strip_ansi_likely(val).to_string(), span)
}
other => {
// Fake stripping ansi for other types and just show the abbreviated string
// instead of showing an error message
Value::string(other.to_abbreviated_string(&args.config), span)
}
}
}
#[cfg(test)]
mod tests {
use super::{action, Arguments, SubCommand};
use nu_protocol::{engine::EngineState, Span, Value};
#[test]
fn examples_work_as_expected() {
use crate::test_examples;
test_examples(SubCommand {})
}
#[test]
fn test_stripping() {
let input_string =
Value::test_string("\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld");
let expected = Value::test_string("Hello Nu World");
let args = Arguments {
cell_paths: vec![].into(),
config: EngineState::new().get_config().clone(),
};
let actual = action(&input_string, &args, Span::test_data());
assert_eq!(actual, expected);
}
}