rustyline/
config.rs

1//! Customize line editor
2use crate::{layout::GraphemeClusterMode, Result};
3use std::default::Default;
4
5/// User preferences
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct Config {
8    /// Maximum number of entries in History.
9    max_history_size: usize, // history_max_entries
10    history_duplicates: HistoryDuplicates,
11    history_ignore_space: bool,
12    completion_type: CompletionType,
13    /// Directly show all alternatives or not when [`CompletionType::List`] is
14    /// being used
15    completion_show_all_if_ambiguous: bool,
16    /// When listing completion alternatives, only display
17    /// one screen of possibilities at a time.
18    completion_prompt_limit: usize,
19    /// Duration (milliseconds) Rustyline will wait for a character when
20    /// reading an ambiguous key sequence.
21    keyseq_timeout: Option<u16>,
22    /// Emacs or Vi mode
23    edit_mode: EditMode,
24    /// If true, each nonblank line returned by `readline` will be
25    /// automatically added to the history.
26    auto_add_history: bool,
27    /// Beep or Flash or nothing
28    bell_style: BellStyle,
29    /// if colors should be enabled.
30    color_mode: ColorMode,
31    /// if terminal supports grapheme clustering
32    grapheme_cluster_mode: GraphemeClusterMode,
33    /// Whether to use stdio or not
34    behavior: Behavior,
35    /// Horizontal space taken by a tab.
36    tab_stop: u8,
37    /// Indentation size for indent/dedent commands
38    indent_size: u8,
39    /// Check if cursor position is at leftmost before displaying prompt
40    check_cursor_position: bool,
41    /// Bracketed paste on unix platform
42    enable_bracketed_paste: bool,
43    /// Synchronized output on unix platform
44    enable_synchronized_output: bool,
45    /// Whether to disable or not the signals in termios
46    enable_signals: bool,
47}
48
49impl Config {
50    /// Returns a `Config` builder.
51    #[must_use]
52    pub fn builder() -> Builder {
53        Builder::new()
54    }
55
56    /// Tell the maximum length (i.e. number of entries) for the history.
57    #[must_use]
58    pub fn max_history_size(&self) -> usize {
59        self.max_history_size
60    }
61
62    pub(crate) fn set_max_history_size(&mut self, max_size: usize) {
63        self.max_history_size = max_size;
64    }
65
66    /// Tell if lines which match the previous history entry are saved or not
67    /// in the history list.
68    ///
69    /// By default, they are ignored.
70    #[must_use]
71    pub fn history_duplicates(&self) -> HistoryDuplicates {
72        self.history_duplicates
73    }
74
75    pub(crate) fn set_history_ignore_dups(&mut self, yes: bool) {
76        self.history_duplicates = if yes {
77            HistoryDuplicates::IgnoreConsecutive
78        } else {
79            HistoryDuplicates::AlwaysAdd
80        };
81    }
82
83    /// Tell if lines which begin with a space character are saved or not in
84    /// the history list.
85    ///
86    /// By default, they are saved.
87    #[must_use]
88    pub fn history_ignore_space(&self) -> bool {
89        self.history_ignore_space
90    }
91
92    pub(crate) fn set_history_ignore_space(&mut self, yes: bool) {
93        self.history_ignore_space = yes;
94    }
95
96    /// Completion behaviour.
97    ///
98    /// By default, [`CompletionType::Circular`].
99    #[must_use]
100    pub fn completion_type(&self) -> CompletionType {
101        self.completion_type
102    }
103
104    /// When listing completion alternatives, only display
105    /// one screen of possibilities at a time (used for [`CompletionType::List`]
106    /// mode).
107    #[must_use]
108    pub fn completion_prompt_limit(&self) -> usize {
109        self.completion_prompt_limit
110    }
111
112    /// Directly show all alternatives when using list completion
113    ///
114    /// By default, they are not, a second tab is needed
115    #[must_use]
116    pub fn completion_show_all_if_ambiguous(&self) -> bool {
117        self.completion_show_all_if_ambiguous
118    }
119
120    /// Duration (milliseconds) Rustyline will wait for a character when
121    /// reading an ambiguous key sequence (used for [`EditMode::Vi`] mode on
122    /// unix platform).
123    ///
124    /// By default, no timeout (-1) or 500ms if [`EditMode::Vi`] is activated.
125    #[must_use]
126    pub fn keyseq_timeout(&self) -> Option<u16> {
127        self.keyseq_timeout
128    }
129
130    /// Emacs or Vi mode
131    #[must_use]
132    pub fn edit_mode(&self) -> EditMode {
133        self.edit_mode
134    }
135
136    /// Tell if lines are automatically added to the history.
137    ///
138    /// By default, they are not.
139    #[must_use]
140    pub fn auto_add_history(&self) -> bool {
141        self.auto_add_history
142    }
143
144    /// Bell style: beep, flash or nothing.
145    #[must_use]
146    pub fn bell_style(&self) -> BellStyle {
147        self.bell_style
148    }
149
150    /// Tell if colors should be enabled.
151    ///
152    /// By default, they are except if stdout is not a TTY.
153    #[must_use]
154    pub fn color_mode(&self) -> ColorMode {
155        self.color_mode
156    }
157
158    /// Tell if terminal supports grapheme clustering
159    #[must_use]
160    pub fn grapheme_cluster_mode(&self) -> GraphemeClusterMode {
161        self.grapheme_cluster_mode
162    }
163
164    pub(crate) fn set_color_mode(&mut self, color_mode: ColorMode) {
165        self.color_mode = color_mode;
166    }
167
168    /// Whether to use stdio or not
169    ///
170    /// By default, stdio is used.
171    #[must_use]
172    pub fn behavior(&self) -> Behavior {
173        self.behavior
174    }
175
176    pub(crate) fn set_behavior(&mut self, behavior: Behavior) {
177        self.behavior = behavior;
178    }
179
180    /// Horizontal space taken by a tab.
181    ///
182    /// By default, 8.
183    #[must_use]
184    pub fn tab_stop(&self) -> u8 {
185        self.tab_stop
186    }
187
188    pub(crate) fn set_tab_stop(&mut self, tab_stop: u8) {
189        self.tab_stop = tab_stop;
190    }
191
192    /// Check if cursor position is at leftmost before displaying prompt.
193    ///
194    /// By default, we don't check.
195    #[must_use]
196    pub fn check_cursor_position(&self) -> bool {
197        self.check_cursor_position
198    }
199
200    /// Indentation size used by indentation commands
201    ///
202    /// By default, 2.
203    #[must_use]
204    pub fn indent_size(&self) -> u8 {
205        self.indent_size
206    }
207
208    pub(crate) fn set_indent_size(&mut self, indent_size: u8) {
209        self.indent_size = indent_size;
210    }
211
212    /// Bracketed paste on unix platform
213    ///
214    /// By default, it's enabled.
215    #[must_use]
216    pub fn enable_bracketed_paste(&self) -> bool {
217        self.enable_bracketed_paste
218    }
219
220    /// Synchronized output on unix platform
221    ///
222    /// By default, it's enabled.
223    #[must_use]
224    pub fn enable_synchronized_output(&self) -> bool {
225        self.enable_synchronized_output
226    }
227
228    /// Enable or disable signals in termios
229    ///
230    /// By default, it's disabled.
231    #[must_use]
232    pub fn enable_signals(&self) -> bool {
233        self.enable_signals
234    }
235
236    pub(crate) fn set_enable_signals(&mut self, enable_signals: bool) {
237        self.enable_signals = enable_signals;
238    }
239}
240
241impl Default for Config {
242    fn default() -> Self {
243        Self {
244            max_history_size: 100,
245            history_duplicates: HistoryDuplicates::IgnoreConsecutive,
246            history_ignore_space: false,
247            completion_type: CompletionType::Circular, // TODO Validate
248            completion_prompt_limit: 100,
249            completion_show_all_if_ambiguous: false,
250            keyseq_timeout: None,
251            edit_mode: EditMode::Emacs,
252            auto_add_history: false,
253            bell_style: BellStyle::default(),
254            color_mode: ColorMode::Enabled,
255            grapheme_cluster_mode: GraphemeClusterMode::from_env(),
256            behavior: Behavior::default(),
257            tab_stop: 8,
258            indent_size: 2,
259            check_cursor_position: false,
260            enable_bracketed_paste: true,
261            enable_synchronized_output: true,
262            enable_signals: false,
263        }
264    }
265}
266
267/// Beep or flash or nothing
268#[derive(Clone, Copy, Debug, PartialEq, Eq)]
269pub enum BellStyle {
270    /// Beep
271    Audible,
272    /// Silent
273    None,
274    /// Flash screen (not supported)
275    Visible,
276}
277
278/// `Audible` by default on unix (overridden by current Terminal settings).
279/// `None` on windows.
280impl Default for BellStyle {
281    #[cfg(any(windows, target_arch = "wasm32"))]
282    fn default() -> Self {
283        Self::None
284    }
285
286    #[cfg(unix)]
287    fn default() -> Self {
288        Self::Audible
289    }
290}
291
292/// History filter
293#[derive(Clone, Copy, Debug, PartialEq, Eq)]
294pub enum HistoryDuplicates {
295    /// No filter
296    AlwaysAdd,
297    /// a line will not be added to the history if it matches the previous entry
298    IgnoreConsecutive,
299}
300
301/// Tab completion style
302#[derive(Clone, Copy, Debug, PartialEq, Eq)]
303#[non_exhaustive]
304pub enum CompletionType {
305    /// Complete the next full match (like in Vim by default)
306    Circular,
307    /// Complete till longest match.
308    /// When more than one match, list all matches
309    /// (like in Bash/Readline).
310    List,
311
312    /// Complete the match using fuzzy search and selection
313    /// (like fzf and plugins)
314    /// Currently only available for unix platforms as dependency on
315    /// skim->tuikit Compile with `--features=fuzzy` to enable
316    #[cfg(all(unix, feature = "with-fuzzy"))]
317    Fuzzy,
318}
319
320/// Style of editing / Standard keymaps
321#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322#[non_exhaustive]
323pub enum EditMode {
324    /// Emacs keymap
325    Emacs,
326    /// Vi keymap
327    Vi,
328}
329
330/// Colorization mode
331#[derive(Clone, Copy, Debug, PartialEq, Eq)]
332#[non_exhaustive]
333pub enum ColorMode {
334    /// Activate highlighting if platform/terminal is supported.
335    Enabled,
336    /// Activate highlighting even if platform is not supported (windows < 10).
337    Forced,
338    /// Deactivate highlighting even if platform/terminal is supported.
339    Disabled,
340}
341
342/// Should the editor use stdio
343#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
344#[non_exhaustive]
345pub enum Behavior {
346    /// Use stdin / stdout
347    #[default]
348    Stdio,
349    /// Use terminal-style interaction whenever possible, even if 'stdin' and/or
350    /// 'stdout' are not terminals.
351    PreferTerm,
352    // TODO
353    // Use file-style interaction, reading input from the given file.
354    // useFile
355}
356
357/// Configuration builder
358#[derive(Clone, Debug, Default)]
359pub struct Builder {
360    p: Config,
361}
362
363impl Builder {
364    /// Returns a [`Config`] builder.
365    #[must_use]
366    pub fn new() -> Self {
367        Self {
368            p: Config::default(),
369        }
370    }
371
372    /// Set the maximum length for the history.
373    pub fn max_history_size(mut self, max_size: usize) -> Result<Self> {
374        self.set_max_history_size(max_size)?;
375        Ok(self)
376    }
377
378    /// Tell if lines which match the previous history entry are saved or not
379    /// in the history list.
380    ///
381    /// By default, they are ignored.
382    pub fn history_ignore_dups(mut self, yes: bool) -> Result<Self> {
383        self.set_history_ignore_dups(yes)?;
384        Ok(self)
385    }
386
387    /// Tell if lines which begin with a space character are saved or not in
388    /// the history list.
389    ///
390    /// By default, they are saved.
391    #[must_use]
392    pub fn history_ignore_space(mut self, yes: bool) -> Self {
393        self.set_history_ignore_space(yes);
394        self
395    }
396
397    /// Set `completion_type`.
398    #[must_use]
399    pub fn completion_type(mut self, completion_type: CompletionType) -> Self {
400        self.set_completion_type(completion_type);
401        self
402    }
403
404    /// The number of possible completions that determines when the user is
405    /// asked whether the list of possibilities should be displayed.
406    #[must_use]
407    pub fn completion_prompt_limit(mut self, completion_prompt_limit: usize) -> Self {
408        self.set_completion_prompt_limit(completion_prompt_limit);
409        self
410    }
411
412    /// Choose whether or not to show all alternatives immediately when using
413    /// list completion
414    ///
415    /// By default, a second tab is needed.
416    #[must_use]
417    pub fn completion_show_all_if_ambiguous(
418        mut self,
419        completion_show_all_if_ambiguous: bool,
420    ) -> Self {
421        self.set_completion_show_all_if_ambiguous(completion_show_all_if_ambiguous);
422        self
423    }
424
425    /// Timeout for ambiguous key sequences in milliseconds.
426    /// Currently, it is used only to distinguish a single ESC from an ESC
427    /// sequence.
428    /// After seeing an ESC key, wait at most `keyseq_timeout_ms` for another
429    /// byte.
430    #[must_use]
431    pub fn keyseq_timeout(mut self, keyseq_timeout_ms: Option<u16>) -> Self {
432        self.set_keyseq_timeout(keyseq_timeout_ms);
433        self
434    }
435
436    /// Choose between Emacs or Vi mode.
437    #[must_use]
438    pub fn edit_mode(mut self, edit_mode: EditMode) -> Self {
439        self.set_edit_mode(edit_mode);
440        self
441    }
442
443    /// Tell if lines are automatically added to the history.
444    ///
445    /// By default, they are not.
446    #[must_use]
447    pub fn auto_add_history(mut self, yes: bool) -> Self {
448        self.set_auto_add_history(yes);
449        self
450    }
451
452    /// Set bell style: beep, flash or nothing.
453    #[must_use]
454    pub fn bell_style(mut self, bell_style: BellStyle) -> Self {
455        self.set_bell_style(bell_style);
456        self
457    }
458
459    /// Forces colorization on or off.
460    ///
461    /// By default, colorization is on except if stdout is not a TTY.
462    #[must_use]
463    pub fn color_mode(mut self, color_mode: ColorMode) -> Self {
464        self.set_color_mode(color_mode);
465        self
466    }
467
468    /// Tell if terminal supports grapheme clustering
469    #[must_use]
470    pub fn grapheme_cluster_mode(mut self, grapheme_cluster_mode: GraphemeClusterMode) -> Self {
471        self.set_grapheme_cluster_mode(grapheme_cluster_mode);
472        self
473    }
474
475    /// Whether to use stdio or not
476    ///
477    /// By default, stdio is used.
478    #[must_use]
479    pub fn behavior(mut self, behavior: Behavior) -> Self {
480        self.set_behavior(behavior);
481        self
482    }
483
484    /// Horizontal space taken by a tab.
485    ///
486    /// By default, `8`
487    #[must_use]
488    pub fn tab_stop(mut self, tab_stop: u8) -> Self {
489        self.set_tab_stop(tab_stop);
490        self
491    }
492
493    /// Check if cursor position is at leftmost before displaying prompt.
494    ///
495    /// By default, we don't check.
496    #[must_use]
497    pub fn check_cursor_position(mut self, yes: bool) -> Self {
498        self.set_check_cursor_position(yes);
499        self
500    }
501
502    /// Indentation size
503    ///
504    /// By default, `2`
505    #[must_use]
506    pub fn indent_size(mut self, indent_size: u8) -> Self {
507        self.set_indent_size(indent_size);
508        self
509    }
510
511    /// Enable or disable bracketed paste on unix platform
512    ///
513    /// By default, it's enabled.
514    #[must_use]
515    pub fn bracketed_paste(mut self, enabled: bool) -> Self {
516        self.enable_bracketed_paste(enabled);
517        self
518    }
519
520    /// Enable or disable signals in termios
521    ///
522    /// By default, it's disabled.
523    #[must_use]
524    pub fn enable_signals(mut self, enable_signals: bool) -> Self {
525        self.p.set_enable_signals(enable_signals);
526        self
527    }
528
529    /// Builds a [`Config`] with the settings specified so far.
530    #[must_use]
531    pub fn build(self) -> Config {
532        self.p
533    }
534}
535
536impl Configurer for Builder {
537    fn config_mut(&mut self) -> &mut Config {
538        &mut self.p
539    }
540}
541
542/// Trait for component that holds a [`Config`].
543pub trait Configurer {
544    /// `Config` accessor.
545    fn config_mut(&mut self) -> &mut Config;
546
547    /// Set the maximum length for the history.
548    fn set_max_history_size(&mut self, max_size: usize) -> Result<()> {
549        self.config_mut().set_max_history_size(max_size);
550        Ok(())
551    }
552
553    /// Tell if lines which match the previous history entry are saved or not
554    /// in the history list.
555    ///
556    /// By default, they are ignored.
557    fn set_history_ignore_dups(&mut self, yes: bool) -> Result<()> {
558        self.config_mut().set_history_ignore_dups(yes);
559        Ok(())
560    }
561
562    /// Tell if lines which begin with a space character are saved or not in
563    /// the history list.
564    ///
565    /// By default, they are saved.
566    fn set_history_ignore_space(&mut self, yes: bool) {
567        self.config_mut().set_history_ignore_space(yes);
568    }
569    /// Set `completion_type`.
570    fn set_completion_type(&mut self, completion_type: CompletionType) {
571        self.config_mut().completion_type = completion_type;
572    }
573
574    /// Choose whether or not to show all alternatives immediately when using
575    /// list completion
576    ///
577    /// By default, a second tab is needed.
578    fn set_completion_show_all_if_ambiguous(&mut self, completion_show_all_if_ambiguous: bool) {
579        self.config_mut().completion_show_all_if_ambiguous = completion_show_all_if_ambiguous;
580    }
581
582    /// The number of possible completions that determines when the user is
583    /// asked whether the list of possibilities should be displayed.
584    fn set_completion_prompt_limit(&mut self, completion_prompt_limit: usize) {
585        self.config_mut().completion_prompt_limit = completion_prompt_limit;
586    }
587
588    /// Timeout for ambiguous key sequences in milliseconds.
589    fn set_keyseq_timeout(&mut self, keyseq_timeout_ms: Option<u16>) {
590        self.config_mut().keyseq_timeout = keyseq_timeout_ms;
591    }
592
593    /// Choose between Emacs or Vi mode.
594    fn set_edit_mode(&mut self, edit_mode: EditMode) {
595        self.config_mut().edit_mode = edit_mode;
596        match edit_mode {
597            EditMode::Emacs => self.set_keyseq_timeout(None), // no timeout
598            EditMode::Vi => self.set_keyseq_timeout(Some(500)),
599        }
600    }
601
602    /// Tell if lines are automatically added to the history.
603    ///
604    /// By default, they are not.
605    fn set_auto_add_history(&mut self, yes: bool) {
606        self.config_mut().auto_add_history = yes;
607    }
608
609    /// Set bell style: beep, flash or nothing.
610    fn set_bell_style(&mut self, bell_style: BellStyle) {
611        self.config_mut().bell_style = bell_style;
612    }
613
614    /// Forces colorization on or off.
615    ///
616    /// By default, colorization is on except if stdout is not a TTY.
617    fn set_color_mode(&mut self, color_mode: ColorMode) {
618        self.config_mut().set_color_mode(color_mode);
619    }
620
621    /// Tell if terminal supports grapheme clustering
622    fn set_grapheme_cluster_mode(&mut self, grapheme_cluster_mode: GraphemeClusterMode) {
623        self.config_mut().grapheme_cluster_mode = grapheme_cluster_mode;
624    }
625
626    /// Whether to use stdio or not
627    ///
628    /// By default, stdio is used.
629    fn set_behavior(&mut self, behavior: Behavior) {
630        self.config_mut().set_behavior(behavior);
631    }
632
633    /// Horizontal space taken by a tab.
634    ///
635    /// By default, `8`
636    fn set_tab_stop(&mut self, tab_stop: u8) {
637        self.config_mut().set_tab_stop(tab_stop);
638    }
639
640    /// Check if cursor position is at leftmost before displaying prompt.
641    ///
642    /// By default, we don't check.
643    fn set_check_cursor_position(&mut self, yes: bool) {
644        self.config_mut().check_cursor_position = yes;
645    }
646    /// Indentation size for indent/dedent commands
647    ///
648    /// By default, `2`
649    fn set_indent_size(&mut self, size: u8) {
650        self.config_mut().set_indent_size(size);
651    }
652
653    /// Enable or disable bracketed paste on unix platform
654    ///
655    /// By default, it's enabled.
656    fn enable_bracketed_paste(&mut self, enabled: bool) {
657        self.config_mut().enable_bracketed_paste = enabled;
658    }
659
660    /// Enable or disable synchronized output on unix platform
661    ///
662    /// By default, it's enabled.
663    fn enable_synchronized_output(&mut self, enabled: bool) {
664        self.config_mut().enable_synchronized_output = enabled;
665    }
666
667    /// Enable or disable signals in termios
668    ///
669    /// By default, it's disabled.
670    fn set_enable_signals(&mut self, enable_signals: bool) {
671        self.config_mut().set_enable_signals(enable_signals);
672    }
673}