feat: start type-to-select search from the current focus
This commit is contained in:
parent
109f83799d
commit
da05a85fc5
1 changed files with 54 additions and 9 deletions
63
src/tab.rs
63
src/tab.rs
|
|
@ -3017,7 +3017,7 @@ impl Tab {
|
|||
/// Returns true if an item was selected.
|
||||
pub fn select_by_prefix(&mut self, prefix: &str) -> bool {
|
||||
let prefix_lower = prefix.to_lowercase();
|
||||
self.select_focus = None;
|
||||
let focus = self.select_focus.take();
|
||||
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
// First, deselect all items
|
||||
|
|
@ -3025,18 +3025,63 @@ impl Tab {
|
|||
item.selected = false;
|
||||
}
|
||||
|
||||
// Find first matching item
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
if item.name.to_lowercase().starts_with(&prefix_lower) {
|
||||
item.selected = true;
|
||||
self.select_focus = Some(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Determine the start index of the search. When the index is before the currently focused item, it will be
|
||||
// considered first, otherwise last. Consider the focused item last when only a single character has been
|
||||
// typed, so we eagerly switch focus on the first character and stay on the same item as long as the prefix
|
||||
// matches.
|
||||
let start = if prefix_lower.chars().count() == 1 {
|
||||
Self::index_after_focus(focus, self.sort_direction)
|
||||
} else {
|
||||
Self::index_before_focus(focus, self.sort_direction)
|
||||
};
|
||||
let Some((until, after)) = items.split_at_mut_checked(start) else {
|
||||
log::error!(
|
||||
"invalid select focus index {start} for items of length {}",
|
||||
items.len()
|
||||
);
|
||||
return false;
|
||||
};
|
||||
let search_items = after
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, item)| (i + start, item))
|
||||
.chain(until.into_iter().enumerate());
|
||||
|
||||
self.select_focus = if self.sort_direction {
|
||||
Self::select_first_prefix_match(&prefix_lower, search_items)
|
||||
} else {
|
||||
Self::select_first_prefix_match(&prefix_lower, search_items.rev())
|
||||
};
|
||||
|
||||
return self.select_focus.is_some();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn index_before_focus(current_focus: Option<usize>, forward: bool) -> usize {
|
||||
current_focus.map_or(0, |i| if forward { i } else { i + 1 })
|
||||
}
|
||||
|
||||
fn index_after_focus(current_focus: Option<usize>, forward: bool) -> usize {
|
||||
current_focus.map_or(0, |i| if forward { i + 1 } else { i })
|
||||
}
|
||||
|
||||
/// Selects the first item in the given iterator whose name starts with the given prefix.
|
||||
///
|
||||
/// The `prefix` must be lowercase.
|
||||
fn select_first_prefix_match<'a>(
|
||||
prefix: &str,
|
||||
items: impl Iterator<Item = (usize, &'a mut Item)>,
|
||||
) -> Option<usize> {
|
||||
for (i, item) in items {
|
||||
if item.name.to_lowercase().starts_with(&prefix) {
|
||||
item.selected = true;
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn select_paths(&mut self, paths: Vec<PathBuf>) {
|
||||
self.select_focus = None;
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue