1 extern crate liner;
2 extern crate regex;
3 extern crate termion;
4 
5 use std::env::{args, current_dir};
6 use std::io;
7 use std::mem::replace;
8 
9 use liner::{Completer, Context, CursorPosition, Event, EventKind, FilenameCompleter};
10 use regex::Regex;
11 use termion::color;
12 
highlight_dodo(s: &str) -> String13 fn highlight_dodo(s: &str) -> String {
14     let reg_exp = Regex::new("(?P<k>dodo)").unwrap();
15     let format = format!("{}$k{}", color::Fg(color::Red), color::Fg(color::Reset));
16     reg_exp.replace_all(s, format.as_str()).to_string()
17 }
18 
19 struct NoCommentCompleter {
20     inner: Option<FilenameCompleter>,
21 }
22 
23 impl Completer for NoCommentCompleter {
completions(&mut self, start: &str) -> Vec<String>24     fn completions(&mut self, start: &str) -> Vec<String> {
25         if let Some(inner) = &mut self.inner {
26             inner.completions(start)
27         } else {
28             Vec::new()
29         }
30     }
31 
on_event<W: std::io::Write>(&mut self, event: Event<W>)32     fn on_event<W: std::io::Write>(&mut self, event: Event<W>) {
33         if let EventKind::BeforeComplete = event.kind {
34             let (_, pos) = event.editor.get_words_and_cursor_position();
35 
36             // Figure out of we are completing a command (the first word) or a filename.
37             let filename = match pos {
38                 CursorPosition::InWord(i) => i > 0,
39                 CursorPosition::InSpace(Some(_), _) => true,
40                 CursorPosition::InSpace(None, _) => false,
41                 CursorPosition::OnWordLeftEdge(i) => i >= 1,
42                 CursorPosition::OnWordRightEdge(i) => i >= 1,
43             };
44 
45             if filename {
46                 let completer = FilenameCompleter::new(Some(current_dir().unwrap()));
47                 replace(&mut self.inner, Some(completer));
48             } else {
49                 replace(&mut self.inner, None);
50             }
51         }
52     }
53 }
54 
main()55 fn main() {
56     let mut con = Context::new();
57     let mut completer = NoCommentCompleter { inner: None };
58 
59     let history_file = match args().nth(1) {
60         Some(file_name) => {
61             println!("History file: {}", file_name);
62             file_name
63         }
64         None => {
65             eprintln!("No history file provided. Ending example early.");
66             return;
67         }
68     };
69 
70     con.history
71         .set_file_name_and_load_history(history_file)
72         .unwrap();
73 
74     loop {
75         let res = con.read_line("[prompt]$ ", Some(Box::new(highlight_dodo)), &mut completer);
76 
77         match res {
78             Ok(res) => {
79                 match res.as_str() {
80                     "emacs" => {
81                         con.key_bindings = liner::KeyBindings::Emacs;
82                         println!("emacs mode");
83                     }
84                     "vi" => {
85                         con.key_bindings = liner::KeyBindings::Vi;
86                         println!("vi mode");
87                     }
88                     "exit" | "" => {
89                         println!("exiting...");
90                         break;
91                     }
92                     _ => {}
93                 }
94 
95                 if res.is_empty() {
96                     break;
97                 }
98 
99                 con.history.push(res.into()).unwrap();
100             }
101             Err(e) => {
102                 match e.kind() {
103                     // ctrl-c pressed
104                     io::ErrorKind::Interrupted => {}
105                     // ctrl-d pressed
106                     io::ErrorKind::UnexpectedEof => {
107                         println!("exiting...");
108                         break;
109                     }
110                     _ => {
111                         // Ensure that all writes to the history file
112                         // are written before exiting.
113                         panic!("error: {:?}", e)
114                     }
115                 }
116             }
117         }
118     }
119     // Ensure that all writes to the history file are written before exiting.
120     con.history.commit_to_file();
121 }
122