diff options
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) |