mirror of
https://github.com/nushell/nushell
synced 2025-01-13 21:55:07 +00:00
Support completion for alias and sub-command (#5749)
* 06-07-wsl * 06-07-linux-issue-with-delete-input * 06-08-2023 * 06-08-Linux * commit for merge * Fix unit test * format * clean code Co-authored-by: Frank Zhang <v-frankz@microsoft.com>
This commit is contained in:
parent
d5b99ae316
commit
b6959197bf
2 changed files with 90 additions and 16 deletions
|
@ -59,37 +59,43 @@ impl NuCompleter {
|
|||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||
let offset = working_set.next_span_start();
|
||||
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
||||
let initial_line = line.to_string();
|
||||
let mut line = line.to_string();
|
||||
line.insert(pos, 'a');
|
||||
new_line.push(b'a');
|
||||
let pos = offset + pos;
|
||||
let (output, _err) = parse(
|
||||
&mut working_set,
|
||||
Some("completer"),
|
||||
line.as_bytes(),
|
||||
false,
|
||||
&[],
|
||||
);
|
||||
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
||||
|
||||
for pipeline in output.pipelines.into_iter() {
|
||||
for expr in pipeline.expressions {
|
||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||
|
||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||
if pos >= flat.0.start && pos < flat.0.end {
|
||||
let alias = if alias_offset.is_empty() {
|
||||
0
|
||||
} else {
|
||||
alias_offset[flat_idx]
|
||||
};
|
||||
if pos >= flat.0.start - alias && pos < flat.0.end - alias {
|
||||
// Context variables
|
||||
let most_left_var =
|
||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||
|
||||
// Create a new span
|
||||
let new_span = Span {
|
||||
start: flat.0.start,
|
||||
end: flat.0.end - 1,
|
||||
let new_span = if flat_idx == 0 {
|
||||
Span {
|
||||
start: flat.0.start,
|
||||
end: flat.0.end - 1 - alias,
|
||||
}
|
||||
} else {
|
||||
Span {
|
||||
start: flat.0.start - alias,
|
||||
end: flat.0.end - 1 - alias,
|
||||
}
|
||||
};
|
||||
|
||||
// Parses the prefix
|
||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||
prefix.remove(pos - flat.0.start);
|
||||
prefix.remove(pos - (flat.0.start - alias));
|
||||
|
||||
// Completions that depends on the previous expression (e.g: use, source)
|
||||
if flat_idx > 0 {
|
||||
|
@ -229,6 +235,74 @@ impl ReedlineCompleter for NuCompleter {
|
|||
}
|
||||
}
|
||||
|
||||
type MatchedAlias<'a> = Vec<(&'a [u8], &'a [u8])>;
|
||||
|
||||
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
|
||||
// that `g` is an alias of `git`
|
||||
fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<usize>) {
|
||||
// An vector represents the offsets of alias
|
||||
// e.g: the offset is 2 for the alias `g` of `git`
|
||||
let mut alias_offset = vec![];
|
||||
let mut output = vec![];
|
||||
if let Some(matched_alias) = search_alias(line, working_set) {
|
||||
let mut lens = matched_alias.len();
|
||||
for (input_vec, line_vec) in matched_alias {
|
||||
alias_offset.push(line_vec.len() - input_vec.len());
|
||||
output.extend(line_vec);
|
||||
if lens > 1 {
|
||||
output.push(b' ');
|
||||
lens -= 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output = line.to_vec();
|
||||
}
|
||||
|
||||
(output, alias_offset)
|
||||
}
|
||||
|
||||
fn search_alias<'a>(input: &'a [u8], working_set: &'a StateWorkingSet) -> Option<MatchedAlias<'a>> {
|
||||
let mut vec_names = vec![];
|
||||
let mut vec_alias = vec![];
|
||||
let mut pos = 0;
|
||||
let mut is_alias = false;
|
||||
for (index, character) in input.iter().enumerate() {
|
||||
if *character == b' ' {
|
||||
let range = &input[pos..index];
|
||||
vec_names.push(range);
|
||||
pos = index + 1;
|
||||
}
|
||||
}
|
||||
// Push the rest to names vector.
|
||||
if pos < input.len() {
|
||||
vec_names.push(&input[pos..]);
|
||||
}
|
||||
|
||||
for name in &vec_names {
|
||||
if let Some(alias_id) = working_set.find_alias(name) {
|
||||
let alias_span = working_set.get_alias(alias_id);
|
||||
is_alias = true;
|
||||
for alias in alias_span {
|
||||
let name = working_set.get_span_contents(*alias);
|
||||
if !name.is_empty() {
|
||||
vec_alias.push(name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vec_alias.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
if is_alias {
|
||||
// Zip names and alias vectors, the original inputs and its aliases mapping.
|
||||
// e.g:(['g'], ['g','i','t'])
|
||||
let output = vec_names.into_iter().zip(vec_alias).collect();
|
||||
Some(output)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// reads the most left variable returning it's name (e.g: $myvar)
|
||||
// and the depth (a.b.c)
|
||||
fn most_left_variable(
|
||||
|
|
|
@ -57,7 +57,7 @@ fn variables_completions() {
|
|||
match_suggestions(expected, suggestions);
|
||||
|
||||
// Test completions for custom var (filtering)
|
||||
let suggestions = completer.complete("$actor.n", 7);
|
||||
let suggestions = completer.complete("$actor.n", 8);
|
||||
|
||||
assert_eq!(1, suggestions.len());
|
||||
|
||||
|
@ -77,7 +77,7 @@ fn variables_completions() {
|
|||
match_suggestions(expected, suggestions);
|
||||
|
||||
// Test completions for $env
|
||||
let suggestions = completer.complete("$env.T", 5);
|
||||
let suggestions = completer.complete("$env.T", 6);
|
||||
|
||||
assert_eq!(1, suggestions.len());
|
||||
|
||||
|
|
Loading…
Reference in a new issue