Skip to main content

clap_exec_repl/
builder.rs

1use 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}