diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index da276fd3d7..d7e69d34da 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -59,37 +59,43 @@ impl NuCompleter { fn completion_helper(&mut self, line: &str, pos: usize) -> Vec { 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, Vec) { + // 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> { + 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( diff --git a/crates/nu-cli/tests/variables_completions.rs b/crates/nu-cli/tests/variables_completions.rs index 53f1a9d47b..942eebbc25 100644 --- a/crates/nu-cli/tests/variables_completions.rs +++ b/crates/nu-cli/tests/variables_completions.rs @@ -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());