inquire/
autocompletion.rs

1//! Trait and structs used by prompts to provide autocompletion features.
2//!
3//! Autocompleters receive the user input to a given prompt and may return
4//! a list of suggestions, selectable by the user as an option to be completed.
5//!
6//! When the user presses the autocompletion hotkey (`tab` by default) the
7//! autocompleter receives the current text input and the currently highlighted
8//! selection, if any. Then the developer may return a [Replacement] action
9//! where the current user text input is replaced or not by a provided string.
10//!
11//! Check the example files to see some usages, recommended are `expense_tracker.rs`
12//! and `complex_autocompletion.rs`.
13
14use dyn_clone::DynClone;
15
16use crate::CustomUserError;
17
18/// Used when an autocompletion is triggered for the user's text input.
19///
20/// `None` means that no completion will be made.
21/// `Some(String)` will replace the current text input with the `String` in `Some`.
22pub type Replacement = Option<String>;
23
24/// Mechanism to implement autocompletion features for text inputs. The `Autocomplete` trait has two provided methods: `get_suggestions` and `get_completion`.
25///
26/// - `get_suggestions` is called whenever the user's text input is modified, e.g. a new letter is typed, returning a `Vec<String>`. The `Vec<String>` is the list of suggestions that the prompt displays to the user according to their text input. The user can then navigate through the list and if they submit while highlighting one of these suggestions, the suggestion is treated as the final answer.
27/// - `get_completion` is called whenever the user presses the autocompletion hotkey (`tab` by default), with the current text input and the text of the currently highlighted suggestion, if any, as parameters. This method should return whether any text replacement (an autocompletion) should be made. If the prompt receives a replacement to be made, it substitutes the current text input for the string received from the `get_completion` call.
28///
29/// For example, in the `complex_autocompletion.rs` example file, the `FilePathCompleter` scans the file system based on the current text input, storing a list of paths that match the current text input.
30///
31/// Everytime `get_suggestions` is called, the method returns the list of paths that match the user input. When the user presses the autocompletion hotkey, the `FilePathCompleter` checks whether there is any path selected from the list, if there is, it decides to replace the current text input for it. The interesting piece of functionality is that if there isn't a path selected from the list, the `FilePathCompleter` calculates the longest common prefix amongst all scanned paths and updates the text input to an unambiguous new value. Similar to how terminals work when traversing paths.
32pub trait Autocomplete: DynClone {
33    /// List of input suggestions to be displayed to the user upon typing the
34    /// text input.
35    ///
36    /// If the user presses the autocompletion hotkey (`tab` as default) with
37    /// a suggestion highlighted, the user's text input will be replaced by the
38    /// content of the suggestion string.
39    fn get_suggestions(&mut self, input: &str) -> Result<Vec<String>, CustomUserError>;
40
41    /// Standalone autocompletion that can be implemented based solely on the user's
42    /// input.
43    ///
44    /// If the user presses the autocompletion hotkey (`tab` as default) and
45    /// there are no suggestions highlighted (1), this function will be called in an
46    /// attempt to autocomplete the user's input.
47    ///
48    /// If the returned value is of the `Some` variant, the text input will be replaced
49    /// by the content of the string.
50    ///
51    /// (1) This applies where either there are no suggestions at all, or there are
52    /// some displayed but the user hasn't highlighted any.
53    fn get_completion(
54        &mut self,
55        input: &str,
56        highlighted_suggestion: Option<String>,
57    ) -> Result<Replacement, CustomUserError>;
58}
59
60impl Clone for Box<dyn Autocomplete> {
61    fn clone(&self) -> Self {
62        dyn_clone::clone_box(&**self)
63    }
64}
65
66/// Empty struct and implementation of Autocomplete trait. Used for the default
67/// autocompleter of `Text` prompts.
68#[derive(Clone, Default)]
69pub struct NoAutoCompletion;
70
71impl Autocomplete for NoAutoCompletion {
72    fn get_suggestions(&mut self, _: &str) -> Result<Vec<String>, CustomUserError> {
73        Ok(vec![])
74    }
75
76    fn get_completion(
77        &mut self,
78        _: &str,
79        _: Option<String>,
80    ) -> Result<Replacement, CustomUserError> {
81        Ok(Replacement::None)
82    }
83}
84
85impl<F> Autocomplete for F
86where
87    F: Fn(&str) -> Result<Vec<String>, CustomUserError> + Clone,
88{
89    fn get_suggestions(&mut self, input: &str) -> Result<Vec<String>, CustomUserError> {
90        (self)(input)
91    }
92
93    fn get_completion(
94        &mut self,
95        _: &str,
96        suggestion: Option<String>,
97    ) -> Result<Replacement, CustomUserError> {
98        Ok(suggestion)
99    }
100}