1use crate::history::SearchDirection;
4use crate::Context;
5
6pub trait Hint {
8 fn display(&self) -> &str;
10 fn completion(&self) -> Option<&str>;
12}
13
14impl Hint for String {
15 fn display(&self) -> &str {
16 self.as_str()
17 }
18
19 fn completion(&self) -> Option<&str> {
20 Some(self.as_str())
21 }
22}
23
24pub trait Hinter {
26 type Hint: Hint + 'static;
28
29 fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<Self::Hint> {
34 let _ = (line, pos, ctx);
35 None
36 }
37}
38
39impl Hinter for () {
40 type Hint = String;
41}
42
43impl<'r, H: ?Sized + Hinter> Hinter for &'r H {
44 type Hint = H::Hint;
45
46 fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<Self::Hint> {
47 (**self).hint(line, pos, ctx)
48 }
49}
50
51#[derive(Default)]
54pub struct HistoryHinter {}
55
56impl HistoryHinter {
57 pub fn new() -> HistoryHinter {
59 HistoryHinter::default()
60 }
61}
62
63impl Hinter for HistoryHinter {
64 type Hint = String;
65
66 fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
67 if line.is_empty() || pos < line.len() {
68 return None;
69 }
70 let start = if ctx.history_index() == ctx.history().len() {
71 ctx.history_index().saturating_sub(1)
72 } else {
73 ctx.history_index()
74 };
75 if let Some(sr) = ctx
76 .history
77 .starts_with(line, start, SearchDirection::Reverse)
78 .unwrap_or(None)
79 {
80 if sr.entry == line {
81 return None;
82 }
83 return Some(sr.entry[pos..].to_owned());
84 }
85 None
86 }
87}
88
89#[cfg(test)]
90mod test {
91 use super::{Hinter, HistoryHinter};
92 use crate::history::DefaultHistory;
93 use crate::Context;
94
95 #[test]
96 pub fn empty_history() {
97 let history = DefaultHistory::new();
98 let ctx = Context::new(&history);
99 let hinter = HistoryHinter {};
100 let hint = hinter.hint("test", 4, &ctx);
101 assert_eq!(None, hint);
102 }
103}