1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Autocomplete popup controller for the TUI.
//!
//! Manages a popup overlay showing completion suggestions.
/// A single completion item.
#[derive(Debug, Clone)]
pub struct CompletionItem {
/// The text to insert on selection.
pub text: String,
/// Display label (may differ from insertion text).
pub label: String,
/// Optional description shown alongside the label.
pub description: Option<String>,
}
/// Controller for the autocomplete popup overlay.
pub struct AutocompletePopupController {
items: Vec<CompletionItem>,
selected: usize,
visible: bool,
}
impl AutocompletePopupController {
/// Create a new hidden autocomplete popup.
pub fn new() -> Self {
Self {
items: Vec::new(),
selected: 0,
visible: false,
}
}
/// Whether the popup is currently visible.
pub fn visible(&self) -> bool {
self.visible
}
/// The completion items.
pub fn items(&self) -> &[CompletionItem] {
&self.items
}
/// The currently selected index.
pub fn selected_index(&self) -> usize {
self.selected
}
/// Show the popup with the given completion items.
pub fn show(&mut self, items: Vec<CompletionItem>) {
self.items = items;
self.selected = 0;
self.visible = !self.items.is_empty();
}
/// Hide the popup.
pub fn hide(&mut self) {
self.visible = false;
self.items.clear();
self.selected = 0;
}
/// Move selection to the next item (wrapping).
pub fn next(&mut self) {
if self.items.is_empty() {
return;
}
self.selected = (self.selected + 1) % self.items.len();
}
/// Move selection to the previous item (wrapping).
pub fn prev(&mut self) {
if self.items.is_empty() {
return;
}
self.selected = (self.selected + self.items.len() - 1) % self.items.len();
}
/// Confirm the current selection and hide the popup.
///
/// Returns `None` if items list is empty or popup is hidden.
pub fn select(&mut self) -> Option<&CompletionItem> {
if !self.visible || self.items.is_empty() {
return None;
}
self.visible = false;
Some(&self.items[self.selected])
}
}
impl Default for AutocompletePopupController {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "autocomplete_popup_tests.rs"]
mod tests;