From 4039d45f87ea955fbd9369c458deaf289c2818ee Mon Sep 17 00:00:00 2001 From: Mike Grunweg Date: Thu, 5 May 2022 20:44:59 +0200 Subject: [PATCH 1/4] doc: document and comment clear_preserve_prompt(). Record the logic of the commit which introduced it. The current logic is not quite correct; we'll fix that in the next commits. --- src/theme.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/theme.rs b/src/theme.rs index 43ea7a01..f4d86fbc 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -846,9 +846,15 @@ impl<'a> TermThemeRenderer<'a> { Ok(()) } + /// Clear all output after the last user prompt; leave the prompt behind. + /// + /// `size_vec` contains the lengths of all lines of output after the prompt. pub fn clear_preserve_prompt(&mut self, size_vec: &[usize]) -> io::Result<()> { + // Printing a selectable item (which should only take up one line) + // can yield several lines in the terminal: if the item is longer than the terminal. + // Take this into account, to fully clear all input after the prompt. let mut new_height = self.height; - //Check each item size, increment on finding an overflow + // Check each item size, increment if it overflows the terminal width. for size in size_vec { if *size > self.term.size().1 as usize { new_height += 1; From 26dd94204b4e18bef44cc910cf52fab2bd2fc6b5 Mon Sep 17 00:00:00 2001 From: Mike Grunweg Date: Thu, 5 May 2022 20:27:14 +0200 Subject: [PATCH 2/4] fix: fix off-by-two errors when determining item width in selection prompts. Formatting each item adds two additional characters, which the code forgot to take into account. --- src/prompts/fuzzy_select.rs | 6 +++--- src/prompts/multi_select.rs | 6 +++--- src/prompts/select.rs | 6 +++--- src/prompts/sort.rs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/prompts/fuzzy_select.rs b/src/prompts/fuzzy_select.rs index e17b4908..5f636f8a 100644 --- a/src/prompts/fuzzy_select.rs +++ b/src/prompts/fuzzy_select.rs @@ -144,9 +144,9 @@ impl FuzzySelect<'_> { let mut sel = self.default; let mut size_vec = Vec::new(); - for items in self.items.iter().as_slice() { - let size = &items.len(); - size_vec.push(*size); + for item in self.items.iter().as_slice() { + // Formatting each item adds two more characters. + size_vec.push(item.len() + 2); } // Fuzzy matcher diff --git a/src/prompts/multi_select.rs b/src/prompts/multi_select.rs index f697c3c2..950f7964 100644 --- a/src/prompts/multi_select.rs +++ b/src/prompts/multi_select.rs @@ -210,14 +210,14 @@ impl MultiSelect<'_> { let mut size_vec = Vec::new(); - for items in self + for item in self .items .iter() .flat_map(|i| i.split('\n')) .collect::>() { - let size = &items.len(); - size_vec.push(*size); + // Formatting each item adds two more characters. + size_vec.push(item.len() + 2); } let mut checked: Vec = self.defaults.clone(); diff --git a/src/prompts/select.rs b/src/prompts/select.rs index 5e4335fc..a86e3584 100644 --- a/src/prompts/select.rs +++ b/src/prompts/select.rs @@ -249,14 +249,14 @@ impl Select<'_> { let mut size_vec = Vec::new(); - for items in self + for item in self .items .iter() .flat_map(|i| i.split('\n')) .collect::>() { - let size = &items.len(); - size_vec.push(*size); + // Formatting each item adds two more characters. + size_vec.push(item.len() + 2); } term.hide_cursor()?; diff --git a/src/prompts/sort.rs b/src/prompts/sort.rs index 03152bf4..35152e72 100644 --- a/src/prompts/sort.rs +++ b/src/prompts/sort.rs @@ -183,9 +183,9 @@ impl Sort<'_> { let mut size_vec = Vec::new(); - for items in self.items.iter().as_slice() { - let size = &items.len(); - size_vec.push(*size); + for item in self.items.iter().as_slice() { + // Formatting each item adds two more characters. + size_vec.push(item.len() + 2); } let mut order: Vec<_> = (0..self.items.len()).collect(); From 6cad8ae1a28a5e6a37a304ce2abc690f251bfc2b Mon Sep 17 00:00:00 2001 From: Mike Grunweg Date: Thu, 5 May 2022 20:31:31 +0200 Subject: [PATCH 3/4] fix/refactor: rewrite size_vec collection code using iterators. Handle multi-line items in sort and fuzzy_select. Select and multi-select already handle these. --- src/prompts/fuzzy_select.rs | 8 ++++---- src/prompts/multi_select.rs | 12 +++--------- src/prompts/select.rs | 12 +++--------- src/prompts/sort.rs | 9 ++++----- 4 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/prompts/fuzzy_select.rs b/src/prompts/fuzzy_select.rs index 5f636f8a..164bd453 100644 --- a/src/prompts/fuzzy_select.rs +++ b/src/prompts/fuzzy_select.rs @@ -143,11 +143,11 @@ impl FuzzySelect<'_> { let mut render = TermThemeRenderer::new(term, self.theme); let mut sel = self.default; - let mut size_vec = Vec::new(); - for item in self.items.iter().as_slice() { + let size_vec: Vec<_> = self.items.iter() + .flat_map(|i| i.split('\n')) // Formatting each item adds two more characters. - size_vec.push(item.len() + 2); - } + .map(|item| item.len() + 2) + .collect(); // Fuzzy matcher let matcher = fuzzy_matcher::skim::SkimMatcherV2::default(); diff --git a/src/prompts/multi_select.rs b/src/prompts/multi_select.rs index 950f7964..31f414b6 100644 --- a/src/prompts/multi_select.rs +++ b/src/prompts/multi_select.rs @@ -208,17 +208,11 @@ impl MultiSelect<'_> { let mut render = TermThemeRenderer::new(term, self.theme); let mut sel = 0; - let mut size_vec = Vec::new(); - - for item in self - .items - .iter() + let size_vec: Vec<_> = self.items.iter() .flat_map(|i| i.split('\n')) - .collect::>() - { // Formatting each item adds two more characters. - size_vec.push(item.len() + 2); - } + .map(|item| item.len() + 2) + .collect(); let mut checked: Vec = self.defaults.clone(); diff --git a/src/prompts/select.rs b/src/prompts/select.rs index a86e3584..0fca9a5b 100644 --- a/src/prompts/select.rs +++ b/src/prompts/select.rs @@ -247,17 +247,11 @@ impl Select<'_> { let mut render = TermThemeRenderer::new(term, self.theme); let mut sel = self.default; - let mut size_vec = Vec::new(); - - for item in self - .items - .iter() + let size_vec: Vec<_> = self.items.iter() .flat_map(|i| i.split('\n')) - .collect::>() - { // Formatting each item adds two more characters. - size_vec.push(item.len() + 2); - } + .map(|item| item.len() + 2) + .collect(); term.hide_cursor()?; diff --git a/src/prompts/sort.rs b/src/prompts/sort.rs index 35152e72..ec600a90 100644 --- a/src/prompts/sort.rs +++ b/src/prompts/sort.rs @@ -181,12 +181,11 @@ impl Sort<'_> { let mut render = TermThemeRenderer::new(term, self.theme); let mut sel = 0; - let mut size_vec = Vec::new(); - - for item in self.items.iter().as_slice() { + let size_vec: Vec<_> = self.items.iter() + .flat_map(|i| i.split('\n')) // Formatting each item adds two more characters. - size_vec.push(item.len() + 2); - } + .map(|item| item.len() + 2) + .collect(); let mut order: Vec<_> = (0..self.items.len()).collect(); let mut checked: bool = false; From 13f56318d1c4d1adaf68481cfefb92d1081eaa79 Mon Sep 17 00:00:00 2001 From: Mike Grunweg Date: Thu, 5 May 2022 20:33:52 +0200 Subject: [PATCH 4/4] fix: handle very long items in clear_preserve_prompt correctly. The previous code would (correctly) handle the case of an item spanning between one and two lines, but failed to consider the case of the output spanning more than two. Extend the logic to cover that. --- src/theme.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index f4d86fbc..d0e7f716 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -856,9 +856,8 @@ impl<'a> TermThemeRenderer<'a> { let mut new_height = self.height; // Check each item size, increment if it overflows the terminal width. for size in size_vec { - if *size > self.term.size().1 as usize { - new_height += 1; - } + let term_width = self.term.size().1 as usize; + new_height += *size / term_width; } self.term.clear_last_lines(new_height)?;