dialoguer/theme/
mod.rs

1//! Customizes the rendering of the elements.
2use std::fmt;
3
4#[cfg(feature = "fuzzy-select")]
5use console::style;
6#[cfg(feature = "fuzzy-select")]
7use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
8
9mod colorful;
10pub(crate) mod render;
11mod simple;
12
13pub use colorful::ColorfulTheme;
14pub use simple::SimpleTheme;
15
16/// Implements a theme for dialoguer.
17pub trait Theme {
18    /// Formats a prompt.
19    #[inline]
20    fn format_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
21        write!(f, "{}:", prompt)
22    }
23
24    /// Formats out an error.
25    #[inline]
26    fn format_error(&self, f: &mut dyn fmt::Write, err: &str) -> fmt::Result {
27        write!(f, "error: {}", err)
28    }
29
30    /// Formats a confirm prompt.
31    fn format_confirm_prompt(
32        &self,
33        f: &mut dyn fmt::Write,
34        prompt: &str,
35        default: Option<bool>,
36    ) -> fmt::Result {
37        if !prompt.is_empty() {
38            write!(f, "{} ", &prompt)?;
39        }
40        match default {
41            None => write!(f, "[y/n] ")?,
42            Some(true) => write!(f, "[Y/n] ")?,
43            Some(false) => write!(f, "[y/N] ")?,
44        }
45        Ok(())
46    }
47
48    /// Formats a confirm prompt after selection.
49    fn format_confirm_prompt_selection(
50        &self,
51        f: &mut dyn fmt::Write,
52        prompt: &str,
53        selection: Option<bool>,
54    ) -> fmt::Result {
55        let selection = selection.map(|b| if b { "yes" } else { "no" });
56
57        match selection {
58            Some(selection) if prompt.is_empty() => {
59                write!(f, "{}", selection)
60            }
61            Some(selection) => {
62                write!(f, "{} {}", &prompt, selection)
63            }
64            None if prompt.is_empty() => Ok(()),
65            None => {
66                write!(f, "{}", &prompt)
67            }
68        }
69    }
70
71    /// Formats an input prompt.
72    fn format_input_prompt(
73        &self,
74        f: &mut dyn fmt::Write,
75        prompt: &str,
76        default: Option<&str>,
77    ) -> fmt::Result {
78        match default {
79            Some(default) if prompt.is_empty() => write!(f, "[{}]: ", default),
80            Some(default) => write!(f, "{} [{}]: ", prompt, default),
81            None => write!(f, "{}: ", prompt),
82        }
83    }
84
85    /// Formats an input prompt after selection.
86    #[inline]
87    fn format_input_prompt_selection(
88        &self,
89        f: &mut dyn fmt::Write,
90        prompt: &str,
91        sel: &str,
92    ) -> fmt::Result {
93        write!(f, "{}: {}", prompt, sel)
94    }
95
96    /// Formats a password prompt.
97    #[inline]
98    #[cfg(feature = "password")]
99    fn format_password_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
100        self.format_input_prompt(f, prompt, None)
101    }
102
103    /// Formats a password prompt after selection.
104    #[inline]
105    #[cfg(feature = "password")]
106    fn format_password_prompt_selection(
107        &self,
108        f: &mut dyn fmt::Write,
109        prompt: &str,
110    ) -> fmt::Result {
111        self.format_input_prompt_selection(f, prompt, "[hidden]")
112    }
113
114    /// Formats a select prompt.
115    #[inline]
116    fn format_select_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
117        self.format_prompt(f, prompt)
118    }
119
120    /// Formats a select prompt after selection.
121    #[inline]
122    fn format_select_prompt_selection(
123        &self,
124        f: &mut dyn fmt::Write,
125        prompt: &str,
126        sel: &str,
127    ) -> fmt::Result {
128        self.format_input_prompt_selection(f, prompt, sel)
129    }
130
131    /// Formats a multi select prompt.
132    #[inline]
133    fn format_multi_select_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
134        self.format_prompt(f, prompt)
135    }
136
137    /// Formats a sort prompt.
138    #[inline]
139    fn format_sort_prompt(&self, f: &mut dyn fmt::Write, prompt: &str) -> fmt::Result {
140        self.format_prompt(f, prompt)
141    }
142
143    /// Formats a multi_select prompt after selection.
144    fn format_multi_select_prompt_selection(
145        &self,
146        f: &mut dyn fmt::Write,
147        prompt: &str,
148        selections: &[&str],
149    ) -> fmt::Result {
150        write!(f, "{}: ", prompt)?;
151        for (idx, sel) in selections.iter().enumerate() {
152            write!(f, "{}{}", if idx == 0 { "" } else { ", " }, sel)?;
153        }
154        Ok(())
155    }
156
157    /// Formats a sort prompt after selection.
158    #[inline]
159    fn format_sort_prompt_selection(
160        &self,
161        f: &mut dyn fmt::Write,
162        prompt: &str,
163        selections: &[&str],
164    ) -> fmt::Result {
165        self.format_multi_select_prompt_selection(f, prompt, selections)
166    }
167
168    /// Formats a select prompt item.
169    fn format_select_prompt_item(
170        &self,
171        f: &mut dyn fmt::Write,
172        text: &str,
173        active: bool,
174    ) -> fmt::Result {
175        write!(f, "{} {}", if active { ">" } else { " " }, text)
176    }
177
178    /// Formats a multi select prompt item.
179    fn format_multi_select_prompt_item(
180        &self,
181        f: &mut dyn fmt::Write,
182        text: &str,
183        checked: bool,
184        active: bool,
185    ) -> fmt::Result {
186        write!(
187            f,
188            "{} {}",
189            match (checked, active) {
190                (true, true) => "> [x]",
191                (true, false) => "  [x]",
192                (false, true) => "> [ ]",
193                (false, false) => "  [ ]",
194            },
195            text
196        )
197    }
198
199    /// Formats a sort prompt item.
200    fn format_sort_prompt_item(
201        &self,
202        f: &mut dyn fmt::Write,
203        text: &str,
204        picked: bool,
205        active: bool,
206    ) -> fmt::Result {
207        write!(
208            f,
209            "{} {}",
210            match (picked, active) {
211                (true, true) => "> [x]",
212                (false, true) => "> [ ]",
213                (_, false) => "  [ ]",
214            },
215            text
216        )
217    }
218
219    /// Formats a fuzzy select prompt item.
220    #[cfg(feature = "fuzzy-select")]
221    fn format_fuzzy_select_prompt_item(
222        &self,
223        f: &mut dyn fmt::Write,
224        text: &str,
225        active: bool,
226        highlight_matches: bool,
227        matcher: &SkimMatcherV2,
228        search_term: &str,
229    ) -> fmt::Result {
230        write!(f, "{} ", if active { ">" } else { " " })?;
231
232        if highlight_matches {
233            if let Some((_score, indices)) = matcher.fuzzy_indices(text, search_term) {
234                for (idx, c) in text.chars().enumerate() {
235                    if indices.contains(&idx) {
236                        write!(f, "{}", style(c).for_stderr().bold())?;
237                    } else {
238                        write!(f, "{}", c)?;
239                    }
240                }
241
242                return Ok(());
243            }
244        }
245
246        write!(f, "{}", text)
247    }
248
249    /// Formats a fuzzy select prompt.
250    #[cfg(feature = "fuzzy-select")]
251    fn format_fuzzy_select_prompt(
252        &self,
253        f: &mut dyn fmt::Write,
254        prompt: &str,
255        search_term: &str,
256        bytes_pos: usize,
257    ) -> fmt::Result {
258        if !prompt.is_empty() {
259            write!(f, "{prompt} ")?;
260        }
261
262        let (st_head, st_tail) = search_term.split_at(bytes_pos);
263        write!(f, "{st_head}|{st_tail}")
264    }
265}