1 use crate::app::command::Command;
2 use crate::app::launcher::App;
3 use crate::app::mode::Mode;
4 use crate::app::prompt::OutputType;
5 use crate::app::selection::Selection;
6 use crate::app::tab::Tab;
7 use crate::app::util;
8 use crate::gpg::key::KeyType;
9 use crate::term::tui::Tui;
10 use crate::widget::row::ScrollDirection;
11 use anyhow::Result;
12 use crossterm::event::{KeyCode as Key, KeyEvent, KeyModifiers as Modifiers};
13 use std::str::FromStr;
14 use tui::backend::Backend;
15 
16 /// Handles the key events and executes the application command.
handle_events<B: Backend>( key_event: KeyEvent, tui: &mut Tui<B>, app: &mut App, ) -> Result<()>17 pub fn handle_events<B: Backend>(
18 	key_event: KeyEvent,
19 	tui: &mut Tui<B>,
20 	app: &mut App,
21 ) -> Result<()> {
22 	handle_command_execution(handle_key_event(key_event, app), tui, app)
23 }
24 
25 /// Returns the corresponding application command for a key event.
handle_key_event(key_event: KeyEvent, app: &mut App) -> Command26 fn handle_key_event(key_event: KeyEvent, app: &mut App) -> Command {
27 	let mut command = Command::None;
28 	if app.prompt.is_enabled() {
29 		match key_event.code {
30 			Key::Char(c) => {
31 				app.prompt.text.push(c);
32 				if app.prompt.is_search_enabled() {
33 					app.keys_table.reset_state();
34 				}
35 			}
36 			Key::Up => app.prompt.previous(),
37 			Key::Down => app.prompt.next(),
38 			Key::Tab => {
39 				if app.prompt.is_command_input_enabled() {
40 					app.prompt.enable_search();
41 				} else if app.prompt.is_search_enabled() {
42 					app.prompt.enable_command_input();
43 					app.keys_table.items = app.keys_table.default_items.clone();
44 				}
45 			}
46 			Key::Backspace => {
47 				app.prompt.text.pop();
48 				if app.prompt.is_search_enabled() {
49 					app.keys_table.reset_state();
50 				}
51 			}
52 			Key::Esc => {
53 				app.prompt.clear();
54 				if app.prompt.is_search_enabled() {
55 					app.keys_table.reset_state();
56 				}
57 			}
58 			Key::Enter => {
59 				if app.prompt.is_search_enabled() || app.prompt.text.len() < 2 {
60 					app.prompt.clear();
61 				} else if let Ok(cmd) = Command::from_str(&app.prompt.text) {
62 					app.prompt.history.push(app.prompt.text.clone());
63 					app.prompt.clear();
64 					command = cmd;
65 				} else {
66 					app.prompt.set_output((
67 						OutputType::Failure,
68 						format!(
69 							"invalid command: {}",
70 							app.prompt.text.replacen(':', "", 1)
71 						),
72 					));
73 				}
74 			}
75 			_ => {}
76 		}
77 	} else {
78 		command = match key_event.code {
79 			Key::Char('?') => Command::ShowHelp,
80 			Key::Char('q') | Key::Char('Q') => Command::Quit,
81 			Key::Esc => {
82 				if app.mode != Mode::Normal {
83 					Command::SwitchMode(Mode::Normal)
84 				} else if app.state.show_options {
85 					Command::None
86 				} else if app.prompt.command.is_some() {
87 					app.prompt.clear();
88 					Command::None
89 				} else {
90 					Command::Quit
91 				}
92 			}
93 			Key::Char('d') | Key::Char('D') | Key::Backspace => {
94 				if key_event.modifiers == Modifiers::CONTROL
95 					&& key_event.code != Key::Backspace
96 				{
97 					Command::Quit
98 				} else {
99 					match app.keys_table.selected() {
100 						Some(selected_key) => {
101 							Command::Confirm(Box::new(Command::DeleteKey(
102 								match app.tab {
103 									Tab::Keys(key_type) => key_type,
104 									_ => KeyType::Public,
105 								},
106 								selected_key.get_id(),
107 							)))
108 						}
109 						None => Command::ShowOutput(
110 							OutputType::Failure,
111 							String::from("invalid selection"),
112 						),
113 					}
114 				}
115 			}
116 			Key::Char('c') | Key::Char('C') => {
117 				if key_event.modifiers == Modifiers::CONTROL {
118 					Command::Quit
119 				} else {
120 					Command::SwitchMode(Mode::Copy)
121 				}
122 			}
123 			Key::Char('v') | Key::Char('V') => {
124 				if key_event.modifiers == Modifiers::CONTROL {
125 					Command::Paste
126 				} else {
127 					Command::SwitchMode(Mode::Visual)
128 				}
129 			}
130 			Key::Char('p') | Key::Char('P') => Command::Paste,
131 			Key::Char('r') | Key::Char('R') | Key::F(5) => {
132 				if key_event.modifiers == Modifiers::CONTROL {
133 					Command::RefreshKeys
134 				} else {
135 					Command::Refresh
136 				}
137 			}
138 			Key::Up | Key::Char('k') | Key::Char('K') => {
139 				if key_event.modifiers == Modifiers::CONTROL {
140 					Command::Scroll(ScrollDirection::Top, false)
141 				} else {
142 					Command::Scroll(
143 						ScrollDirection::Up(1),
144 						key_event.modifiers == Modifiers::ALT,
145 					)
146 				}
147 			}
148 			Key::Right | Key::Char('l') | Key::Char('L') => {
149 				if key_event.modifiers == Modifiers::ALT {
150 					Command::Scroll(ScrollDirection::Right(1), true)
151 				} else {
152 					Command::NextTab
153 				}
154 			}
155 			Key::Down | Key::Char('j') | Key::Char('J') => {
156 				if key_event.modifiers == Modifiers::CONTROL {
157 					Command::Scroll(ScrollDirection::Bottom, false)
158 				} else {
159 					Command::Scroll(
160 						ScrollDirection::Down(1),
161 						key_event.modifiers == Modifiers::ALT,
162 					)
163 				}
164 			}
165 			Key::Left | Key::Char('h') | Key::Char('H') => {
166 				if key_event.modifiers == Modifiers::ALT {
167 					Command::Scroll(ScrollDirection::Left(1), true)
168 				} else {
169 					Command::PreviousTab
170 				}
171 			}
172 			Key::PageUp => Command::Scroll(ScrollDirection::Top, false),
173 			Key::PageDown => Command::Scroll(ScrollDirection::Bottom, false),
174 			Key::Char('t') | Key::Char('T') => Command::ToggleDetail(true),
175 			Key::Tab => Command::ToggleDetail(false),
176 			Key::Char('`') => Command::Set(
177 				String::from("margin"),
178 				String::from(if app.keys_table_margin == 1 {
179 					"0"
180 				} else {
181 					"1"
182 				}),
183 			),
184 			Key::Char('s') | Key::Char('S') => {
185 				if key_event.modifiers == Modifiers::CONTROL {
186 					Command::ChangeStyle(app.state.style.next())
187 				} else {
188 					match app.keys_table.selected() {
189 						Some(selected_key) => {
190 							Command::SignKey(selected_key.get_id())
191 						}
192 						None => Command::ShowOutput(
193 							OutputType::Failure,
194 							String::from("invalid selection"),
195 						),
196 					}
197 				}
198 			}
199 			Key::Char('e') | Key::Char('E') => {
200 				match app.keys_table.selected() {
201 					Some(selected_key) => {
202 						Command::EditKey(selected_key.get_id())
203 					}
204 					None => Command::ShowOutput(
205 						OutputType::Failure,
206 						String::from("invalid selection"),
207 					),
208 				}
209 			}
210 			Key::Char('x') | Key::Char('X') => {
211 				if app.mode == Mode::Copy {
212 					Command::Copy(Selection::Key)
213 				} else {
214 					match app.keys_table.selected() {
215 						Some(selected_key) => Command::ExportKeys(
216 							match app.tab {
217 								Tab::Keys(key_type) => key_type,
218 								_ => KeyType::Public,
219 							},
220 							vec![selected_key.get_id()],
221 							false,
222 						),
223 						None => Command::ShowOutput(
224 							OutputType::Failure,
225 							String::from("invalid selection"),
226 						),
227 					}
228 				}
229 			}
230 			Key::Char('g') | Key::Char('G') => Command::GenerateKey,
231 			Key::Char('a') | Key::Char('A') => Command::Set(
232 				String::from("armor"),
233 				(!app.gpgme.config.armor).to_string(),
234 			),
235 			Key::Char('n') | Key::Char('N') => {
236 				if app.prompt.command.is_some() {
237 					app.prompt.clear();
238 					Command::None
239 				} else {
240 					Command::SwitchMode(Mode::Normal)
241 				}
242 			}
243 			Key::Char('1') => {
244 				if app.mode == Mode::Copy {
245 					Command::Copy(Selection::TableRow(1))
246 				} else {
247 					Command::Set(
248 						String::from("detail"),
249 						String::from("minimum"),
250 					)
251 				}
252 			}
253 			Key::Char('2') => {
254 				if app.mode == Mode::Copy {
255 					Command::Copy(Selection::TableRow(2))
256 				} else {
257 					Command::Set(
258 						String::from("detail"),
259 						String::from("standard"),
260 					)
261 				}
262 			}
263 			Key::Char('3') => {
264 				Command::Set(String::from("detail"), String::from("full"))
265 			}
266 			Key::Char('i') | Key::Char('I') => {
267 				if app.mode == Mode::Copy {
268 					Command::Copy(Selection::KeyId)
269 				} else {
270 					Command::Set(
271 						String::from("prompt"),
272 						String::from(":import "),
273 					)
274 				}
275 			}
276 			Key::Char('f') | Key::Char('F') => {
277 				if app.mode == Mode::Copy {
278 					Command::Copy(Selection::KeyFingerprint)
279 				} else {
280 					Command::Set(
281 						String::from("prompt"),
282 						String::from(":receive "),
283 					)
284 				}
285 			}
286 			Key::Char('u') | Key::Char('U') => {
287 				if app.mode == Mode::Copy {
288 					Command::Copy(Selection::KeyUserId)
289 				} else {
290 					match app.keys_table.selected() {
291 						Some(selected_key) => Command::Confirm(Box::new(
292 							Command::SendKey(selected_key.get_id()),
293 						)),
294 						None => Command::ShowOutput(
295 							OutputType::Failure,
296 							String::from("invalid selection"),
297 						),
298 					}
299 				}
300 			}
301 			Key::Char('m') | Key::Char('M') => Command::ToggleTableSize,
302 			Key::Char('y') | Key::Char('Y') => {
303 				if let Some(command) = &app.prompt.command {
304 					command.clone()
305 				} else {
306 					Command::None
307 				}
308 			}
309 			Key::Char('o') | Key::Char(' ') | Key::Enter => {
310 				if let Some(select_type) = app.state.select {
311 					Command::Copy(select_type)
312 				} else if app.state.show_options {
313 					app.options.selected().cloned().unwrap_or(Command::None)
314 				} else {
315 					Command::ShowOptions
316 				}
317 			}
318 			Key::Char(':') => Command::EnableInput,
319 			Key::Char('/') => Command::Search(None),
320 			_ => Command::None,
321 		};
322 	}
323 	command
324 }
325 
326 /// Handles the execution of an application command.
327 ///
328 /// It checks the additional conditions for determining
329 /// if the execution of the given command is applicable.
330 /// For example, depending on the command, it toggles the
331 /// [`paused`] state of [`Tui`] or enables/disables the mouse capture.
332 ///
333 /// [`Tui`]: Tui
334 /// [`paused`]: Tui::paused
handle_command_execution<B: Backend>( mut command: Command, tui: &mut Tui<B>, app: &mut App, ) -> Result<()>335 fn handle_command_execution<B: Backend>(
336 	mut command: Command,
337 	tui: &mut Tui<B>,
338 	app: &mut App,
339 ) -> Result<()> {
340 	if app.state.show_splash && command != Command::Quit {
341 		command = Command::None;
342 	}
343 	if let Tab::Help = app.tab {
344 		match command {
345 			Command::ShowOptions
346 			| Command::ChangeStyle(_)
347 			| Command::Scroll(_, _)
348 			| Command::ListKeys(_)
349 			| Command::SwitchMode(_)
350 			| Command::Paste
351 			| Command::EnableInput
352 			| Command::NextTab
353 			| Command::PreviousTab
354 			| Command::Refresh
355 			| Command::Quit
356 			| Command::None => {}
357 			Command::Set(ref option, _) => {
358 				if option != "style" {
359 					command = Command::None
360 				}
361 			}
362 			_ => command = Command::None,
363 		}
364 	}
365 	let mut toggle_pause = false;
366 	match command {
367 		Command::SwitchMode(Mode::Normal) | Command::Refresh => {
368 			tui.enable_mouse_capture()?
369 		}
370 		Command::SwitchMode(Mode::Visual) => tui.disable_mouse_capture()?,
371 		Command::Set(ref option, ref value) => {
372 			if option == "mode" {
373 				match Mode::from_str(value) {
374 					Ok(Mode::Normal) => tui.enable_mouse_capture()?,
375 					Ok(Mode::Visual) => tui.disable_mouse_capture()?,
376 					_ => {}
377 				}
378 			} else if option == "prompt" && value == ":import " {
379 				tui.toggle_pause()?;
380 				toggle_pause = true;
381 				match util::run_os_command(&app.state.file_explorer) {
382 					Ok(files) => {
383 						command = Command::ImportKeys(files, false);
384 					}
385 					Err(e) => eprintln!("{:?}", e),
386 				}
387 			}
388 		}
389 		Command::ExportKeys(_, _, _)
390 		| Command::DeleteKey(_, _)
391 		| Command::GenerateKey
392 		| Command::RefreshKeys
393 		| Command::EditKey(_)
394 		| Command::SignKey(_)
395 		| Command::ImportKeys(_, true) => {
396 			tui.toggle_pause()?;
397 			toggle_pause = true;
398 		}
399 		Command::Copy(Selection::Key) => {
400 			if app.gpgme.config.armor {
401 				tui.toggle_pause()?;
402 				toggle_pause = true;
403 			} else {
404 				command = Command::ShowOutput(
405 					OutputType::Warning,
406 					String::from(
407 						"enable armored output for copying the exported key(s)",
408 					),
409 				);
410 			}
411 		}
412 		_ => {}
413 	}
414 	app.run_command(command)?;
415 	if toggle_pause {
416 		tui.toggle_pause()?;
417 	}
418 	Ok(())
419 }
420 
421 #[cfg(feature = "gpg-tests")]
422 #[cfg(test)]
423 mod tests {
424 	use super::*;
425 	use crate::app::command::Command;
426 	use crate::app::style::Style;
427 	use crate::args::Args;
428 	use crate::gpg::config::GpgConfig;
429 	use crate::gpg::context::GpgContext;
430 	use pretty_assertions::assert_eq;
431 	use std::env;
432 	#[test]
test_app_handler() -> Result<()>433 	fn test_app_handler() -> Result<()> {
434 		env::set_var(
435 			"GNUPGHOME",
436 			dirs_next::cache_dir()
437 				.unwrap()
438 				.join(env!("CARGO_PKG_NAME"))
439 				.to_str()
440 				.unwrap(),
441 		);
442 		let args = Args::default();
443 		let config = GpgConfig::new(&args)?;
444 		let mut context = GpgContext::new(config)?;
445 		let mut app = App::new(&mut context, &args)?;
446 		let key_id = app.gpgme.get_all_keys()?.get(&KeyType::Public).unwrap()
447 			[0]
448 		.get_id();
449 		let test_cases = vec![
450 			(
451 				Command::Confirm(Box::new(Command::DeleteKey(
452 					KeyType::Public,
453 					key_id.to_string(),
454 				))),
455 				vec![
456 					KeyEvent::new(Key::Char('d'), Modifiers::NONE),
457 					KeyEvent::new(Key::Backspace, Modifiers::NONE),
458 				],
459 			),
460 			(
461 				Command::Confirm(Box::new(Command::SendKey(
462 					key_id.to_string(),
463 				))),
464 				vec![KeyEvent::new(Key::Char('u'), Modifiers::NONE)],
465 			),
466 			(
467 				Command::ExportKeys(
468 					KeyType::Public,
469 					vec![key_id.to_string()],
470 					false,
471 				),
472 				vec![KeyEvent::new(Key::Char('x'), Modifiers::NONE)],
473 			),
474 			(
475 				Command::EditKey(key_id.to_string()),
476 				vec![KeyEvent::new(Key::Char('e'), Modifiers::NONE)],
477 			),
478 			(
479 				Command::SignKey(key_id),
480 				vec![KeyEvent::new(Key::Char('s'), Modifiers::NONE)],
481 			),
482 			(
483 				Command::ShowHelp,
484 				vec![KeyEvent::new(Key::Char('?'), Modifiers::NONE)],
485 			),
486 			(
487 				Command::ShowOptions,
488 				vec![
489 					KeyEvent::new(Key::Char('o'), Modifiers::NONE),
490 					KeyEvent::new(Key::Char(' '), Modifiers::NONE),
491 					KeyEvent::new(Key::Enter, Modifiers::NONE),
492 				],
493 			),
494 			(
495 				Command::GenerateKey,
496 				vec![KeyEvent::new(Key::Char('g'), Modifiers::NONE)],
497 			),
498 			(
499 				Command::RefreshKeys,
500 				vec![KeyEvent::new(Key::Char('r'), Modifiers::CONTROL)],
501 			),
502 			(
503 				Command::ToggleDetail(true),
504 				vec![KeyEvent::new(Key::Char('t'), Modifiers::NONE)],
505 			),
506 			(
507 				Command::ToggleDetail(false),
508 				vec![KeyEvent::new(Key::Tab, Modifiers::NONE)],
509 			),
510 			(
511 				Command::Scroll(ScrollDirection::Top, false),
512 				vec![
513 					KeyEvent::new(Key::Up, Modifiers::CONTROL),
514 					KeyEvent::new(Key::Char('k'), Modifiers::CONTROL),
515 					KeyEvent::new(Key::PageUp, Modifiers::NONE),
516 				],
517 			),
518 			(
519 				Command::Scroll(ScrollDirection::Up(1), false),
520 				vec![
521 					KeyEvent::new(Key::Up, Modifiers::NONE),
522 					KeyEvent::new(Key::Char('k'), Modifiers::NONE),
523 				],
524 			),
525 			(
526 				Command::Scroll(ScrollDirection::Right(1), true),
527 				vec![
528 					KeyEvent::new(Key::Right, Modifiers::ALT),
529 					KeyEvent::new(Key::Char('l'), Modifiers::ALT),
530 				],
531 			),
532 			(
533 				Command::Scroll(ScrollDirection::Bottom, false),
534 				vec![
535 					KeyEvent::new(Key::Down, Modifiers::CONTROL),
536 					KeyEvent::new(Key::Char('j'), Modifiers::CONTROL),
537 					KeyEvent::new(Key::PageDown, Modifiers::NONE),
538 				],
539 			),
540 			(
541 				Command::Scroll(ScrollDirection::Down(1), false),
542 				vec![
543 					KeyEvent::new(Key::Down, Modifiers::NONE),
544 					KeyEvent::new(Key::Char('j'), Modifiers::NONE),
545 				],
546 			),
547 			(
548 				Command::Scroll(ScrollDirection::Left(1), true),
549 				vec![
550 					KeyEvent::new(Key::Left, Modifiers::ALT),
551 					KeyEvent::new(Key::Char('h'), Modifiers::ALT),
552 				],
553 			),
554 			(
555 				Command::Set(String::from("margin"), String::from("0")),
556 				vec![KeyEvent::new(Key::Char('`'), Modifiers::NONE)],
557 			),
558 			(
559 				Command::ChangeStyle(Style::Colored),
560 				vec![KeyEvent::new(Key::Char('s'), Modifiers::CONTROL)],
561 			),
562 			(
563 				Command::Set(String::from("armor"), String::from("true")),
564 				vec![KeyEvent::new(Key::Char('a'), Modifiers::NONE)],
565 			),
566 			(
567 				Command::Set(String::from("detail"), String::from("minimum")),
568 				vec![KeyEvent::new(Key::Char('1'), Modifiers::NONE)],
569 			),
570 			(
571 				Command::Set(String::from("detail"), String::from("standard")),
572 				vec![KeyEvent::new(Key::Char('2'), Modifiers::NONE)],
573 			),
574 			(
575 				Command::Set(String::from("detail"), String::from("full")),
576 				vec![KeyEvent::new(Key::Char('3'), Modifiers::NONE)],
577 			),
578 			(
579 				Command::Set(String::from("prompt"), String::from(":import ")),
580 				vec![KeyEvent::new(Key::Char('i'), Modifiers::NONE)],
581 			),
582 			(
583 				Command::Set(String::from("prompt"), String::from(":receive ")),
584 				vec![KeyEvent::new(Key::Char('f'), Modifiers::NONE)],
585 			),
586 			(
587 				Command::ToggleTableSize,
588 				vec![KeyEvent::new(Key::Char('m'), Modifiers::NONE)],
589 			),
590 			(
591 				Command::SwitchMode(Mode::Normal),
592 				vec![KeyEvent::new(Key::Char('n'), Modifiers::NONE)],
593 			),
594 			(
595 				Command::SwitchMode(Mode::Visual),
596 				vec![KeyEvent::new(Key::Char('v'), Modifiers::NONE)],
597 			),
598 			(
599 				Command::SwitchMode(Mode::Copy),
600 				vec![KeyEvent::new(Key::Char('c'), Modifiers::NONE)],
601 			),
602 			(
603 				Command::Paste,
604 				vec![KeyEvent::new(Key::Char('v'), Modifiers::CONTROL)],
605 			),
606 			(
607 				Command::Paste,
608 				vec![KeyEvent::new(Key::Char('p'), Modifiers::NONE)],
609 			),
610 			(
611 				Command::EnableInput,
612 				vec![KeyEvent::new(Key::Char(':'), Modifiers::CONTROL)],
613 			),
614 			(
615 				Command::Search(None),
616 				vec![KeyEvent::new(Key::Char('/'), Modifiers::CONTROL)],
617 			),
618 			(
619 				Command::NextTab,
620 				vec![
621 					KeyEvent::new(Key::Right, Modifiers::CONTROL),
622 					KeyEvent::new(Key::Char('l'), Modifiers::NONE),
623 				],
624 			),
625 			(
626 				Command::PreviousTab,
627 				vec![
628 					KeyEvent::new(Key::Left, Modifiers::CONTROL),
629 					KeyEvent::new(Key::Char('h'), Modifiers::NONE),
630 				],
631 			),
632 			(
633 				Command::Refresh,
634 				vec![
635 					KeyEvent::new(Key::Char('r'), Modifiers::NONE),
636 					KeyEvent::new(Key::F(5), Modifiers::NONE),
637 				],
638 			),
639 			(
640 				Command::Quit,
641 				vec![
642 					KeyEvent::new(Key::Char('q'), Modifiers::NONE),
643 					KeyEvent::new(Key::Esc, Modifiers::NONE),
644 					KeyEvent::new(Key::Char('d'), Modifiers::CONTROL),
645 					KeyEvent::new(Key::Char('c'), Modifiers::CONTROL),
646 				],
647 			),
648 			(
649 				Command::None,
650 				vec![KeyEvent::new(Key::Char('y'), Modifiers::NONE)],
651 			),
652 			(
653 				Command::None,
654 				vec![KeyEvent::new(Key::Char('ö'), Modifiers::NONE)],
655 			),
656 		];
657 		for (command, key_events) in test_cases {
658 			for key_event in key_events {
659 				assert_eq!(command, handle_key_event(key_event, &mut app));
660 			}
661 		}
662 		app.prompt.enable_command_input();
663 		handle_key_event(KeyEvent::new(Key::Esc, Modifiers::NONE), &mut app);
664 		assert!(!app.prompt.is_enabled());
665 		app.prompt.enable_search();
666 		handle_key_event(KeyEvent::new(Key::Tab, Modifiers::NONE), &mut app);
667 		for c in String::from("normal-").chars() {
668 			handle_key_event(
669 				KeyEvent::new(Key::Char(c), Modifiers::NONE),
670 				&mut app,
671 			);
672 		}
673 		handle_key_event(
674 			KeyEvent::new(Key::Backspace, Modifiers::NONE),
675 			&mut app,
676 		);
677 		assert_eq!(":normal", app.prompt.text);
678 		assert_eq!(
679 			Command::SwitchMode(Mode::Normal),
680 			handle_key_event(
681 				KeyEvent::new(Key::Enter, Modifiers::NONE),
682 				&mut app,
683 			)
684 		);
685 		app.prompt.enable_command_input();
686 		handle_key_event(KeyEvent::new(Key::Down, Modifiers::NONE), &mut app);
687 		handle_key_event(KeyEvent::new(Key::Up, Modifiers::NONE), &mut app);
688 		assert_eq!(":normal", app.prompt.text);
689 		Ok(())
690 	}
691 }
692