diff --git a/src/completion/base.rs b/src/completion/base.rs index 3de63c8d..fdf73696 100644 --- a/src/completion/base.rs +++ b/src/completion/base.rs @@ -24,7 +24,8 @@ impl Span { } } -/// A trait that defines how to convert a line and position to a list of potential completions in that position. +/// A trait that defines how to convert some text and a position to a list of potential completions in that position. +/// The text could be a part of the whole line, and the position is the index of the end of the text in the original line. pub trait Completer: Send { /// the action that will take the line and position and convert it to a vector of completions, which include the /// span to replace and the contents of that replacement diff --git a/src/completion/default.rs b/src/completion/default.rs index 11062d7b..4a3dd871 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -71,8 +71,11 @@ impl Completer for DefaultCompleter { fn complete(&mut self, line: &str, pos: usize) -> Vec { let mut span_line_whitespaces = 0; let mut completions = vec![]; + // Trimming in case someone passes in text containing stuff after the cursor, if + // `only_buffer_difference` is false + let line = if line.len() > pos { &line[..pos] } else { line }; if !line.is_empty() { - let mut split = line[0..pos].split(' ').rev(); + let mut split = line.split(' ').rev(); let mut span_line: String = String::new(); for _ in 0..split.clone().count() { if let Some(s) = split.next() { diff --git a/src/completion/history.rs b/src/completion/history.rs index ea6aedb6..324e3a17 100644 --- a/src/completion/history.rs +++ b/src/completion/history.rs @@ -52,8 +52,8 @@ impl<'menu> HistoryCompleter<'menu> { fn create_suggestion(&self, line: &str, pos: usize, value: &str) -> Suggestion { let span = Span { - start: pos, - end: pos + line.len(), + start: pos - line.len(), + end: pos, }; Suggestion { diff --git a/src/engine.rs b/src/engine.rs index 43555eec..30bb43bc 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1538,7 +1538,7 @@ impl Reedline { self.get_history_session_id(), ))) .unwrap_or_else(|_| Vec::new()) - .get(0) + .first() .and_then(|history| history.command_line.split_whitespace().next_back()) .map(|token| (parsed.remainder.len(), indicator.len(), token.to_string())), }); diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index ec391777..980c706e 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -526,13 +526,16 @@ impl Menu for ColumnarMenu { /// Updates menu values fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) { - if self.only_buffer_difference { + self.values = if self.only_buffer_difference { if let Some(old_string) = &self.input { let (start, input) = string_difference(editor.get_buffer(), old_string); if !input.is_empty() { - self.values = completer.complete(input, start); - self.reset_position(); + completer.complete(input, start + input.len()) + } else { + completer.complete("", editor.insertion_point()) } + } else { + completer.complete("", editor.insertion_point()) } } else { // If there is a new line character in the line buffer, the completer @@ -541,9 +544,13 @@ impl Menu for ColumnarMenu { // Also, by replacing the new line character with a space, the insert // position is maintain in the line buffer. let trimmed_buffer = editor.get_buffer().replace('\n', " "); - self.values = completer.complete(trimmed_buffer.as_str(), editor.insertion_point()); - self.reset_position(); - } + completer.complete( + &trimmed_buffer[..editor.insertion_point()], + editor.insertion_point(), + ) + }; + + self.reset_position(); } /// The working details for the menu changes based on the size of the lines diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index 5b10e68d..d90e3672 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -395,20 +395,23 @@ impl Menu for ListMenu { /// Collecting the value from the completer to be shown in the menu fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) { let line_buffer = editor.line_buffer(); - let (start, input) = if self.only_buffer_difference { + let (pos, input) = if self.only_buffer_difference { match &self.input { Some(old_string) => { let (start, input) = string_difference(line_buffer.get_buffer(), old_string); if input.is_empty() { (line_buffer.insertion_point(), "") } else { - (start, input) + (start + input.len(), input) } } None => (line_buffer.insertion_point(), ""), } } else { - (line_buffer.insertion_point(), line_buffer.get_buffer()) + ( + line_buffer.insertion_point(), + &line_buffer.get_buffer()[..line_buffer.insertion_point()], + ) }; let parsed = parse_selection_char(input, SELECTION_CHAR); @@ -421,7 +424,7 @@ impl Menu for ListMenu { } self.values = if parsed.remainder.is_empty() { - self.query_size = Some(completer.total_completions(parsed.remainder, start)); + self.query_size = Some(completer.total_completions(parsed.remainder, pos)); let skip = self.pages.iter().take(self.page).sum::().size; let take = self @@ -430,10 +433,10 @@ impl Menu for ListMenu { .map(|page| page.size) .unwrap_or(self.page_size); - completer.partial_complete(input, start, skip, take) + completer.partial_complete(input, pos, skip, take) } else { self.query_size = None; - completer.complete(input, start) + completer.complete(input, pos) } } diff --git a/src/menu/menu_functions.rs b/src/menu/menu_functions.rs index 8d15d696..f3400f4e 100644 --- a/src/menu/menu_functions.rs +++ b/src/menu/menu_functions.rs @@ -209,7 +209,7 @@ pub fn string_difference<'a>(new_string: &'a str, old_string: &str) -> (usize, & false } } else { - *c == old_chars[old_char_index].1 + old_char_index == new_char_index && *c == old_chars[old_char_index].1 }; if equal { @@ -479,6 +479,15 @@ mod tests { assert_eq!(res, (6, "she")); } + #[test] + fn string_difference_with_repeat() { + let new_string = "ee"; + let old_string = "e"; + + let res = string_difference(new_string, old_string); + assert_eq!(res, (1, "e")); + } + #[test] fn find_common_string_with_ansi() { use crate::Span;