Add filter ability to picker

Inspired by doom emacs. Able to filter picker options multiple times.
imgbot
Ivan Tham 3 years ago
parent 7c2fb92c91
commit 002f1ad397

@ -169,3 +169,17 @@ This layer is a kludge of mappings I had under leader key in neovim.
| s | Open symbol picker (current document)| | s | Open symbol picker (current document)|
| w | Enter window mode | | w | Enter window mode |
| space | Keep primary selection TODO: it's here because space mode replaced it | | 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 |

@ -23,6 +23,8 @@ pub struct Picker<T> {
matcher: Box<Matcher>, matcher: Box<Matcher>,
/// (index, score) /// (index, score)
matches: Vec<(usize, i64)>, matches: Vec<(usize, i64)>,
/// Filter over original options.
filters: Vec<usize>, // could be optimized into bit but not worth it now
cursor: usize, cursor: usize,
// pattern: String, // pattern: String,
@ -50,6 +52,7 @@ impl<T> Picker<T> {
options, options,
matcher: Box::new(Matcher::default()), matcher: Box::new(Matcher::default()),
matches: Vec::new(), matches: Vec::new(),
filters: Vec::new(),
cursor: 0, cursor: 0,
prompt, prompt,
format_fn: Box::new(format_fn), format_fn: Box::new(format_fn),
@ -68,6 +71,7 @@ impl<T> Picker<T> {
ref mut options, ref mut options,
ref mut matcher, ref mut matcher,
ref mut matches, ref mut matches,
ref filters,
ref format_fn, ref format_fn,
.. ..
} = *self; } = *self;
@ -81,6 +85,10 @@ impl<T> Picker<T> {
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(index, option)| { .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 // TODO: maybe using format_fn isn't the best idea here
let text = (format_fn)(option); let text = (format_fn)(option);
// TODO: using fuzzy_indices could give us the char idx for match highlighting // TODO: using fuzzy_indices could give us the char idx for match highlighting
@ -114,6 +122,14 @@ impl<T> Picker<T> {
.get(self.cursor) .get(self.cursor)
.map(|(index, _score)| &self.options[*index]) .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: // process:
@ -205,6 +221,12 @@ impl<T: 'static> Component for Picker<T> {
} }
return close_fn; return close_fn;
} }
KeyEvent {
code: KeyCode::Char(' '),
modifiers: KeyModifiers::CONTROL,
} => {
self.save_filter();
}
_ => { _ => {
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) { if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
// TODO: recalculate only if pattern changed // TODO: recalculate only if pattern changed

@ -106,6 +106,13 @@ impl Prompt {
self.exit_selection(); 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) { pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
if self.completion.is_empty() { if self.completion.is_empty() {
return; return;

Loading…
Cancel
Save