use egui::{Area, Context, Frame, Id, Label, Order, RichText, TextWrapMode};
use crate::completion;
#[derive(Default)]
pub(super) struct Suggest {
pub(super) open: bool,
pub(super) matches: Vec<String>,
pub(super) selected: usize,
pub(super) suppressed: bool,
}
impl Suggest {
pub(super) fn accept(&mut self, text: &mut String, cursor: usize) -> Option<(usize, usize)> {
let (start, end) = completion::current_word(text, cursor);
let matched = self.matches.get(self.selected)?.clone();
let start_byte = completion::char_to_byte(text, start);
let end_byte = completion::char_to_byte(text, end);
text.replace_range(start_byte..end_byte, &matched);
self.open = false;
let caret = start + matched.chars().count();
Some((caret, caret))
}
pub(super) fn refresh(&mut self, text: &str, cursor: usize, words: &[String]) {
if self.suppressed {
self.open = false;
return;
}
let (start, end) = completion::current_word(text, cursor);
if start == end {
self.open = false;
return;
}
let word: String = text.chars().skip(start).take(end - start).collect();
let found: Vec<String> = words
.iter()
.filter(|candidate| candidate.starts_with(&word))
.cloned()
.collect();
if found.iter().all(|candidate| *candidate == word) {
self.open = false;
return;
}
if !self.open {
self.selected = 0;
}
self.selected = self.selected.min(found.len() - 1);
self.matches = found;
self.open = true;
}
pub(super) const fn visible(&self) -> bool {
self.open && !self.matches.is_empty()
}
pub(super) fn draw(
&self,
ctx: &Context,
galley: &egui::Galley,
galley_pos: egui::Pos2,
cursor: usize,
id_source: impl std::hash::Hash + std::fmt::Debug,
) {
let cursor_rect = galley.pos_from_cursor(egui::text::CCursor::new(cursor));
let popup_pos = galley_pos + cursor_rect.left_bottom().to_vec2();
Area::new(Id::new("idet-core-suggest").with(id_source))
.order(Order::Foreground)
.fixed_pos(popup_pos)
.show(ctx, |ui| {
Frame::popup(ui.style()).show(ui, |ui| {
for (index, candidate) in self.matches.iter().enumerate() {
let mut label = RichText::new(candidate.as_str()).monospace();
if index == self.selected {
label = label
.strong()
.background_color(ui.visuals().selection.bg_fill);
}
ui.add(Label::new(label).wrap_mode(TextWrapMode::Extend));
}
});
});
}
}