aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/ui/menu.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui/menu.rs')
-rw-r--r--helix-term/src/ui/menu.rs54
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)