on master help sign in
Add filter ability to picker

Inspired by doom emacs. Able to filter picker options multiple times.

commit002f1ad397a6fff4fec044e05a8784c643a04d48 authorIvan Tham <pickfire@riseup.net> dateSat 12 Jun 2021, 18:46 (5 years ago) parent7c2fb92
3 files changed+43
book/src/keymap.md+14 helix-term/src/ui/picker.rs+22 helix-term/src/ui/prompt.rs+7
book/src/keymap.md+14
@@ -169,3 +169,17 @@ This layer is a kludge of mappings I had under leader key in neovim.
169169| s | Open symbol picker (current document)|
170170| w | Enter window mode |
171171| space | Keep primary selection TODO: it's here because space mode replaced it |
172+
173+# Picker
174+
175+Keys to use within picker.
176+
177+| Key | Description |
178+|-----|-------------|
179+| up, ctrl-p | Previous entry |
180+| down, ctrl-n | Next entry |
181+| ctrl-space | Filter options |
182+| enter | Open selected |
183+| ctrl-h | Open horizontally |
184+| ctrl-v | Open vertically |
185+| escape, ctrl-c | Close picker |
helix-term/src/ui/picker.rs+22
@@ -23,6 +23,8 @@ pub struct Picker<T> {
2323 matcher: Box<Matcher>,
2424 /// (index, score)
2525 matches: Vec<(usize, i64)>,
26+ /// Filter over original options.
27+ filters: Vec<usize>, // could be optimized into bit but not worth it now
2628
2729 cursor: usize,
2830 // pattern: String,
@@ -50,6 +52,7 @@ impl<T> Picker<T> {
5052 options,
5153 matcher: Box::new(Matcher::default()),
5254 matches: Vec::new(),
55+ filters: Vec::new(),
5356 cursor: 0,
5457 prompt,
5558 format_fn: Box::new(format_fn),
@@ -68,6 +71,7 @@ impl<T> Picker<T> {
6871 ref mut options,
6972 ref mut matcher,
7073 ref mut matches,
74+ ref filters,
7175 ref format_fn,
7276 ..
7377 } = *self;
@@ -81,6 +85,10 @@ impl<T> Picker<T> {
8185 .iter()
8286 .enumerate()
8387 .filter_map(|(index, option)| {
88+ // filter options first before matching
89+ if !filters.is_empty() {
90+ filters.binary_search(&index).ok()?;
91+ }
8492 // TODO: maybe using format_fn isn't the best idea here
8593 let text = (format_fn)(option);
8694 // TODO: using fuzzy_indices could give us the char idx for match highlighting
@@ -114,6 +122,14 @@ impl<T> Picker<T> {
114122 .get(self.cursor)
115123 .map(|(index, _score)| &self.options[*index])
116124 }
125+
126+ pub fn save_filter(&mut self) {
127+ self.filters.clear();
128+ self.filters
129+ .extend(self.matches.iter().map(|(index, _)| *index));
130+ self.filters.sort_unstable(); // used for binary search later
131+ self.prompt.clear();
132+ }
117133}
118134
119135// process:
@@ -205,6 +221,12 @@ impl<T: 'static> Component for Picker<T> {
205221 }
206222 return close_fn;
207223 }
224+ KeyEvent {
225+ code: KeyCode::Char(' '),
226+ modifiers: KeyModifiers::CONTROL,
227+ } => {
228+ self.save_filter();
229+ }
208230 _ => {
209231 if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
210232 // TODO: recalculate only if pattern changed
helix-term/src/ui/prompt.rs+7
@@ -106,6 +106,13 @@ impl Prompt {
106106 self.exit_selection();
107107 }
108108
109+ pub fn clear(&mut self) {
110+ self.line.clear();
111+ self.cursor = 0;
112+ self.completion = (self.completion_fn)(&self.line);
113+ self.exit_selection();
114+ }
115+
109116 pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
110117 if self.completion.is_empty() {
111118 return;