use super::{CommandItem, Row};
use crate::fuzzy;
pub(super) const CATEGORY_WIDTH: usize = 12;
pub(super) struct RowLayout<'a> {
pub(super) rows: Vec<Row<'a>>,
pub(super) selectable: Vec<usize>,
pub(super) section_starts: Vec<usize>,
}
pub(super) fn selected_index(
layout: &RowLayout,
cursor: usize,
) -> Option<usize> {
let row = *layout.selectable.get(cursor)?;
match layout.rows[row] {
Row::Item { index, .. } => Some(index),
Row::Header(_) => None,
}
}
pub(super) fn layout_rows<'a>(
items: &'a [CommandItem<'a>],
query: &str,
) -> RowLayout<'a> {
if query.trim().is_empty() {
grouped_rows(items)
} else {
ranked_rows(items, query.trim())
}
}
pub(super) fn grouped_rows<'a>(items: &'a [CommandItem<'a>]) -> RowLayout<'a> {
let mut rows: Vec<Row<'a>> = Vec::new();
let mut selectable: Vec<usize> = Vec::new();
let mut section_starts: Vec<usize> = Vec::new();
let mut current_category: Option<&str> = None;
let mut section_has_selectable = false;
for (index, item) in items.iter().enumerate() {
if current_category != Some(item.category) {
rows.push(Row::Header(item.category));
current_category = Some(item.category);
section_has_selectable = false;
}
if item.enabled {
if !section_has_selectable {
section_starts.push(selectable.len());
section_has_selectable = true;
}
selectable.push(rows.len());
}
rows.push(Row::Item { item, index });
}
RowLayout {
rows,
selectable,
section_starts,
}
}
pub(super) fn ranked_rows<'a>(
items: &'a [CommandItem<'a>],
query: &str,
) -> RowLayout<'a> {
let ranked = fuzzy::rank_by(items, query, |item| {
format!("{} {}", item.category, item.label).into()
});
let mut rows: Vec<Row<'a>> = Vec::new();
let mut selectable: Vec<usize> = Vec::new();
for index in ranked {
let item = &items[index];
if item.enabled {
selectable.push(rows.len());
}
rows.push(Row::Item { item, index });
}
RowLayout {
rows,
selectable,
section_starts: Vec::new(),
}
}