1use crate::command::ReplCommand;
2use crate::completer::ReplCompleter;
3use crate::error::*;
4use crate::prompt::ReplPrompt;
5use crate::{paint_green_bold, paint_yellow_bold, AfterCommandCallback, Callback};
6#[cfg(feature = "async")]
7use crate::{AsyncAfterCommandCallback, AsyncCallback};
8use clap::Command;
9use reedline::MenuBuilder;
10use nu_ansi_term::{Color, Style};
12use reedline::{
13 self, default_emacs_keybindings, ColumnarMenu, DefaultHinter, DefaultValidator, Emacs,
14 ExampleHighlighter, ExternalPrinter, FileBackedHistory, KeyCode, KeyModifiers, Keybindings,
15 Reedline, ReedlineEvent, ReedlineMenu, Signal,
16};
17use std::boxed::Box;
18use std::collections::HashMap;
19use std::fmt::Display;
20use std::path::PathBuf;
21
22type ErrorHandler<Context, E> = fn(error: E, repl: &Repl<Context, E>) -> Result<()>;
23
24fn default_error_handler<Context, E: Display>(error: E, _repl: &Repl<Context, E>) -> Result<()> {
25 eprintln!("{}", error);
26 Ok(())
27}
28
29pub struct Repl<Context, E: Display> {
31 name: String,
32 banner: Option<String>,
33 version: String,
34 description: String,
35 prompt: ReplPrompt,
36 after_command_callback: Option<AfterCommandCallback<Context, E>>,
37 #[cfg(feature = "async")]
38 after_command_callback_async: Option<AsyncAfterCommandCallback<Context, E>>,
39 commands: HashMap<String, ReplCommand<Context, E>>,
40 history: Option<PathBuf>,
41 history_capacity: Option<usize>,
42 context: Context,
43 keybindings: Keybindings,
44 external_printer: ExternalPrinter<String>,
45 hinter_style: Style,
46 hinter_enabled: bool,
47 quick_completions: bool,
48 partial_completions: bool,
49 stop_on_ctrl_c: bool,
50 stop_on_ctrl_d: bool,
51 error_handler: ErrorHandler<Context, E>,
52}
53
54impl<Context, E> Repl<Context, E>
55where
56 E: Display + From<Error> + std::fmt::Debug,
57{
58 pub fn new(context: Context) -> Self {
60 let name = String::from("repl");
61 let style = Style::new().italic().fg(Color::LightGray);
62 let mut keybindings = default_emacs_keybindings();
63 keybindings.add_binding(
64 KeyModifiers::NONE,
65 KeyCode::Tab,
66 ReedlineEvent::Menu("completion_menu".to_string()),
67 );
68 let prompt = ReplPrompt::new(&paint_green_bold(&format!("{}> ", name)));
69
70 Self {
71 name,
72 banner: None,
73 version: String::new(),
74 description: String::new(),
75 commands: HashMap::new(),
76 history: None,
77 history_capacity: None,
78 after_command_callback: None,
79 #[cfg(feature = "async")]
80 after_command_callback_async: None,
81 quick_completions: true,
82 partial_completions: false,
83 hinter_enabled: true,
84 hinter_style: style,
85 prompt,
86 context,
87 keybindings,
88 external_printer: ExternalPrinter::new(2048),
89 stop_on_ctrl_c: false,
90 stop_on_ctrl_d: true,
91 error_handler: default_error_handler,
92 }
93 }
94
95 pub fn with_name(mut self, name: &str) -> Self {
97 self.name = name.to_string();
98 self.with_formatted_prompt(name)
99 }
100
101 pub fn with_banner(mut self, banner: &str) -> Self {
103 self.banner = Some(banner.to_string());
104
105 self
106 }
107
108 pub fn with_version(mut self, version: &str) -> Self {
110 self.version = version.to_string();
111
112 self
113 }
114
115 pub fn with_description(mut self, description: &str) -> Self {
117 self.description = description.to_string();
118
119 self
120 }
121
122 pub fn with_on_after_command(mut self, callback: AfterCommandCallback<Context, E>) -> Self {
124 self.after_command_callback = Some(callback);
125
126 self
127 }
128
129 #[cfg(feature = "async")]
131 pub fn with_on_after_command_async(
132 mut self,
133 callback: AsyncAfterCommandCallback<Context, E>,
134 ) -> Self {
135 self.after_command_callback_async = Some(callback);
136
137 self
138 }
139
140 pub fn with_history(mut self, history_path: PathBuf, capacity: usize) -> Self {
142 self.history = Some(history_path);
143 self.history_capacity = Some(capacity);
144
145 self
146 }
147
148 pub fn with_prompt(mut self, prompt: &str) -> Self {
153 self.prompt.update_prefix(prompt);
154
155 self
156 }
157
158 pub fn with_formatted_prompt(mut self, prompt: &str) -> Self {
162 self.prompt.update_prefix(prompt);
163
164 self
165 }
166
167 pub fn with_error_handler(mut self, handler: ErrorHandler<Context, E>) -> Self {
170 self.error_handler = handler;
171
172 self
173 }
174
175 pub fn with_stop_on_ctrl_c(mut self, stop_on_ctrl_c: bool) -> Self {
177 self.stop_on_ctrl_c = stop_on_ctrl_c;
178
179 self
180 }
181
182 pub fn with_stop_on_ctrl_d(mut self, stop_on_ctrl_d: bool) -> Self {
184 self.stop_on_ctrl_d = stop_on_ctrl_d;
185
186 self
187 }
188
189 pub fn with_quick_completions(mut self, quick_completions: bool) -> Self {
192 self.quick_completions = quick_completions;
193
194 self
195 }
196
197 pub fn with_partial_completions(mut self, partial_completions: bool) -> Self {
200 self.partial_completions = partial_completions;
201
202 self
203 }
204
205 pub fn with_hinter_style(mut self, style: Style) -> Self {
210 self.hinter_style = style;
211
212 self
213 }
214
215 pub fn with_hinter_disabled(mut self) -> Self {
217 self.hinter_enabled = false;
218
219 self
220 }
221
222 pub fn with_keybinding(
228 mut self,
229 modifier: KeyModifiers,
230 key_code: KeyCode,
231 command: ReedlineEvent,
232 ) -> Self {
233 self.keybindings.add_binding(modifier, key_code, command);
234
235 self
236 }
237
238 pub fn find_keybinding(
240 &self,
241 modifier: KeyModifiers,
242 key_code: KeyCode,
243 ) -> Option<ReedlineEvent> {
244 self.keybindings.find_binding(modifier, key_code)
245 }
246
247 pub fn get_keybindings(&self) -> HashMap<(KeyModifiers, KeyCode), ReedlineEvent> {
249 self.keybindings
251 .get_keybindings()
252 .iter()
253 .map(|(key, value)| ((key.modifier, key.key_code), value.clone()))
254 .collect()
255 }
256
257 pub fn without_keybinding(mut self, modifier: KeyModifiers, key_code: KeyCode) -> Self {
261 self.keybindings.remove_binding(modifier, key_code);
262
263 self
264 }
265
266 pub fn external_printer(&self) -> ExternalPrinter<String> {
282 self.external_printer.clone()
283 }
284
285 pub fn with_command(mut self, command: Command, callback: Callback<Context, E>) -> Self {
287 let name = command.get_name().to_string();
288 self.commands
289 .insert(name.clone(), ReplCommand::new(&name, command, callback));
290 self
291 }
292
293 #[cfg(feature = "async_derive")]
294 pub fn with_async_derived<Clap: clap::Parser>(
295 mut self,
296 callbacks: crate::AsyncCallBackMap<Context, E>,
297 ) -> Self {
298 let derived = Clap::command();
299
300 self = self.with_name(derived.get_name());
301
302 if let Some(version) = derived.get_version() {
303 self = self.with_version(version);
304 }
305
306 if let Some(desc) = derived.get_about() {
307 self = self.with_description(&desc.to_string());
308 }
309
310 let commands = derived.get_subcommands();
311
312 for command in commands {
313 let name = command.get_name();
314 let Some(callback) = callbacks.get(name) else {
315 continue;
316 };
317 let cmd: ReplCommand<Context, E> =
318 ReplCommand::new_async(&name, command.clone(), *callback);
319
320 self.commands.insert(name.to_string(), cmd);
321 }
322
323 self
324 }
325
326 #[cfg(feature = "derive")]
328 pub fn with_derived<Clap: clap::Parser>(
329 mut self,
330 callbacks: crate::CallBackMap<Context, E>,
331 ) -> Self {
332 let derived = Clap::command();
333
334 self = self.with_name(derived.get_name());
335
336 if let Some(version) = derived.get_version() {
337 self = self.with_version(version);
338 }
339
340 if let Some(desc) = derived.get_about() {
341 self = self.with_description(&desc.to_string());
342 }
343
344 let commands = derived.get_subcommands();
345
346 for command in commands {
347 let name = command.get_name();
348 let Some(callback) = callbacks.get(name) else {
349 continue;
350 };
351 let cmd: ReplCommand<Context, E> = ReplCommand::new(name, command.clone(), *callback);
352
353 self.commands.insert(name.to_string(), cmd);
354 }
355
356 self
357 }
358
359 #[cfg(feature = "async")]
361 pub fn with_command_async(
362 mut self,
363 command: Command,
364 callback: AsyncCallback<Context, E>,
365 ) -> Self {
366 let name = command.get_name().to_string();
367 self.commands.insert(
368 name.clone(),
369 ReplCommand::new_async(&name, command, callback),
370 );
371 self
372 }
373
374 fn show_help(&self, args: &[&str]) -> Result<()> {
375 if args.is_empty() {
376 let mut app = Command::new("app");
377
378 for (_, com) in self.commands.iter() {
379 app = app.subcommand(com.command.clone());
380 }
381 let mut help_bytes: Vec<u8> = Vec::new();
382 app.write_help(&mut help_bytes)
383 .expect("failed to print help");
384 let mut help_string =
385 String::from_utf8(help_bytes).expect("Help message was invalid UTF8");
386 let marker = "SUBCOMMANDS:";
387 if let Some(marker_pos) = help_string.find(marker) {
388 help_string = paint_yellow_bold("COMMANDS:")
389 + &help_string[(marker_pos + marker.len())..help_string.len()];
390 }
391 let header = format!(
392 "{} {}\n{}\n",
393 paint_green_bold(&self.name),
394 self.version,
395 self.description
396 );
397 println!("{}", header);
398 println!("{}", help_string);
399 } else if let Some((_, subcommand)) = self
400 .commands
401 .iter()
402 .find(|(name, _)| name.as_str() == args[0])
403 {
404 subcommand
405 .command
406 .clone()
407 .print_help()
408 .expect("failed to print help");
409 println!();
410 } else {
411 eprintln!("Help not found for command '{}'", args[0]);
412 }
413 Ok(())
414 }
415
416 fn handle_command(&mut self, command: &str, args: &[&str]) -> core::result::Result<(), E> {
417 match self.commands.get(command) {
418 Some(definition) => {
419 let mut argv: Vec<&str> = vec![command];
420 argv.extend(args);
421 match definition.command.clone().try_get_matches_from_mut(argv) {
422 Ok(matches) => match (definition
423 .callback
424 .expect("Must be filled for sync commands"))(
425 matches, &mut self.context
426 ) {
427 Ok(Some(value)) => println!("{}", value),
428 Ok(None) => (),
429 Err(error) => return Err(error),
430 },
431 Err(err) => {
432 err.print().expect("failed to print");
433 }
434 };
435 self.execute_after_command_callback()?;
436 }
437 None => {
438 if command == "help" {
439 self.show_help(args)?;
440 } else {
441 return Err(Error::UnknownCommand(command.to_string()).into());
442 }
443 }
444 }
445
446 Ok(())
447 }
448
449 fn execute_after_command_callback(&mut self) -> core::result::Result<(), E> {
450 if let Some(callback) = self.after_command_callback {
451 match callback(&mut self.context) {
452 Ok(Some(new_prompt)) => {
453 self.prompt.update_prefix(&new_prompt);
454 }
455 Ok(None) => {}
456 Err(err) => {
457 eprintln!("failed to execute after_command_callback {:?}", err);
458 }
459 }
460 }
461
462 Ok(())
463 }
464
465 #[cfg(feature = "async")]
466 async fn execute_after_command_callback_async(&mut self) -> core::result::Result<(), E> {
467 self.execute_after_command_callback()?;
468 if let Some(callback) = self.after_command_callback_async {
469 match callback(&mut self.context).await {
470 Ok(new_prompt) => {
471 if let Some(new_prompt) = new_prompt {
472 self.prompt.update_prefix(&new_prompt);
473 }
474 }
475 Err(err) => {
476 eprintln!("failed to execute after_command_callback {:?}", err);
477 }
478 }
479 }
480
481 Ok(())
482 }
483
484 #[cfg(feature = "async")]
485 async fn handle_command_async(
486 &mut self,
487 command: &str,
488 args: &[&str],
489 ) -> core::result::Result<(), E> {
490 match self.commands.get(command) {
491 Some(definition) => {
492 let mut argv: Vec<&str> = vec![command];
493 argv.extend(args);
494 match definition.command.clone().try_get_matches_from_mut(argv) {
495 Ok(matches) => match if let Some(async_callback) = definition.async_callback {
496 async_callback(matches, &mut self.context).await
497 } else {
498 definition
499 .callback
500 .expect("Either async or sync callback must be set")(
501 matches,
502 &mut self.context,
503 )
504 } {
505 Ok(Some(value)) => println!("{}", value),
506 Ok(None) => (),
507 Err(error) => return Err(error),
508 },
509 Err(err) => {
510 err.print().expect("failed to print");
511 }
512 };
513 self.execute_after_command_callback_async().await?;
514 }
515 None => {
516 if command == "help" {
517 self.show_help(args)?;
518 } else {
519 return Err(Error::UnknownCommand(command.to_string()).into());
520 }
521 }
522 }
523
524 Ok(())
525 }
526
527 #[cfg(not(feature = "shlex"))]
528 fn parse_line(&self, line: &str) -> Option<Vec<String>> {
529 let r = regex::Regex::new(r#"("[^"\n]+"|[\S]+)"#).unwrap();
530 Some(r.captures_iter(line)
531 .map(|a| a[0].to_string().replace('\"', ""))
532 .collect::<Vec<String>>())
533 }
534
535 #[cfg(feature = "shlex")]
536 fn parse_line(&self, line: &str) -> Option<Vec<String>> {
537 shlex::split(line)
538 }
539
540 pub fn process_argv(&mut self, argv: Vec<String>) -> core::result::Result<(), E> {
557 let mut iter = argv.iter();
558 if let Some(command) = iter.next() {
559 self.handle_command(command, &iter.map(AsRef::as_ref).collect::<Vec<&str>>())
560 } else {
561 Ok(())
562 }
563 }
564
565 #[cfg(feature = "async")]
566 pub async fn process_argv_async(&mut self, argv: Vec<String>) -> core::result::Result<(), E> {
568 let mut iter = argv.iter();
569 if let Some(command) = iter.next() {
570 self.handle_command_async(command, &iter.map(AsRef::as_ref).collect::<Vec<&str>>()).await
571 } else {
572 Ok(())
573 }
574 }
575
576 fn process_line(&mut self, line: String) -> core::result::Result<(), E> {
577 if let Some(args) = self.parse_line(&line) {
578 self.process_argv(args)
579 } else {
580 Ok(())
581 }
582 }
583
584 #[cfg(feature = "async")]
585 async fn process_line_async(&mut self, line: String) -> core::result::Result<(), E> {
586 if let Some(args) = self.parse_line(&line) {
587 self.process_argv_async(args).await
588 } else {
589 Ok(())
590 }
591 }
592
593 fn build_line_editor(&mut self) -> Result<Reedline> {
594 let mut valid_commands: Vec<String> = self
595 .commands
596 .iter()
597 .map(|(_, command)| command.name.clone())
598 .collect();
599 valid_commands.push("help".to_string());
600 let completer = Box::new(ReplCompleter::new(&self.commands));
601 let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
602 let validator = Box::new(DefaultValidator);
603 let mut line_editor = Reedline::create()
604 .with_edit_mode(Box::new(Emacs::new(self.keybindings.clone())))
605 .with_completer(completer)
606 .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
607 .with_highlighter(Box::new(ExampleHighlighter::new(valid_commands.clone())))
608 .with_validator(validator)
609 .with_partial_completions(self.partial_completions)
610 .with_quick_completions(self.quick_completions)
611 .with_external_printer(self.external_printer.clone());
612
613 if self.hinter_enabled {
614 line_editor = line_editor.with_hinter(Box::new(
615 DefaultHinter::default().with_style(self.hinter_style),
616 ));
617 }
618
619 if let Some(history_path) = &self.history {
620 let capacity = self.history_capacity.unwrap();
621 let history =
622 FileBackedHistory::with_file(capacity, history_path.to_path_buf()).unwrap();
623 line_editor = line_editor.with_history(Box::new(history));
624 }
625
626 Ok(line_editor)
627 }
628
629 #[cfg(feature = "scripts")]
630 pub fn run_with_reader(&mut self, reader: impl std::io::BufRead) -> Result<()> {
635 let lines = reader.lines();
636 for line in lines {
637 let line = line.expect("failed to read line");
638 if let Err(err) = self.process_line(line) {
639 (self.error_handler)(err, self)?;
640 }
641 }
642
643 Ok(())
644 }
645
646 pub fn run(&mut self) -> Result<()> {
648 enable_virtual_terminal_processing();
649 if let Some(banner) = &self.banner {
650 println!("{}", banner);
651 }
652 let mut line_editor = self.build_line_editor()?;
653
654 loop {
655 let sig = line_editor
656 .read_line(&self.prompt)
657 .expect("failed to read_line");
658 match sig {
659 Signal::Success(line) => {
660 if let Err(err) = self.process_line(line) {
661 (self.error_handler)(err, self)?;
662 }
663 }
664 Signal::CtrlC => {
665 if self.stop_on_ctrl_c {
666 break;
667 }
668 }
669 Signal::CtrlD => {
670 if self.stop_on_ctrl_d {
671 break;
672 }
673 }
674 }
675 }
676 disable_virtual_terminal_processing();
677 Ok(())
678 }
679
680 #[cfg(feature = "async")]
682 pub async fn run_async(&mut self) -> Result<()> {
683 enable_virtual_terminal_processing();
684 if let Some(banner) = &self.banner {
685 println!("{}", banner);
686 }
687 let mut line_editor = self.build_line_editor()?;
688
689 loop {
690 let sig = line_editor
691 .read_line(&self.prompt)
692 .expect("failed to read_line");
693 match sig {
694 Signal::Success(line) => {
695 if let Err(err) = self.process_line_async(line).await {
696 (self.error_handler)(err, self)?;
697 }
698 }
699 Signal::CtrlC => {
700 if self.stop_on_ctrl_c {
701 break;
702 }
703 }
704 Signal::CtrlD => {
705 if self.stop_on_ctrl_d {
706 break;
707 }
708 }
709 }
710 }
711 disable_virtual_terminal_processing();
712 Ok(())
713 }
714}
715
716#[cfg(windows)]
717pub fn enable_virtual_terminal_processing() {
718 use winapi_util::console::Console;
719 if let Ok(mut term) = Console::stdout() {
720 let _guard = term.set_virtual_terminal_processing(true);
721 }
722 if let Ok(mut term) = Console::stderr() {
723 let _guard = term.set_virtual_terminal_processing(true);
724 }
725}
726
727#[cfg(windows)]
728pub fn disable_virtual_terminal_processing() {
729 use winapi_util::console::Console;
730 if let Ok(mut term) = Console::stdout() {
731 let _guard = term.set_virtual_terminal_processing(false);
732 }
733 if let Ok(mut term) = Console::stderr() {
734 let _guard = term.set_virtual_terminal_processing(false);
735 }
736}
737
738#[cfg(not(windows))]
739pub fn enable_virtual_terminal_processing() {
740 }
742
743#[cfg(not(windows))]
744pub fn disable_virtual_terminal_processing() {
745 }