Add filter ability to picker

Inspired by doom emacs. Able to filter picker options multiple times.
This commit is contained in:
Ivan Tham 2021-06-12 18:46:05 +08:00
parent 7c2fb92c91
commit 002f1ad397
3 changed files with 43 additions and 0 deletions

View file

@ -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 |

View file

@ -23,6 +23,8 @@ pub struct Picker<T> {
matcher: Box<Matcher>,
/// (index, score)
matches: Vec<(usize, i64)>,
/// Filter over original options.
filters: Vec<usize>, // could be optimized into bit but not worth it now
cursor: usize,
// pattern: String,
@ -50,6 +52,7 @@ impl<T> Picker<T> {
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<T> Picker<T> {
ref mut options,
ref mut matcher,
ref mut matches,
ref filters,
ref format_fn,
..
} = *self;
@ -81,6 +85,10 @@ impl<T> Picker<T> {
.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<T> Picker<T> {
.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<T: 'static> Component for Picker<T> {
}
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

View file

@ -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;