clap_exec_repl/
builder.rs1use std::marker::PhantomData;
2
3use clap::Parser;
4use clap_exec::{Runner, RunnerAsync};
5use nu_ansi_term::{Color, Style};
6use reedline::{
7 DefaultHinter,
8 DefaultPrompt,
9 EditMode,
10 Emacs,
11 IdeMenu,
12 KeyModifiers,
13 MenuBuilder,
14 Prompt,
15 Reedline,
16 ReedlineEvent,
17 ReedlineMenu,
18 default_emacs_keybindings,
19};
20
21use crate::{ClapAsyncEditor, ClapEditor, ReedCompleter};
22
23macro_rules! default_reedline {
24 ($c:ty, $edit_mode:expr) => {{
25 let completion_menu = Box::new(IdeMenu::default().with_default_border().with_name("completion_menu"));
26
27 Reedline::create()
28 .with_completer(Box::new(ReedCompleter::<$c> { c_phantom: PhantomData }))
29 .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
30 .with_hinter(Box::new(
31 DefaultHinter::default().with_style(Style::new().italic().fg(Color::DarkGray)),
32 ))
33 .with_edit_mode($edit_mode)
34 }};
35}
36
37macro_rules! new_builder {
38 ($s:ident) => {
39 $s {
40 prompt: Box::<DefaultPrompt>::default(),
41 edit_mode: {
42 let mut keybindings = default_emacs_keybindings();
43 keybindings.add_binding(
44 KeyModifiers::NONE,
45 reedline::KeyCode::Tab,
46 ReedlineEvent::UntilFound(vec![
47 ReedlineEvent::Menu("completion_menu".to_string()),
48 ReedlineEvent::MenuNext,
49 ]),
50 );
51 Box::new(Emacs::new(keybindings))
52 },
53 hook: Box::new(|e| e),
54 c_phantom: PhantomData,
55 }
56 };
57}
58
59pub struct ClapEditorBuilder<C: Parser + Runner + Send + Sync + 'static> {
60 prompt: Box<dyn Prompt>,
61 edit_mode: Box<dyn EditMode>,
62 hook: Box<dyn FnOnce(Reedline) -> Reedline>,
63 c_phantom: PhantomData<C>,
64}
65
66impl<C: Parser + Runner + Send + Sync + 'static> ClapEditorBuilder<C> {
67 pub(crate) fn new() -> Self {
68 new_builder!(Self)
69 }
70
71 pub fn with_prompt(mut self, prompt: Box<dyn Prompt>) -> Self {
72 self.prompt = prompt;
73 self
74 }
75
76 pub fn with_edit_mode(mut self, edit_mode: Box<dyn EditMode>) -> Self {
77 self.edit_mode = edit_mode;
78 self
79 }
80
81 pub fn with_editor_hook(mut self, hook: impl FnOnce(Reedline) -> Reedline + 'static) -> Self {
82 self.hook = Box::new(hook);
83 self
84 }
85
86 pub fn build(self) -> ClapEditor<C> {
87 let rl = (self.hook)(default_reedline!(C, self.edit_mode));
88 ClapEditor {
89 rl,
90 prompt: self.prompt,
91 c_phantom: PhantomData,
92 }
93 }
94}
95
96pub struct ClapEditorAsyncBuilder<C: Parser + RunnerAsync + Send + Sync + 'static> {
97 prompt: Box<dyn Prompt>,
98 edit_mode: Box<dyn EditMode>,
99 hook: Box<dyn FnOnce(Reedline) -> Reedline>,
100 c_phantom: PhantomData<C>,
101}
102
103impl<C: Parser + RunnerAsync + Send + Sync + 'static> ClapEditorAsyncBuilder<C> {
104 pub(crate) fn new() -> Self {
105 new_builder!(Self)
106 }
107
108 pub fn with_prompt(mut self, prompt: Box<dyn Prompt>) -> Self {
109 self.prompt = prompt;
110 self
111 }
112
113 pub fn with_edit_mode(mut self, edit_mode: Box<dyn EditMode>) -> Self {
114 self.edit_mode = edit_mode;
115 self
116 }
117
118 pub fn with_editor_hook(mut self, hook: impl FnOnce(Reedline) -> Reedline + 'static) -> Self {
119 self.hook = Box::new(hook);
120 self
121 }
122
123 pub fn build(self) -> ClapAsyncEditor<C> {
124 let rl = (self.hook)(default_reedline!(C, self.edit_mode));
125
126 ClapAsyncEditor {
127 rl,
128 prompt: self.prompt,
129 c_phantom: PhantomData,
130 }
131 }
132}