From 002f1ad397a6fff4fec044e05a8784c643a04d48 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sat, 12 Jun 2021 18:46:05 +0800 Subject: [PATCH] Add filter ability to picker Inspired by doom emacs. Able to filter picker options multiple times. --- book/src/keymap.md | 14 ++++++++++++++ helix-term/src/ui/picker.rs | 22 ++++++++++++++++++++++ helix-term/src/ui/prompt.rs | 7 +++++++ 3 files changed, 43 insertions(+) diff --git a/book/src/keymap.md b/book/src/keymap.md index a4af5ce8..72bedbf3 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -169,3 +169,17 @@ This layer is a kludge of mappings I had under leader key in neovim. | s | Open symbol picker (current document)| | w | Enter window mode | | space | Keep primary selection TODO: it's here because space mode replaced it | + +# Picker + +Keys to use within picker. + +| Key | Description | +|-----|-------------| +| up, ctrl-p | Previous entry | +| down, ctrl-n | Next entry | +| ctrl-space | Filter options | +| enter | Open selected | +| ctrl-h | Open horizontally | +| ctrl-v | Open vertically | +| escape, ctrl-c | Close picker | diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index eae9d74f..20d51d5d 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -23,6 +23,8 @@ pub struct Picker { matcher: Box, /// (index, score) matches: Vec<(usize, i64)>, + /// Filter over original options. + filters: Vec, // could be optimized into bit but not worth it now cursor: usize, // pattern: String, @@ -50,6 +52,7 @@ impl Picker { options, matcher: Box::new(Matcher::default()), matches: Vec::new(), + filters: Vec::new(), cursor: 0, prompt, format_fn: Box::new(format_fn), @@ -68,6 +71,7 @@ impl Picker { ref mut options, ref mut matcher, ref mut matches, + ref filters, ref format_fn, .. } = *self; @@ -81,6 +85,10 @@ impl Picker { .iter() .enumerate() .filter_map(|(index, option)| { + // filter options first before matching + if !filters.is_empty() { + filters.binary_search(&index).ok()?; + } // TODO: maybe using format_fn isn't the best idea here let text = (format_fn)(option); // TODO: using fuzzy_indices could give us the char idx for match highlighting @@ -114,6 +122,14 @@ impl Picker { .get(self.cursor) .map(|(index, _score)| &self.options[*index]) } + + pub fn save_filter(&mut self) { + self.filters.clear(); + self.filters + .extend(self.matches.iter().map(|(index, _)| *index)); + self.filters.sort_unstable(); // used for binary search later + self.prompt.clear(); + } } // process: @@ -205,6 +221,12 @@ impl Component for Picker { } return close_fn; } + KeyEvent { + code: KeyCode::Char(' '), + modifiers: KeyModifiers::CONTROL, + } => { + self.save_filter(); + } _ => { if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) { // TODO: recalculate only if pattern changed diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 433de15e..c388c315 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -106,6 +106,13 @@ impl Prompt { self.exit_selection(); } + pub fn clear(&mut self) { + self.line.clear(); + self.cursor = 0; + self.completion = (self.completion_fn)(&self.line); + self.exit_selection(); + } + pub fn change_completion_selection(&mut self, direction: CompletionDirection) { if self.completion.is_empty() { return;