diff options
author | Gokul Soumya | 2022-07-02 11:21:27 +0000 |
---|---|---|
committer | GitHub | 2022-07-02 11:21:27 +0000 |
commit | 6e2aaed5c2cbcedc9ee4e225510cae4f357888aa (patch) | |
tree | bddbecd95c9b3df5cb207736f1d0cbdeb56c1c3c /helix-term/src/ui/menu.rs | |
parent | 290b3ebbbe0c365eee436b9de9d6d6fc2b4339e9 (diff) |
Reuse menu::Item trait in picker (#2814)
* Refactor menu::Item to accomodate external state
Will be useful for storing editor state when reused by pickers.
* Add some type aliases for readability
* Reuse menu::Item trait in picker
This opens the way for merging the menu and picker code in the
future, since a picker is essentially a menu + prompt. More
excitingly, this change will also allow aligning items in the
picker, which would be useful (for example) in the command palette
for aligning the descriptions to the left and the keybinds to
the right in two separate columns.
The item formatting of each picker has been kept as is, even though
there is room for improvement now that we can format the data into
columns, since that is better tackled in a separate PR.
* Rename menu::Item::EditorData to Data
* Call and inline filter_text() in sort_text() completion
* Rename diagnostic picker's Item::Data
Diffstat (limited to 'helix-term/src/ui/menu.rs')
-rw-r--r-- | helix-term/src/ui/menu.rs | 54 |
1 files changed, 39 insertions, 15 deletions
diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 0519374a..6bb64139 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -1,9 +1,11 @@ +use std::{borrow::Cow, path::PathBuf}; + use crate::{ compositor::{Callback, Component, Compositor, Context, EventResult}, ctrl, key, shift, }; use crossterm::event::Event; -use tui::{buffer::Buffer as Surface, widgets::Table}; +use tui::{buffer::Buffer as Surface, text::Spans, widgets::Table}; pub use tui::widgets::{Cell, Row}; @@ -14,22 +16,41 @@ use helix_view::{graphics::Rect, Editor}; use tui::layout::Constraint; pub trait Item { - fn label(&self) -> &str; + /// Additional editor state that is used for label calculation. + type Data; + + fn label(&self, data: &Self::Data) -> Spans; + + fn sort_text(&self, data: &Self::Data) -> Cow<str> { + let label: String = self.label(data).into(); + label.into() + } - fn sort_text(&self) -> &str { - self.label() + fn filter_text(&self, data: &Self::Data) -> Cow<str> { + let label: String = self.label(data).into(); + label.into() } - fn filter_text(&self) -> &str { - self.label() + + fn row(&self, data: &Self::Data) -> Row { + Row::new(vec![Cell::from(self.label(data))]) } +} - fn row(&self) -> Row { - Row::new(vec![Cell::from(self.label())]) +impl Item for PathBuf { + /// Root prefix to strip. + type Data = PathBuf; + + fn label(&self, root_path: &Self::Data) -> Spans { + self.strip_prefix(&root_path) + .unwrap_or(self) + .to_string_lossy() + .into() } } pub struct Menu<T: Item> { options: Vec<T>, + editor_data: T::Data, cursor: Option<usize>, @@ -54,10 +75,12 @@ impl<T: Item> Menu<T> { // rendering) pub fn new( options: Vec<T>, + editor_data: <T as Item>::Data, callback_fn: impl Fn(&mut Editor, Option<&T>, MenuEvent) + 'static, ) -> Self { let mut menu = Self { options, + editor_data, matcher: Box::new(Matcher::default()), matches: Vec::new(), cursor: None, @@ -83,16 +106,17 @@ impl<T: Item> Menu<T> { .iter() .enumerate() .filter_map(|(index, option)| { - let text = option.filter_text(); + let text: String = option.filter_text(&self.editor_data).into(); // TODO: using fuzzy_indices could give us the char idx for match highlighting self.matcher - .fuzzy_match(text, pattern) + .fuzzy_match(&text, pattern) .map(|score| (index, score)) }), ); // matches.sort_unstable_by_key(|(_, score)| -score); - self.matches - .sort_unstable_by_key(|(index, _score)| self.options[*index].sort_text()); + self.matches.sort_unstable_by_key(|(index, _score)| { + self.options[*index].sort_text(&self.editor_data) + }); // reset cursor position self.cursor = None; @@ -127,10 +151,10 @@ impl<T: Item> Menu<T> { let n = self .options .first() - .map(|option| option.row().cells.len()) + .map(|option| option.row(&self.editor_data).cells.len()) .unwrap_or_default(); let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| { - let row = option.row(); + let row = option.row(&self.editor_data); // maintain max for each column for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) { let width = cell.content.width(); @@ -300,7 +324,7 @@ impl<T: Item + 'static> Component for Menu<T> { let scroll_line = (win_height - scroll_height) * scroll / std::cmp::max(1, len.saturating_sub(win_height)); - let rows = options.iter().map(|option| option.row()); + let rows = options.iter().map(|option| option.row(&self.editor_data)); let table = Table::new(rows) .style(style) .highlight_style(selected) |