clap_repl/
builder.rs

1use std::marker::PhantomData;
2
3use clap::Parser;
4use nu_ansi_term::{Color, Style};
5use reedline::{
6    default_emacs_keybindings, DefaultHinter, DefaultPrompt, EditMode, Emacs, IdeMenu,
7    KeyModifiers, MenuBuilder, Prompt, Reedline, ReedlineEvent, ReedlineMenu,
8};
9
10use crate::{ClapEditor, ReedCompleter};
11
12pub struct ClapEditorBuilder<C: Parser + Send + Sync + 'static> {
13    prompt: Box<dyn Prompt>,
14    edit_mode: Box<dyn EditMode>,
15    hook: Box<dyn FnOnce(Reedline) -> Reedline>,
16    c_phantom: PhantomData<C>,
17}
18
19impl<C: Parser + Send + Sync + 'static> ClapEditorBuilder<C> {
20    pub(crate) fn new() -> Self {
21        Self {
22            prompt: Box::<DefaultPrompt>::default(),
23            edit_mode: {
24                let mut keybindings = default_emacs_keybindings();
25                keybindings.add_binding(
26                    KeyModifiers::NONE,
27                    reedline::KeyCode::Tab,
28                    ReedlineEvent::UntilFound(vec![
29                        ReedlineEvent::Menu("completion_menu".to_string()),
30                        ReedlineEvent::MenuNext,
31                    ]),
32                );
33                Box::new(Emacs::new(keybindings))
34            },
35            hook: Box::new(|e| e),
36            c_phantom: PhantomData,
37        }
38    }
39
40    pub fn with_prompt(mut self, prompt: Box<dyn Prompt>) -> Self {
41        self.prompt = prompt;
42        self
43    }
44
45    pub fn with_edit_mode(mut self, edit_mode: Box<dyn EditMode>) -> Self {
46        self.edit_mode = edit_mode;
47        self
48    }
49
50    pub fn with_editor_hook(mut self, hook: impl FnOnce(Reedline) -> Reedline + 'static) -> Self {
51        self.hook = Box::new(hook);
52        self
53    }
54
55    pub fn build(self) -> ClapEditor<C> {
56        let completion_menu = Box::new(
57            IdeMenu::default()
58                .with_default_border()
59                .with_name("completion_menu"),
60        );
61
62        let rl = Reedline::create()
63            .with_completer(Box::new(ReedCompleter::<C> {
64                c_phantom: PhantomData,
65            }))
66            .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
67            .with_hinter(Box::new(
68                DefaultHinter::default().with_style(Style::new().italic().fg(Color::DarkGray)),
69            ))
70            .with_edit_mode(self.edit_mode);
71        let rl = (self.hook)(rl);
72        ClapEditor {
73            rl,
74            prompt: self.prompt,
75            c_phantom: PhantomData,
76        }
77    }
78}