nushell/crates/nu-cli/src/menus/menu_completions.rs
Leon 220b105efb
Reduced LOC by replacing several instances of Value::Int {}, Value::Float{}, Value::Bool {}, and Value::String {} with Value::int(), Value::float(), Value::boolean() and Value::string() (#7412)
# Description

While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
  val: a,
  span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.

# User-Facing Changes

None.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-09 11:37:51 -05:00

171 lines
5.2 KiB
Rust

use nu_engine::eval_block;
use nu_protocol::{
engine::{EngineState, Stack},
IntoPipelineData, Span, Value,
};
use reedline::{menu_functions::parse_selection_char, Completer, Suggestion};
use std::sync::Arc;
const SELECTION_CHAR: char = '!';
pub struct NuMenuCompleter {
block_id: usize,
span: Span,
stack: Stack,
engine_state: Arc<EngineState>,
only_buffer_difference: bool,
}
impl NuMenuCompleter {
pub fn new(
block_id: usize,
span: Span,
stack: Stack,
engine_state: Arc<EngineState>,
only_buffer_difference: bool,
) -> Self {
Self {
block_id,
span,
stack,
engine_state,
only_buffer_difference,
}
}
}
impl Completer for NuMenuCompleter {
fn complete(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
let parsed = parse_selection_char(line, SELECTION_CHAR);
let block = self.engine_state.get_block(self.block_id);
if let Some(buffer) = block.signature.get_positional(0) {
if let Some(buffer_id) = &buffer.var_id {
let line_buffer = Value::string(parsed.remainder, self.span);
self.stack.add_var(*buffer_id, line_buffer);
}
}
if let Some(position) = block.signature.get_positional(1) {
if let Some(position_id) = &position.var_id {
let line_buffer = Value::int(pos as i64, self.span);
self.stack.add_var(*position_id, line_buffer);
}
}
let input = Value::nothing(self.span).into_pipeline_data();
let res = eval_block(
&self.engine_state,
&mut self.stack,
block,
input,
false,
false,
);
if let Ok(values) = res {
let values = values.into_value(self.span);
convert_to_suggestions(values, line, pos, self.only_buffer_difference)
} else {
Vec::new()
}
}
}
fn convert_to_suggestions(
value: Value,
line: &str,
pos: usize,
only_buffer_difference: bool,
) -> Vec<Suggestion> {
match value {
Value::Record { .. } => {
let text = match value
.get_data_by_key("value")
.and_then(|val| val.as_string().ok())
{
Some(val) => val,
None => "No value key".to_string(),
};
let description = value
.get_data_by_key("description")
.and_then(|val| val.as_string().ok());
let span = match value.get_data_by_key("span") {
Some(span @ Value::Record { .. }) => {
let start = span
.get_data_by_key("start")
.and_then(|val| val.as_integer().ok());
let end = span
.get_data_by_key("end")
.and_then(|val| val.as_integer().ok());
match (start, end) {
(Some(start), Some(end)) => {
let start = start.min(end);
reedline::Span {
start: start as usize,
end: end as usize,
}
}
_ => reedline::Span {
start: if only_buffer_difference { pos } else { 0 },
end: if only_buffer_difference {
pos + line.len()
} else {
line.len()
},
},
}
}
_ => reedline::Span {
start: if only_buffer_difference { pos } else { 0 },
end: if only_buffer_difference {
pos + line.len()
} else {
line.len()
},
},
};
let extra = match value.get_data_by_key("extra") {
Some(Value::List { vals, .. }) => {
let extra: Vec<String> = vals
.into_iter()
.filter_map(|extra| match extra {
Value::String { val, .. } => Some(val),
_ => None,
})
.collect();
Some(extra)
}
_ => None,
};
vec![Suggestion {
value: text,
description,
extra,
span,
append_whitespace: false,
}]
}
Value::List { vals, .. } => vals
.into_iter()
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
.collect(),
_ => vec![Suggestion {
value: format!("Not a record: {:?}", value),
description: None,
extra: None,
span: reedline::Span {
start: 0,
end: line.len(),
},
append_whitespace: false,
}],
}
}