mirror of
https://github.com/nushell/nushell.git
synced 2025-05-31 15:18:23 +02:00
fix(cli): completion in nested blocks (#14856)
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> ## Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> Fixes completion for when the cursor is inside a block: ```nu foo | each { open -<Tab> } ``` ```nu print (open -<Tab>) print [5, 'foo', (open -<Tab>)] ``` etc. Fixes: #11084 Related: #13897 (partially fixes—leading `|` is a different issue) Related: #14643 (different issue not fixed by this pr) Related: #14822 ## User-Facing Changes Flag/command completion (internal) inside blocks has been fixed. ## Tests + Formatting <!-- 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` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> As far as I can tell there is only 1 test that's failing (locally), but it has nothing to do with my pr and is failing before my changes are applied. The test is `completions::variables_completions`. It's because I'm missing `$nu.user-autoload-dirs`.
This commit is contained in:
parent
cce12efe48
commit
945e9511ce
@ -2,6 +2,7 @@ use crate::completions::{
|
|||||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
||||||
DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||||
};
|
};
|
||||||
|
use log::debug;
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_pipeline_element, parse, FlatShape};
|
use nu_parser::{flatten_pipeline_element, parse, FlatShape};
|
||||||
@ -52,6 +53,11 @@ impl NuCompleter {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"process_completion: prefix: {}, new_span: {new_span:?}, offset: {offset}, pos: {pos}",
|
||||||
|
String::from_utf8_lossy(prefix)
|
||||||
|
);
|
||||||
|
|
||||||
completer.fetch(
|
completer.fetch(
|
||||||
working_set,
|
working_set,
|
||||||
&self.stack,
|
&self.stack,
|
||||||
@ -134,9 +140,29 @@ impl NuCompleter {
|
|||||||
|
|
||||||
let config = self.engine_state.get_config();
|
let config = self.engine_state.get_config();
|
||||||
|
|
||||||
let output = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
let outermost_block = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
||||||
|
|
||||||
for pipeline in &output.pipelines {
|
// Try to get the innermost block parsed (by span) so that we consider the correct context/scope.
|
||||||
|
let target_block = working_set
|
||||||
|
.delta
|
||||||
|
.blocks
|
||||||
|
.iter()
|
||||||
|
.filter_map(|block| match block.span {
|
||||||
|
Some(span) if span.contains(pos) => Some((block, span)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.reduce(|prev, cur| {
|
||||||
|
// |(block, span), (block, span)|
|
||||||
|
match cur.1.start.cmp(&prev.1.start) {
|
||||||
|
core::cmp::Ordering::Greater => cur,
|
||||||
|
core::cmp::Ordering::Equal if cur.1.end < prev.1.end => cur,
|
||||||
|
_ => prev,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(block, _)| block)
|
||||||
|
.unwrap_or(&outermost_block);
|
||||||
|
|
||||||
|
for pipeline in &target_block.pipelines {
|
||||||
for pipeline_element in &pipeline.elements {
|
for pipeline_element in &pipeline.elements {
|
||||||
let flattened = flatten_pipeline_element(&working_set, pipeline_element);
|
let flattened = flatten_pipeline_element(&working_set, pipeline_element);
|
||||||
let mut spans: Vec<String> = vec![];
|
let mut spans: Vec<String> = vec![];
|
||||||
@ -146,10 +172,10 @@ impl NuCompleter {
|
|||||||
.first()
|
.first()
|
||||||
.filter(|content| content.as_str() == "sudo" || content.as_str() == "doas")
|
.filter(|content| content.as_str() == "sudo" || content.as_str() == "doas")
|
||||||
.is_some();
|
.is_some();
|
||||||
// Read the current spam to string
|
|
||||||
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
|
||||||
let current_span_str = String::from_utf8_lossy(¤t_span);
|
|
||||||
|
|
||||||
|
// Read the current span to string
|
||||||
|
let current_span = working_set.get_span_contents(flat.0);
|
||||||
|
let current_span_str = String::from_utf8_lossy(current_span);
|
||||||
let is_last_span = pos >= flat.0.start && pos < flat.0.end;
|
let is_last_span = pos >= flat.0.start && pos < flat.0.end;
|
||||||
|
|
||||||
// Skip the last 'a' as span item
|
// Skip the last 'a' as span item
|
||||||
@ -176,9 +202,8 @@ impl NuCompleter {
|
|||||||
let new_span = Span::new(flat.0.start, flat.0.end - 1);
|
let new_span = Span::new(flat.0.start, flat.0.end - 1);
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
// Parses the prefix. Completion should look up to the cursor position, not after.
|
||||||
let mut prefix = working_set.get_span_contents(flat.0);
|
|
||||||
let index = pos - flat.0.start;
|
let index = pos - flat.0.start;
|
||||||
prefix = &prefix[..index];
|
let prefix = ¤t_span[..index];
|
||||||
|
|
||||||
// Variables completion
|
// Variables completion
|
||||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||||
|
@ -1862,3 +1862,31 @@ fn alias_offset_bug_7754() {
|
|||||||
// This crashes before PR #7756
|
// This crashes before PR #7756
|
||||||
let _suggestions = completer.complete("ll -a | c", 9);
|
let _suggestions = completer.complete("ll -a | c", 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn nested_block(mut completer: NuCompleter) {
|
||||||
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
|
|
||||||
|
let suggestions = completer.complete("somecmd | lines | each { tst - }", 30);
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("somecmd | lines | each { tst -}", 30);
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn incomplete_nested_block(mut completer: NuCompleter) {
|
||||||
|
let suggestions = completer.complete("somecmd | lines | each { tst -", 30);
|
||||||
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn deeply_nested_block(mut completer: NuCompleter) {
|
||||||
|
let suggestions = completer.complete(
|
||||||
|
"somecmd | lines | each { print ([each (print) (tst -)]) }",
|
||||||
|
52,
|
||||||
|
);
|
||||||
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user