mirror of
https://github.com/nushell/nushell.git
synced 2025-05-28 22:07:40 +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,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||
};
|
||||
use log::debug;
|
||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::{flatten_pipeline_element, parse, FlatShape};
|
||||
@ -52,6 +53,11 @@ impl NuCompleter {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
debug!(
|
||||
"process_completion: prefix: {}, new_span: {new_span:?}, offset: {offset}, pos: {pos}",
|
||||
String::from_utf8_lossy(prefix)
|
||||
);
|
||||
|
||||
completer.fetch(
|
||||
working_set,
|
||||
&self.stack,
|
||||
@ -134,9 +140,29 @@ impl NuCompleter {
|
||||
|
||||
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 {
|
||||
let flattened = flatten_pipeline_element(&working_set, pipeline_element);
|
||||
let mut spans: Vec<String> = vec![];
|
||||
@ -146,10 +172,10 @@ impl NuCompleter {
|
||||
.first()
|
||||
.filter(|content| content.as_str() == "sudo" || content.as_str() == "doas")
|
||||
.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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
prefix = &prefix[..index];
|
||||
let prefix = ¤t_span[..index];
|
||||
|
||||
// Variables completion
|
||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||
|
@ -1862,3 +1862,31 @@ fn alias_offset_bug_7754() {
|
||||
// This crashes before PR #7756
|
||||
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