refactor(completion): expression based variable/cell_path completion (#15033)

# Description

fixes #14643 , as well as some nested cell path cases:

```nushell
let foo = {a: [1 {a: 1}]}

$foo.a.1.#<tab>

const bar = {a: 1, b: 2}
$bar.#<tab>
```

So my plan of the refactoring process is that:
1. gradually move those rules of flattened shapes into expression match
branches, until they are gone
2. keep each PR focused, easier to review and track. 

# User-Facing Changes

# Tests + Formatting

+2

# After Submitting
This commit is contained in:
zc he
2025-02-10 11:26:41 +08:00
committed by GitHub
parent 720813339f
commit 6e88b3f8d6
5 changed files with 243 additions and 282 deletions

View File

@ -1554,6 +1554,58 @@ fn variables_completions() {
match_suggestions(&expected, &suggestions);
}
#[test]
fn record_cell_path_completions() {
let (_, _, mut engine, mut stack) = new_engine();
let command = r#"let foo = {a: [1 {a: 2}]}; const bar = {a: [1 {a: 2}]}"#;
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let expected: Vec<String> = vec!["a".into()];
let completion_str = "$foo.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let completion_str = "$foo.a.1.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let completion_str = "$bar.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let completion_str = "$bar.a.1.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let completion_str = "{a: [1 {a: 2}]}.a.1.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
}
#[test]
fn table_cell_path_completions() {
let (_, _, mut engine, mut stack) = new_engine();
let command = r#"let foo = [{a:{b:1}}, {a:{b:2}}]; const bar = [[a b]; [1 2]]"#;
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let expected: Vec<String> = vec!["a".into()];
let completion_str = "$foo.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let expected: Vec<String> = vec!["b".into()];
let completion_str = "$foo.a.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
let expected: Vec<String> = vec!["a".into(), "b".into()];
let completion_str = "$bar.";
let suggestions = completer.complete(completion_str, completion_str.len());
match_suggestions(&expected, &suggestions);
}
#[test]
fn alias_of_command_and_flags() {
let (_, _, mut engine, mut stack) = new_engine();