reedline/hinter/
cwd_aware.rs

1use crate::{
2    hinter::get_first_token,
3    history::SearchQuery,
4    result::{ReedlineError, ReedlineErrorVariants::HistoryFeatureUnsupported},
5    Hinter, History,
6};
7use nu_ansi_term::{Color, Style};
8
9/// A hinter that uses the completions or the history to show a hint to the user
10///
11/// Similar to `fish` autosuggestions
12pub struct CwdAwareHinter {
13    style: Style,
14    current_hint: String,
15    min_chars: usize,
16}
17
18impl Hinter for CwdAwareHinter {
19    fn handle(
20        &mut self,
21        line: &str,
22        #[allow(unused_variables)] pos: usize,
23        history: &dyn History,
24        use_ansi_coloring: bool,
25    ) -> String {
26        self.current_hint = if line.chars().count() >= self.min_chars {
27            let with_cwd = history
28                .search(SearchQuery::last_with_prefix_and_cwd(
29                    line.to_string(),
30                    history.session(),
31                ))
32                .or_else(|err| {
33                    if let ReedlineError(HistoryFeatureUnsupported { .. }) = err {
34                        history.search(SearchQuery::last_with_prefix(
35                            line.to_string(),
36                            history.session(),
37                        ))
38                    } else {
39                        Err(err)
40                    }
41                })
42                .unwrap_or_default();
43            if !with_cwd.is_empty() {
44                with_cwd[0]
45                    .command_line
46                    .get(line.len()..)
47                    .unwrap_or_default()
48                    .to_string()
49            } else {
50                history
51                    .search(SearchQuery::last_with_prefix(
52                        line.to_string(),
53                        history.session(),
54                    ))
55                    .unwrap_or_default()
56                    .first()
57                    .map_or_else(String::new, |entry| {
58                        entry
59                            .command_line
60                            .get(line.len()..)
61                            .unwrap_or_default()
62                            .to_string()
63                    })
64            }
65        } else {
66            String::new()
67        };
68
69        if use_ansi_coloring && !self.current_hint.is_empty() {
70            self.style.paint(&self.current_hint).to_string()
71        } else {
72            self.current_hint.clone()
73        }
74    }
75
76    fn complete_hint(&self) -> String {
77        self.current_hint.clone()
78    }
79
80    fn next_hint_token(&self) -> String {
81        get_first_token(&self.current_hint)
82    }
83}
84
85impl Default for CwdAwareHinter {
86    fn default() -> Self {
87        CwdAwareHinter {
88            style: Style::new().fg(Color::LightGray),
89            current_hint: String::new(),
90            min_chars: 1,
91        }
92    }
93}
94
95impl CwdAwareHinter {
96    /// A builder that sets the style applied to the hint as part of the buffer
97    #[must_use]
98    pub fn with_style(mut self, style: Style) -> Self {
99        self.style = style;
100        self
101    }
102
103    /// A builder that sets the number of characters that have to be present to enable history hints
104    #[must_use]
105    pub fn with_min_chars(mut self, min_chars: usize) -> Self {
106        self.min_chars = min_chars;
107        self
108    }
109}