From 07fbd4bd21667770364336cfef07c7f776ecf42e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:09:05 -0500 Subject: [PATCH 01/10] Don't slice line in DefaultCompleter --- examples/completions.rs | 2 +- src/completion/default.rs | 2 +- src/menu/columnar_menu.rs | 7 +++++-- src/menu/list_menu.rs | 7 +++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/completions.rs b/examples/completions.rs index 95f4975e..a7d8d3fb 100644 --- a/examples/completions.rs +++ b/examples/completions.rs @@ -29,7 +29,7 @@ fn main() -> io::Result<()> { ]; let completer = Box::new(DefaultCompleter::new_with_wordlen(commands, 2)); // Use the interactive menu to select options from the completer - let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu")); + let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu").with_only_buffer_difference(true)); let mut keybindings = default_emacs_keybindings(); add_menu_keybindings(&mut keybindings); diff --git a/src/completion/default.rs b/src/completion/default.rs index 11062d7b..73496b37 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -72,7 +72,7 @@ impl Completer for DefaultCompleter { let mut span_line_whitespaces = 0; let mut completions = vec![]; 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/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index ec391777..9e45adb7 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -530,7 +530,7 @@ impl Menu for ColumnarMenu { 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.values = completer.complete(input, start + input.len()); self.reset_position(); } } @@ -541,7 +541,10 @@ 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.values = completer.complete( + &trimmed_buffer[..editor.insertion_point()], + editor.insertion_point(), + ); self.reset_position(); } } diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index 5b10e68d..f92a14a7 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -402,13 +402,16 @@ impl Menu for ListMenu { 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); From 950d66dbe6489b2d749e1525a7ba2e0d9921fb1d Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:27:01 -0500 Subject: [PATCH 02/10] Revert completions example --- examples/completions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/completions.rs b/examples/completions.rs index a7d8d3fb..95f4975e 100644 --- a/examples/completions.rs +++ b/examples/completions.rs @@ -29,7 +29,7 @@ fn main() -> io::Result<()> { ]; let completer = Box::new(DefaultCompleter::new_with_wordlen(commands, 2)); // Use the interactive menu to select options from the completer - let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu").with_only_buffer_difference(true)); + let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu")); let mut keybindings = default_emacs_keybindings(); add_menu_keybindings(&mut keybindings); From 6a0fe0f4b25e2b7b56c96fe5990482cbb8a2dabe Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:44:46 -0500 Subject: [PATCH 03/10] Fix clippy lint --- src/engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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())), }); From 8e3fb447a2d4299fd0a81155aec968333760ab36 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 5 Jan 2024 12:02:07 -0500 Subject: [PATCH 04/10] Treat input as '' if None --- src/menu/columnar_menu.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index 9e45adb7..b0b571a6 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -533,6 +533,8 @@ impl Menu for ColumnarMenu { self.values = completer.complete(input, start + input.len()); self.reset_position(); } + } else { + self.values = completer.complete("", editor.insertion_point()); } } else { // If there is a new line character in the line buffer, the completer From 65d0d5aab38b48c05904a4d29e52d03f0e93957c Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 5 Jan 2024 12:30:40 -0500 Subject: [PATCH 05/10] Update values even if string_difference empty --- src/menu/columnar_menu.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index b0b571a6..7c466008 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -526,29 +526,31 @@ 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 + input.len()); - self.reset_position(); + completer.complete(input, start + input.len()) + } else { + completer.complete("", editor.insertion_point()) } } else { - self.values = completer.complete("", editor.insertion_point()); + completer.complete("", editor.insertion_point()) } } else { // If there is a new line character in the line buffer, the completer // doesn't calculate the suggested values correctly. This happens when // editing a multiline buffer. - // Also, by replacing the new line character with a space, the insert + // Also, by replacing the new line character with a space, the insertF // position is maintain in the line buffer. let trimmed_buffer = editor.get_buffer().replace('\n', " "); - self.values = completer.complete( + completer.complete( &trimmed_buffer[..editor.insertion_point()], editor.insertion_point(), - ); - self.reset_position(); - } + ) + }; + + self.reset_position(); } /// The working details for the menu changes based on the size of the lines From 297babba7f8bb7c9213cd46efba4cfa4c4134db5 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:20:04 -0500 Subject: [PATCH 06/10] Rename start to pos in list_menu --- src/menu/columnar_menu.rs | 2 +- src/menu/list_menu.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index 7c466008..980c706e 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -541,7 +541,7 @@ impl Menu for ColumnarMenu { // If there is a new line character in the line buffer, the completer // doesn't calculate the suggested values correctly. This happens when // editing a multiline buffer. - // Also, by replacing the new line character with a space, the insertF + // 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', " "); completer.complete( diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index f92a14a7..d90e3672 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -395,7 +395,7 @@ 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); @@ -424,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 @@ -433,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) } } From 789c690e7dfec4d342d15ab8abbe7407bd7b0600 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 5 Jan 2024 18:12:04 -0500 Subject: [PATCH 07/10] Fix string_diff bug with repeated char --- src/menu/menu_functions.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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; From 4342ff556454c856fcc7e6545c21293c971abe1e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:10:47 -0500 Subject: [PATCH 08/10] Trim before completing in DefaultCompleter --- src/completion/default.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/completion/default.rs b/src/completion/default.rs index 73496b37..171e30f3 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -71,6 +71,9 @@ impl Completer for DefaultCompleter { fn complete(&mut self, line: &str, pos: usize) -> Vec { let mut span_line_whitespaces = 0; let mut completions = vec![]; + // TODO trimming here is only necessary if someone passes in text containing + // stuff after the cursor with `only_buffer_difference: false` + let line = if line.len() > pos { &line[..pos] } else { line }; if !line.is_empty() { let mut split = line.split(' ').rev(); let mut span_line: String = String::new(); From f333f6c0a94377640f9f832e5135571823902587 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:24:34 -0500 Subject: [PATCH 09/10] Remove TODO --- src/completion/default.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/completion/default.rs b/src/completion/default.rs index 171e30f3..4a3dd871 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -71,8 +71,8 @@ impl Completer for DefaultCompleter { fn complete(&mut self, line: &str, pos: usize) -> Vec { let mut span_line_whitespaces = 0; let mut completions = vec![]; - // TODO trimming here is only necessary if someone passes in text containing - // stuff after the cursor with `only_buffer_difference: false` + // 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.split(' ').rev(); From 8eefe0dbf76e41679ef887f53fd2b3eb1ebd1603 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:54:11 -0500 Subject: [PATCH 10/10] Make HistoryCompleter take end as pos --- src/completion/base.rs | 3 ++- src/completion/history.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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 {