use std::time::{Duration, Instant};
use super::*;
const RECENT: usize = 5;
const SEARCH_DELAY: Duration = Duration::from_millis(250);
const SEARCH_MIN_CHARS: usize = 3;
fn looks_like_identifier(query: &str) -> bool {
let Some((team, number)) = query.split_once('-') else {
return false;
};
!team.is_empty()
&& team.chars().all(|c| c.is_ascii_alphabetic())
&& !number.is_empty()
&& number.chars().all(|c| c.is_ascii_digit())
}
impl App {
pub fn open_palette(&mut self) {
self.view.popup = Popup::Palette;
let issue = self.focused_issue_id();
let palette = &mut self.view.palette;
palette.query.clear();
palette.selected = 0;
palette.issue = issue;
palette.results.clear();
palette.searching = false;
palette.search_due = None;
}
pub fn palette_query_changed(&mut self, now: Instant) {
let palette = &mut self.view.palette;
let query = palette.query.value.trim();
let worth_searching = !query.starts_with('>')
&& (query.chars().count() >= SEARCH_MIN_CHARS || looks_like_identifier(query));
palette.search_due = worth_searching.then(|| now + SEARCH_DELAY);
if !worth_searching {
palette.searching = false;
palette.results.clear();
}
}
pub fn flush_palette_search(&mut self, now: Instant) -> bool {
if self.view.popup != Popup::Palette {
self.view.palette.search_due = None;
return false;
}
let palette = &mut self.view.palette;
match palette.search_due {
Some(due) if now >= due => {
palette.search_due = None;
palette.seq += 1;
palette.searching = true;
let request = Request::PaletteSearch {
term: palette.query.value.trim().to_string(),
seq: palette.seq,
};
self.request(request);
true
}
_ => false,
}
}
pub(super) fn accept_palette_results(&mut self, seq: u64, issues: Vec<Issue>) {
let palette = &mut self.view.palette;
if seq != palette.seq {
return;
}
palette.searching = false;
palette.results = issues;
}
pub fn remember_command(&mut self, title: &'static str) {
let recent = &mut self.view.palette.recent;
recent.retain(|t| *t != title);
recent.insert(0, title);
recent.truncate(RECENT);
}
}