reedline/menu/
mod.rs

1mod columnar_menu;
2mod list_menu;
3pub mod menu_functions;
4
5use crate::core_editor::Editor;
6use crate::History;
7use crate::{completion::history::HistoryCompleter, painting::Painter, Completer, Suggestion};
8pub use columnar_menu::ColumnarMenu;
9pub use list_menu::ListMenu;
10use nu_ansi_term::{Color, Style};
11
12/// Struct to store the menu style
13pub struct MenuTextStyle {
14    /// Text style for selected text in a menu
15    pub selected_text_style: Style,
16    /// Text style for not selected text in the menu
17    pub text_style: Style,
18    /// Text style for the item description
19    pub description_style: Style,
20}
21
22impl Default for MenuTextStyle {
23    fn default() -> Self {
24        Self {
25            selected_text_style: Color::Green.bold().reverse(),
26            text_style: Color::DarkGray.normal(),
27            description_style: Color::Yellow.normal(),
28        }
29    }
30}
31
32/// Defines all possible events that could happen with a menu.
33#[derive(Clone)]
34pub enum MenuEvent {
35    /// Activation event for the menu. When the bool is true it means that the values
36    /// have already being updated. This is true when the option `quick_completions` is true
37    Activate(bool),
38    /// Deactivation event
39    Deactivate,
40    /// Line buffer edit event. When the bool is true it means that the values
41    /// have already being updated. This is true when the option `quick_completions` is true
42    Edit(bool),
43    /// Selecting next element in the menu
44    NextElement,
45    /// Selecting previous element in the menu
46    PreviousElement,
47    /// Moving up in the menu
48    MoveUp,
49    /// Moving down in the menu
50    MoveDown,
51    /// Moving left in the menu
52    MoveLeft,
53    /// Moving right in the menu
54    MoveRight,
55    /// Move to next page
56    NextPage,
57    /// Move to previous page
58    PreviousPage,
59}
60
61/// Trait that defines how a menu will be printed by the painter
62pub trait Menu: Send {
63    /// Menu name
64    fn name(&self) -> &str;
65
66    /// Menu indicator
67    fn indicator(&self) -> &str;
68
69    /// Checks if the menu is active
70    fn is_active(&self) -> bool;
71
72    /// Selects what type of event happened with the menu
73    fn menu_event(&mut self, event: MenuEvent);
74
75    /// A menu may not be allowed to quick complete because it needs to stay
76    /// active even with one element
77    fn can_quick_complete(&self) -> bool;
78
79    /// The completion menu can try to find the common string and replace it
80    /// in the given line buffer
81    fn can_partially_complete(
82        &mut self,
83        values_updated: bool,
84        editor: &mut Editor,
85        completer: &mut dyn Completer,
86    ) -> bool;
87
88    /// Updates the values presented in the menu
89    /// This function needs to be defined in the trait because when the menu is
90    /// activated or the `quick_completion` option is true, the len of the values
91    /// is calculated to know if there is only one value so it can be selected
92    /// immediately
93    fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer);
94
95    /// The working details of a menu are values that could change based on
96    /// the menu conditions before it being printed, such as the number or size
97    /// of columns, etc.
98    /// In this function should be defined how the menu event is treated since
99    /// it is called just before painting the menu
100    fn update_working_details(
101        &mut self,
102        editor: &mut Editor,
103        completer: &mut dyn Completer,
104        painter: &Painter,
105    );
106
107    /// Indicates how to replace in the line buffer the selected value from the menu
108    fn replace_in_buffer(&self, editor: &mut Editor);
109
110    /// Calculates the real required lines for the menu considering how many lines
111    /// wrap the terminal or if entries have multiple lines
112    fn menu_required_lines(&self, terminal_columns: u16) -> u16;
113
114    /// Creates the menu representation as a string which will be painted by the painter
115    fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String;
116
117    /// Minimum rows that should be displayed by the menu
118    fn min_rows(&self) -> u16;
119
120    /// Gets cached values from menu that will be displayed
121    fn get_values(&self) -> &[Suggestion];
122}
123
124/// Allowed menus in Reedline
125pub enum ReedlineMenu {
126    /// Menu that uses Reedline's completer to update its values
127    EngineCompleter(Box<dyn Menu>),
128    /// Menu that uses the history as its completer
129    HistoryMenu(Box<dyn Menu>),
130    /// Menu that has its own Completer
131    WithCompleter {
132        /// Base menu
133        menu: Box<dyn Menu>,
134        /// External completer defined outside Reedline
135        completer: Box<dyn Completer>,
136    },
137}
138
139impl ReedlineMenu {
140    fn as_ref(&self) -> &dyn Menu {
141        match self {
142            Self::EngineCompleter(menu)
143            | Self::HistoryMenu(menu)
144            | Self::WithCompleter { menu, .. } => menu.as_ref(),
145        }
146    }
147
148    fn as_mut(&mut self) -> &mut dyn Menu {
149        match self {
150            Self::EngineCompleter(menu)
151            | Self::HistoryMenu(menu)
152            | Self::WithCompleter { menu, .. } => menu.as_mut(),
153        }
154    }
155
156    pub(crate) fn can_partially_complete(
157        &mut self,
158        values_updated: bool,
159        editor: &mut Editor,
160        completer: &mut dyn Completer,
161        history: &dyn History,
162    ) -> bool {
163        match self {
164            Self::EngineCompleter(menu) => {
165                menu.can_partially_complete(values_updated, editor, completer)
166            }
167            Self::HistoryMenu(menu) => {
168                let mut history_completer = HistoryCompleter::new(history);
169                menu.can_partially_complete(values_updated, editor, &mut history_completer)
170            }
171            Self::WithCompleter {
172                menu,
173                completer: own_completer,
174            } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()),
175        }
176    }
177
178    pub(crate) fn update_values(
179        &mut self,
180        editor: &mut Editor,
181        completer: &mut dyn Completer,
182        history: &dyn History,
183    ) {
184        match self {
185            Self::EngineCompleter(menu) => menu.update_values(editor, completer),
186            Self::HistoryMenu(menu) => {
187                let mut history_completer = HistoryCompleter::new(history);
188                menu.update_values(editor, &mut history_completer);
189            }
190            Self::WithCompleter {
191                menu,
192                completer: own_completer,
193            } => {
194                menu.update_values(editor, own_completer.as_mut());
195            }
196        }
197    }
198
199    pub(crate) fn update_working_details(
200        &mut self,
201        editor: &mut Editor,
202        completer: &mut dyn Completer,
203        history: &dyn History,
204        painter: &Painter,
205    ) {
206        match self {
207            Self::EngineCompleter(menu) => {
208                menu.update_working_details(editor, completer, painter);
209            }
210            Self::HistoryMenu(menu) => {
211                let mut history_completer = HistoryCompleter::new(history);
212                menu.update_working_details(editor, &mut history_completer, painter);
213            }
214            Self::WithCompleter {
215                menu,
216                completer: own_completer,
217            } => {
218                menu.update_working_details(editor, own_completer.as_mut(), painter);
219            }
220        }
221    }
222}
223
224impl Menu for ReedlineMenu {
225    fn name(&self) -> &str {
226        self.as_ref().name()
227    }
228
229    fn indicator(&self) -> &str {
230        self.as_ref().indicator()
231    }
232
233    fn is_active(&self) -> bool {
234        self.as_ref().is_active()
235    }
236
237    fn menu_event(&mut self, event: MenuEvent) {
238        self.as_mut().menu_event(event);
239    }
240
241    fn can_quick_complete(&self) -> bool {
242        self.as_ref().can_quick_complete()
243    }
244
245    fn can_partially_complete(
246        &mut self,
247        values_updated: bool,
248        editor: &mut Editor,
249        completer: &mut dyn Completer,
250    ) -> bool {
251        match self {
252            Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
253                menu.can_partially_complete(values_updated, editor, completer)
254            }
255            Self::WithCompleter {
256                menu,
257                completer: own_completer,
258            } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()),
259        }
260    }
261
262    fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
263        match self {
264            Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
265                menu.update_values(editor, completer);
266            }
267            Self::WithCompleter {
268                menu,
269                completer: own_completer,
270            } => {
271                menu.update_values(editor, own_completer.as_mut());
272            }
273        }
274    }
275
276    fn update_working_details(
277        &mut self,
278        editor: &mut Editor,
279        completer: &mut dyn Completer,
280        painter: &Painter,
281    ) {
282        match self {
283            Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
284                menu.update_working_details(editor, completer, painter);
285            }
286            Self::WithCompleter {
287                menu,
288                completer: own_completer,
289            } => {
290                menu.update_working_details(editor, own_completer.as_mut(), painter);
291            }
292        }
293    }
294
295    fn replace_in_buffer(&self, editor: &mut Editor) {
296        self.as_ref().replace_in_buffer(editor);
297    }
298
299    fn menu_required_lines(&self, terminal_columns: u16) -> u16 {
300        self.as_ref().menu_required_lines(terminal_columns)
301    }
302
303    fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String {
304        self.as_ref()
305            .menu_string(available_lines, use_ansi_coloring)
306    }
307
308    fn min_rows(&self) -> u16 {
309        self.as_ref().min_rows()
310    }
311
312    fn get_values(&self) -> &[Suggestion] {
313        self.as_ref().get_values()
314    }
315}