use gpui::{App, Global, SharedString};
use icons::Icon;
use ui::menu::{self, Item};
use markdown::Cursor;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mention {
pub icon: Option<Icon>,
pub label: SharedString,
pub description: Option<SharedString>,
pub url: String,
}
pub type MentionSource = fn(query: &str, cx: &App) -> Vec<Mention>;
#[derive(Default)]
pub(crate) struct Installed(pub Vec<(char, MentionSource)>);
impl Global for Installed {}
impl Installed {
pub(crate) fn source(cx: &App, trigger: &str) -> Option<(char, MentionSource)> {
let mut chars = trigger.chars();
let trigger = chars.next().filter(|_| chars.next().is_none())?;
cx.try_global::<Self>()?
.0
.iter()
.find(|(installed, _)| *installed == trigger)
.copied()
}
}
pub struct MentionMenu {
pub at: Cursor,
pub trigger: char,
source: MentionSource,
query: String,
asked: Option<String>,
rows: Vec<Mention>,
pub cursor: menu::Cursor,
}
impl MentionMenu {
pub fn open(at: Cursor, (trigger, source): (char, MentionSource)) -> Self {
Self {
at,
trigger,
source,
query: String::new(),
asked: None,
rows: Vec::new(),
cursor: menu::Cursor::default(),
}
}
pub fn set_query(&mut self, query: String) {
self.query = query;
}
pub fn refresh(&mut self, cx: &App) {
if self.asked.as_ref() == Some(&self.query) {
return;
}
self.rows = (self.source)(&self.query, cx);
self.asked = Some(self.query.clone());
self.cursor.clear();
self.cursor.step(&self.menu(), 1);
}
pub fn menu(&self) -> Vec<Item> {
self.rows
.iter()
.map(|row| {
let mut item = Item::action(row.label.clone());
if let Some(icon) = &row.icon {
item = item.with_icon(icon.clone());
}
match &row.description {
Some(description) => item.with_description(description.clone()),
None => item,
}
})
.collect()
}
pub fn step(&mut self, delta: isize) {
let menu = self.menu();
self.cursor.step(&menu, delta);
}
pub fn row(&self, ix: Option<usize>) -> Option<&Mention> {
self.rows.get(ix.or(self.cursor.row())?)
}
}