diff --git a/crates/nu-cli/src/completions/command_completions.rs b/crates/nu-cli/src/completions/command_completions.rs index 9103bc2ad9..73de7ac8cd 100644 --- a/crates/nu-cli/src/completions/command_completions.rs +++ b/crates/nu-cli/src/completions/command_completions.rs @@ -164,7 +164,7 @@ impl Completer for CommandCompletion { .flattened .iter() .rev() - .skip_while(|x| x.0.end > pos) + .skip_while(|x| x.0.end + offset > pos) .take_while(|x| { matches!( x.1, diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index 5ee7452f0c..a7cd5096b0 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -111,11 +111,18 @@ impl NuCompleter { } fn completion_helper(&mut self, line: &str, pos: usize) -> Vec { + // pos: is the position of the cursor in the shell input. + // e.g. lets say you have an alias -> `alias ll = ls -l` and you type in the shell: + // > ll -a | c + // and your cursor is right after `c` then `pos` = 9 + let mut working_set = StateWorkingSet::new(&self.engine_state); - let offset = working_set.next_span_start(); + let mut 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 alias_total_offset: usize = alias_offset.iter().sum(); + // new_line: vector containing all alias "translations" so if it was `ll` now is `ls -l`. + // alias_offset:vector the offset between the name and the alias) + let initial_line = line.to_string(); // Entire line in the shell input. + let alias_total_offset: usize = alias_offset.iter().sum(); // the sum of all alias offsets. new_line.insert(alias_total_offset + pos, b'a'); let pos = offset + pos; let config = self.engine_state.get_config(); @@ -156,14 +163,22 @@ impl NuCompleter { most_left_variable(flat_idx, &working_set, flattened.clone()); // Create a new span - let new_span = if flat_idx == 0 { - Span::new(flat.0.start, flat.0.end - 1 - span_offset) - } else { - Span::new( - flat.0.start - span_offset, - flat.0.end - 1 - span_offset, - ) - }; + // if flat_idx == 0 + let mut span_start = flat.0.start; + let mut span_end = flat.0.end - 1 - span_offset; + + if flat_idx != 0 { + span_start = flat.0.start - span_offset; + span_end = flat.0.end - 1 - span_offset; + } + + if span_end < span_start { + span_start = flat.0.start; + span_end = flat.0.end - 1; + offset += span_offset + } + + let new_span = Span::new(span_start, span_end); // Parses the prefix. Completion should look up to the cursor position, not after. let mut prefix = working_set.get_span_contents(flat.0).to_vec(); @@ -178,6 +193,10 @@ impl NuCompleter { most_left_var.unwrap_or((vec![], vec![])), ); + if offset > new_span.start { + offset -= span_offset; + } + return self.process_completion( &mut completer, &working_set, diff --git a/crates/nu-cli/tests/completions.rs b/crates/nu-cli/tests/completions.rs index 240b794f67..e345fb4e0a 100644 --- a/crates/nu-cli/tests/completions.rs +++ b/crates/nu-cli/tests/completions.rs @@ -815,3 +815,22 @@ fn extern_complete_flags(mut extern_completer: NuCompleter) { let expected: Vec = vec!["--foo".into(), "-b".into(), "-f".into()]; match_suggestions(expected, suggestions); } + +#[rstest] +fn alias_offset_bug_7748() { + let (dir, _, mut engine, mut stack) = new_engine(); + + // Create an alias + let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#; + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok()); + + let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack); + + // Issue #7748 + // Nushell crashes when an alias name is shorter than the alias command + // and the alias command is a external command + // This happens because of offset is not correct. + // This crashes before PR #7779 + let _suggestions = completer.complete("e", 1); + //println!(" --------- suggestions: {:?}", suggestions); +}