picker: Implement fuzzy search.

imgbot
Blaž Hrastnik 4 years ago
parent 4f9cde25cf
commit edfd3933db

@ -21,6 +21,8 @@ pub struct Picker {
files: Vec<PathBuf>, files: Vec<PathBuf>,
// filter: String, // filter: String,
matcher: Box<Matcher>, matcher: Box<Matcher>,
/// (index, score)
matches: Vec<(usize, i64)>,
cursor: usize, cursor: usize,
// pattern: String, // pattern: String,
@ -51,20 +53,46 @@ impl Picker {
const MAX: usize = 1024; const MAX: usize = 1024;
Self { let mut picker = Self {
files: files.take(MAX).collect(), files: files.take(MAX).collect(),
matcher: Box::new(Matcher::default()), matcher: Box::new(Matcher::default()),
matches: Vec::new(),
cursor: 0, cursor: 0,
prompt, prompt,
} };
// TODO: scoring on empty input should just use a fastpath
picker.score();
picker
} }
pub fn score(&mut self, pattern: &str) { pub fn score(&mut self) {
self.files.iter().filter_map(|path| match path.to_str() { // need to borrow via pattern match otherwise it complains about simultaneous borrow
// TODO: using fuzzy_indices could give us the char idx for match highlighting let Self {
Some(path) => (self.matcher.fuzzy_match(path, pattern)), ref mut files,
None => None, ref mut matcher,
}); ref mut matches,
..
} = *self;
let pattern = &self.prompt.line;
// reuse the matches allocation
matches.clear();
matches.extend(self.files.iter().enumerate().filter_map(|(index, path)| {
match path.to_str() {
// TODO: using fuzzy_indices could give us the char idx for match highlighting
Some(path) => matcher
.fuzzy_match(path, pattern)
.map(|score| (index, score)),
None => None,
}
}));
matches.sort_unstable_by_key(|(_, score)| -score);
// reset cursor position
self.cursor = 0;
} }
pub fn move_up(&mut self) { pub fn move_up(&mut self) {
@ -77,6 +105,12 @@ impl Picker {
self.cursor += 1; self.cursor += 1;
} }
} }
pub fn selection(&self) -> Option<&PathBuf> {
self.matches
.get(self.cursor)
.map(|(index, _score)| &self.files[*index])
}
} }
// process: // process:
@ -125,7 +159,15 @@ impl Component for Picker {
} => { } => {
return close_fn; return close_fn;
} }
_ => return self.prompt.handle_event(event, cx), _ => {
match self.prompt.handle_event(event, cx) {
EventResult::Consumed(_) => {
// TODO: recalculate only if pattern changed
self.score();
}
_ => (),
}
}
} }
EventResult::Consumed(None) EventResult::Consumed(None)
@ -184,7 +226,12 @@ impl Component for Picker {
let selected = Style::default().fg(Color::Rgb(255, 255, 255)); let selected = Style::default().fg(Color::Rgb(255, 255, 255));
let rows = inner.height - 2; // -1 for search bar let rows = inner.height - 2; // -1 for search bar
for (i, file) in self.files.iter().take(rows as usize).enumerate() {
let files = self.matches.iter().map(|(index, _score)| {
(index, self.files.get(*index).unwrap()) // get_unchecked
});
for (i, (_index, file)) in files.take(rows as usize).enumerate() {
if i == self.cursor { if i == self.cursor {
surface.set_string(inner.x + 1, inner.y + 2 + i as u16, ">", selected); surface.set_string(inner.x + 1, inner.y + 2 + i as u16, ">", selected);
} }

Loading…
Cancel
Save