1mod columnar_menu;
2mod description_menu;
3mod ide_menu;
4mod list_menu;
5pub mod menu_functions;
6
7use crate::core_editor::Editor;
8use crate::History;
9use crate::{completion::history::HistoryCompleter, painting::Painter, Completer, Suggestion};
10pub use columnar_menu::ColumnarMenu;
11pub use columnar_menu::TraversalDirection;
12pub use description_menu::DescriptionMenu;
13pub use ide_menu::DescriptionMode;
14pub use ide_menu::IdeMenu;
15pub use list_menu::ListMenu;
16use nu_ansi_term::{Color, Style};
17
18pub struct MenuTextStyle {
20 pub selected_text_style: Style,
22 pub text_style: Style,
24 pub description_style: Style,
26 pub selected_match_style: Style,
29 pub match_style: Style,
32}
33
34impl Default for MenuTextStyle {
35 fn default() -> Self {
36 Self {
37 selected_text_style: Color::Green.bold().reverse(),
38 text_style: Color::DarkGray.normal(),
39 description_style: Color::Yellow.normal(),
40 selected_match_style: Color::Green.bold().reverse().underline(),
41 match_style: Style::default().underline(),
42 }
43 }
44}
45
46#[derive(Clone)]
48pub enum MenuEvent {
49 Activate(bool),
52 Deactivate,
54 Edit(bool),
57 NextElement,
59 PreviousElement,
61 MoveUp,
63 MoveDown,
65 MoveLeft,
67 MoveRight,
69 NextPage,
71 PreviousPage,
73}
74
75pub trait Menu: Send {
77 fn settings(&self) -> &MenuSettings {
79 panic!("`settings` requires a manual implementation per menu. It has a base implementation to not break existing menus")
83 }
84
85 fn name(&self) -> &str {
87 &self.settings().name
88 }
89
90 fn indicator(&self) -> &str {
92 &self.settings().marker
93 }
94
95 fn is_active(&self) -> bool;
97
98 fn menu_event(&mut self, event: MenuEvent);
100
101 fn can_quick_complete(&self) -> bool;
104
105 fn can_partially_complete(
108 &mut self,
109 values_updated: bool,
110 editor: &mut Editor,
111 completer: &mut dyn Completer,
112 ) -> bool;
113
114 fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer);
120
121 fn update_working_details(
127 &mut self,
128 editor: &mut Editor,
129 completer: &mut dyn Completer,
130 painter: &Painter,
131 );
132
133 fn replace_in_buffer(&self, editor: &mut Editor);
135
136 fn menu_required_lines(&self, terminal_columns: u16) -> u16;
139
140 fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String;
142
143 fn min_rows(&self) -> u16;
145
146 fn get_values(&self) -> &[Suggestion];
148 fn set_cursor_pos(&mut self, _pos: (u16, u16)) {
150 }
152}
153
154pub struct MenuSettings {
156 name: String,
158 color: MenuTextStyle,
160 marker: String,
162 only_buffer_difference: bool,
165}
166
167impl Default for MenuSettings {
168 fn default() -> Self {
169 Self {
170 name: "menu".to_string(),
171 color: MenuTextStyle::default(),
172 marker: "| ".to_string(),
173 only_buffer_difference: false,
174 }
175 }
176}
177
178impl MenuSettings {
179 #[must_use]
181 pub fn with_name(mut self, name: &str) -> Self {
182 self.name = name.to_string();
183 self
184 }
185
186 #[must_use]
188 pub fn with_color(mut self, color: MenuTextStyle) -> Self {
189 self.color = color;
190 self
191 }
192
193 #[must_use]
195 pub fn with_marker(mut self, marker: &str) -> Self {
196 self.marker = marker.to_string();
197 self
198 }
199
200 #[must_use]
202 pub fn with_only_buffer_difference(mut self, only_buffer_difference: bool) -> Self {
203 self.only_buffer_difference = only_buffer_difference;
204 self
205 }
206}
207
208pub trait MenuBuilder: Menu + Sized {
210 fn settings_mut(&mut self) -> &mut MenuSettings;
213
214 #[must_use]
216 fn with_name(mut self, name: &str) -> Self {
217 self.settings_mut().name = name.to_string();
218 self
219 }
220
221 #[must_use]
223 fn with_text_style(mut self, color: Style) -> Self {
224 self.settings_mut().color.text_style = color;
225 self
226 }
227
228 #[must_use]
230 fn with_selected_text_style(mut self, color: Style) -> Self {
231 self.settings_mut().color.selected_text_style = color;
232 self
233 }
234
235 #[must_use]
237 fn with_description_text_style(mut self, color: Style) -> Self {
238 self.settings_mut().color.description_style = color;
239 self
240 }
241
242 #[must_use]
246 fn with_match_text_style(mut self, color: Style) -> Self {
247 self.settings_mut().color.match_style = color;
248 self
249 }
250
251 #[must_use]
255 fn with_selected_match_text_style(mut self, color: Style) -> Self {
256 self.settings_mut().color.selected_match_style = color;
257 self
258 }
259
260 #[must_use]
262 fn with_marker(mut self, marker: &str) -> Self {
263 self.settings_mut().marker = marker.to_string();
264 self
265 }
266
267 #[must_use]
269 fn with_only_buffer_difference(mut self, only_buffer_difference: bool) -> Self {
270 self.settings_mut().only_buffer_difference = only_buffer_difference;
271 self
272 }
273}
274
275pub enum ReedlineMenu {
277 EngineCompleter(Box<dyn Menu>),
279 HistoryMenu(Box<dyn Menu>),
281 WithCompleter {
283 menu: Box<dyn Menu>,
285 completer: Box<dyn Completer>,
287 },
288}
289
290impl ReedlineMenu {
291 fn as_ref(&self) -> &dyn Menu {
292 match self {
293 Self::EngineCompleter(menu)
294 | Self::HistoryMenu(menu)
295 | Self::WithCompleter { menu, .. } => menu.as_ref(),
296 }
297 }
298
299 fn as_mut(&mut self) -> &mut dyn Menu {
300 match self {
301 Self::EngineCompleter(menu)
302 | Self::HistoryMenu(menu)
303 | Self::WithCompleter { menu, .. } => menu.as_mut(),
304 }
305 }
306
307 pub(crate) fn can_partially_complete(
308 &mut self,
309 values_updated: bool,
310 editor: &mut Editor,
311 completer: &mut dyn Completer,
312 history: &dyn History,
313 ) -> bool {
314 match self {
315 Self::EngineCompleter(menu) => {
316 menu.can_partially_complete(values_updated, editor, completer)
317 }
318 Self::HistoryMenu(menu) => {
319 let mut history_completer = HistoryCompleter::new(history);
320 menu.can_partially_complete(values_updated, editor, &mut history_completer)
321 }
322 Self::WithCompleter {
323 menu,
324 completer: own_completer,
325 } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()),
326 }
327 }
328
329 pub(crate) fn update_values(
330 &mut self,
331 editor: &mut Editor,
332 completer: &mut dyn Completer,
333 history: &dyn History,
334 ) {
335 match self {
336 Self::EngineCompleter(menu) => menu.update_values(editor, completer),
337 Self::HistoryMenu(menu) => {
338 let mut history_completer = HistoryCompleter::new(history);
339 menu.update_values(editor, &mut history_completer);
340 }
341 Self::WithCompleter {
342 menu,
343 completer: own_completer,
344 } => {
345 menu.update_values(editor, own_completer.as_mut());
346 }
347 }
348 }
349
350 pub(crate) fn update_working_details(
351 &mut self,
352 editor: &mut Editor,
353 completer: &mut dyn Completer,
354 history: &dyn History,
355 painter: &Painter,
356 ) {
357 match self {
358 Self::EngineCompleter(menu) => {
359 menu.update_working_details(editor, completer, painter);
360 }
361 Self::HistoryMenu(menu) => {
362 let mut history_completer = HistoryCompleter::new(history);
363 menu.update_working_details(editor, &mut history_completer, painter);
364 }
365 Self::WithCompleter {
366 menu,
367 completer: own_completer,
368 } => {
369 menu.update_working_details(editor, own_completer.as_mut(), painter);
370 }
371 }
372 }
373}
374
375impl Menu for ReedlineMenu {
376 fn settings(&self) -> &MenuSettings {
377 self.as_ref().settings()
378 }
379
380 fn name(&self) -> &str {
381 self.as_ref().name()
382 }
383
384 fn indicator(&self) -> &str {
385 self.as_ref().indicator()
386 }
387
388 fn is_active(&self) -> bool {
389 self.as_ref().is_active()
390 }
391
392 fn menu_event(&mut self, event: MenuEvent) {
393 self.as_mut().menu_event(event);
394 }
395
396 fn can_quick_complete(&self) -> bool {
397 self.as_ref().can_quick_complete()
398 }
399
400 fn can_partially_complete(
401 &mut self,
402 values_updated: bool,
403 editor: &mut Editor,
404 completer: &mut dyn Completer,
405 ) -> bool {
406 match self {
407 Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
408 menu.can_partially_complete(values_updated, editor, completer)
409 }
410 Self::WithCompleter {
411 menu,
412 completer: own_completer,
413 } => menu.can_partially_complete(values_updated, editor, own_completer.as_mut()),
414 }
415 }
416
417 fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
418 match self {
419 Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
420 menu.update_values(editor, completer);
421 }
422 Self::WithCompleter {
423 menu,
424 completer: own_completer,
425 } => {
426 menu.update_values(editor, own_completer.as_mut());
427 }
428 }
429 }
430
431 fn update_working_details(
432 &mut self,
433 editor: &mut Editor,
434 completer: &mut dyn Completer,
435 painter: &Painter,
436 ) {
437 match self {
438 Self::EngineCompleter(menu) | Self::HistoryMenu(menu) => {
439 menu.update_working_details(editor, completer, painter);
440 }
441 Self::WithCompleter {
442 menu,
443 completer: own_completer,
444 } => {
445 menu.update_working_details(editor, own_completer.as_mut(), painter);
446 }
447 }
448 }
449
450 fn replace_in_buffer(&self, editor: &mut Editor) {
451 self.as_ref().replace_in_buffer(editor);
452 }
453
454 fn menu_required_lines(&self, terminal_columns: u16) -> u16 {
455 self.as_ref().menu_required_lines(terminal_columns)
456 }
457
458 fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String {
459 self.as_ref()
460 .menu_string(available_lines, use_ansi_coloring)
461 }
462
463 fn min_rows(&self) -> u16 {
464 self.as_ref().min_rows()
465 }
466
467 fn get_values(&self) -> &[Suggestion] {
468 self.as_ref().get_values()
469 }
470
471 fn set_cursor_pos(&mut self, pos: (u16, u16)) {
472 self.as_mut().set_cursor_pos(pos);
473 }
474}