Refactor and fix Config<->Value mechanism (#10896)

# Description
Our config exists both as a `Config` struct for internal consumption and
as a `Value`. The latter is exposed through `$env.config` and can be
both set and read.
Thus we have a complex bug-prone mechanism, that reads a `Value` and
then tries to plug anything where the value is unrepresentable in
`Config` with the correct state from `Config`.

The parsing involves therefore mutation of the `Value` in a nested
`Record` structure. Previously this was wholy done manually, with
indices.
To enable deletion for example, things had to be iterated over from the
back. Also things were indexed in a bunch of places. This was hard to
read and an invitation for bugs.

With #10876 we can now use `Record::retain_mut` to traverse the records,
modify anything that needs fixing, and drop invalid fields.

# Parts:

- Error messages now consistently use the correct spans pointing to the
problematic value and the paths displayed in some messages are also
aligned with the keys used for lookup.
- Reconstruction of values has been fixed for:
	- `table.padding`
	- `buffer_editor`
	- `hooks.command_not_found`
	- `datetime_format` (partial solution)
- Fix validation of `table.padding` input so value is not set (and
underflows `usize` causing `table` to run forever with negative values)
- New proper types for settings. Fully validated enums instead of
strings:
  - `config.edit_mode` -> `EditMode` 
  	- Don't fall back to vi-mode on invalid string
  - `config.table.mode` -> `TableMode`
- there is still a fall back to `rounded` if given an invalid
`TableMode` as argument to the `nu` binary
  - `config.completions.algorithm` -> `CompletionAlgorithm`
  - `config.error_style` -> `ErrorStyle`
    - don't implicitly fall back to `fancy` when given an invalid value.
- This should also shrink the size of `Config` as instead of 4x24 bytes
those fields now need only 4x1 bytes in `Config`
- Completely removed macros relying on the scope of `Value::into_config`
so we can break it up into smaller parts in the future.
- Factored everything into smaller files with the types and helpers for
particular topics.
- `NuCursorShape` now explicitly expresses the `Inherit` setting.
conversion to option only happens at the interface to `reedline`
This commit is contained in:
Stefan Holderbach
2023-11-08 20:31:30 +01:00
committed by GitHub
parent edbf3aaccb
commit 86cd387439
19 changed files with 1729 additions and 1684 deletions

View File

@ -1,6 +1,6 @@
use crate::completions::{
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
};
use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape};
@ -39,15 +39,12 @@ impl NuCompleter {
) -> Vec<Suggestion> {
let config = self.engine_state.get_config();
let mut options = CompletionOptions {
let options = CompletionOptions {
case_sensitive: config.case_sensitive_completions,
match_algorithm: config.completion_algorithm.into(),
..Default::default()
};
if config.completion_algorithm == "fuzzy" {
options.match_algorithm = MatchAlgorithm::Fuzzy;
}
// Fetch
let mut suggestions =
completer.fetch(working_set, prefix.clone(), new_span, offset, pos, &options);

View File

@ -2,6 +2,7 @@ use std::fmt::Display;
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_parser::trim_quotes_str;
use nu_protocol::CompletionAlgorithm;
#[derive(Copy, Clone)]
pub enum SortBy {
@ -55,6 +56,15 @@ impl MatchAlgorithm {
}
}
impl From<CompletionAlgorithm> for MatchAlgorithm {
fn from(value: CompletionAlgorithm) -> Self {
match value {
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
}
}
}
impl TryFrom<String> for MatchAlgorithm {
type Error = InvalidMatchAlgorithm;

View File

@ -28,8 +28,8 @@ pub fn evaluate_commands(
let (block, delta) = {
if let Some(ref t_mode) = table_mode {
let mut config = engine_state.get_config().clone();
config.table_mode = t_mode.as_string()?;
engine_state.set_config(&config);
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
engine_state.set_config(config);
}
let mut working_set = StateWorkingSet::new(engine_state);
@ -55,7 +55,7 @@ pub fn evaluate_commands(
Ok(pipeline_data) => {
let mut config = engine_state.get_config().clone();
if let Some(t_mode) = table_mode {
config.table_mode = t_mode.as_string()?;
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
}
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
}

View File

@ -195,7 +195,7 @@ pub(crate) fn print_table_or_error(
};
// Change the engine_state config to use the passed in configuration
engine_state.set_config(config);
engine_state.set_config(config.clone());
if let PipelineData::Value(Value::Error { error, .. }, ..) = &pipeline_data {
let working_set = StateWorkingSet::new(engine_state);

View File

@ -7,8 +7,8 @@ use nu_parser::parse;
use nu_protocol::{
create_menus,
engine::{EngineState, Stack, StateWorkingSet},
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, Record, ShellError, Span,
Value,
extract_value, Config, EditBindings, ParsedKeybinding, ParsedMenu, PipelineData, Record,
ShellError, Span, Value,
};
use reedline::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
@ -537,11 +537,11 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
let mut insert_keybindings = default_vi_insert_keybindings();
let mut normal_keybindings = default_vi_normal_keybindings();
match config.edit_mode.as_str() {
"emacs" => {
match config.edit_mode {
EditBindings::Emacs => {
add_menu_keybindings(&mut emacs_keybindings);
}
_ => {
EditBindings::Vi => {
add_menu_keybindings(&mut insert_keybindings);
add_menu_keybindings(&mut normal_keybindings);
}
@ -557,9 +557,9 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
)?
}
match config.edit_mode.as_str() {
"emacs" => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
_ => Ok(KeybindingsMode::Vi {
match config.edit_mode {
EditBindings::Emacs => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
EditBindings::Vi => Ok(KeybindingsMode::Vi {
insert_keybindings,
normal_keybindings,
}),

View File

@ -245,15 +245,9 @@ pub fn evaluate_repl(
// Find the configured cursor shapes for each mode
let cursor_config = CursorConfig {
vi_insert: config
.cursor_shape_vi_insert
.map(map_nucursorshape_to_cursorshape),
vi_normal: config
.cursor_shape_vi_normal
.map(map_nucursorshape_to_cursorshape),
emacs: config
.cursor_shape_emacs
.map(map_nucursorshape_to_cursorshape),
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert),
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
};
perf(
"get config/cursor config",
@ -770,14 +764,15 @@ fn update_line_editor_history(
Ok(line_editor)
}
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorStyle> {
match shape {
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
NuCursorShape::Line => SetCursorStyle::SteadyBar,
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock),
NuCursorShape::UnderScore => Some(SetCursorStyle::SteadyUnderScore),
NuCursorShape::Line => Some(SetCursorStyle::SteadyBar),
NuCursorShape::BlinkBlock => Some(SetCursorStyle::BlinkingBlock),
NuCursorShape::BlinkUnderScore => Some(SetCursorStyle::BlinkingUnderScore),
NuCursorShape::BlinkLine => Some(SetCursorStyle::BlinkingBar),
NuCursorShape::Inherit => None,
}
}