Skip to main content

interaction/
lib.rs

1//! Interaction is a minimal and a simple readline library.
2//!
3//! Features
4//! * Single line editing mode
5//! * Multi line editing mode
6//! * Key bindings
7//! * History
8//! * Completion
9//!
10//! # Example
11//! ```no_run
12//! use interaction::InteractionBuilder;
13//! use std::io;
14//!
15//! fn main() {
16//!     let history_file = "./.example_history";
17//!     let mut inter = InteractionBuilder::new()
18//!         .prompt_str(";;>")
19//!         .history_limit(5)
20//!         .completion(|_input, completions| {
21//!             completions.push(b"foo".to_vec());
22//!             completions.push(b"bar".to_vec());
23//!         })
24//!         .load_history(history_file)
25//!         .unwrap()
26//!         .build();
27//!     loop {
28//!         match inter.line() {
29//!             Ok(input) => {
30//!                 // write any code.
31//!             }
32//!             Err(e) if e.kind() == io::ErrorKind::Interrupted => {
33//!                 inter.save_history(history_file).unwrap();
34//!                 break;
35//!             }
36//!             Err(_) => {
37//!                 break;
38//!             }
39//!         }
40//!     }
41//! }
42//! ```
43
44use libc;
45use std::collections::VecDeque;
46use std::fs::File;
47use std::io;
48use std::io::{Read, Write};
49use std::os::unix::io::RawFd;
50use std::path::Path;
51use termios::*;
52
53fn get_stdin_fd() -> RawFd {
54    libc::STDIN_FILENO
55}
56
57fn get_stdout_fd() -> RawFd {
58    libc::STDOUT_FILENO
59}
60
61fn get_col() -> u16 {
62    let mut winsize = libc::winsize {
63        ws_row: 0,
64        ws_col: 0,
65        ws_xpixel: 0,
66        ws_ypixel: 0,
67    };
68    if unsafe { libc::ioctl(get_stdout_fd(), libc::TIOCGWINSZ, &mut winsize) } == 0 {
69        winsize.ws_col
70    } else {
71        80
72    }
73}
74
75mod keys {
76    pub(crate) const CTRL_A: u8 = 1;
77    pub(crate) const CTRL_B: u8 = 2;
78    pub(crate) const CTRL_C: u8 = 3;
79    pub(crate) const CTRL_D: u8 = 4;
80    pub(crate) const CTRL_E: u8 = 5;
81    pub(crate) const CTRL_F: u8 = 6;
82    pub(crate) const CTRL_H: u8 = 8;
83    pub(crate) const CTRL_I: u8 = 9;
84    pub(crate) const CTRL_J: u8 = 10;
85    pub(crate) const CTRL_K: u8 = 11;
86    pub(crate) const CTRL_L: u8 = 12;
87    pub(crate) const CTRL_M: u8 = 13;
88    pub(crate) const ESC: u8 = 27;
89    pub(crate) const ONE: u8 = 49;
90    pub(crate) const TWO: u8 = 50;
91    pub(crate) const THREE: u8 = 51;
92    pub(crate) const FOUR: u8 = 52;
93    pub(crate) const FIVE: u8 = 53;
94    pub(crate) const SIX: u8 = 54;
95    pub(crate) const A: u8 = 65;
96    pub(crate) const B: u8 = 66;
97    pub(crate) const C: u8 = 67;
98    pub(crate) const D: u8 = 68;
99    // This char is `[`.
100    pub(crate) const LEFT_BRACKET: u8 = 91;
101    pub(crate) const BACKSPACE: u8 = 127;
102}
103
104/// The type is a callback for completion.
105pub type Completion = fn(&Vec<u8>, &mut Vec<Vec<u8>>);
106
107/// The struct is to management the history of command line.
108pub struct History {
109    commands: VecDeque<Vec<u8>>,
110    position: usize,
111    // if limit is 0, history is unlimited.
112    limit: usize,
113}
114
115impl History {
116    /// Initialize history. `limit` is the maximum size of history. If limit is zero, unlimited.
117    pub fn new(limit: usize) -> Self {
118        History {
119            commands: VecDeque::new(),
120            position: 0,
121            limit,
122        }
123    }
124
125    /// Return a next command from the current line.
126    pub(crate) fn next(&mut self) -> Option<&Vec<u8>> {
127        if self.commands.len() == 0 || self.position == self.commands.len() {
128            None
129        } else {
130            self.position += 1;
131            self.commands.get(self.position)
132        }
133    }
134
135    /// Return a previous command from the current line.
136    pub(crate) fn prev(&mut self) -> Option<&Vec<u8>> {
137        if self.commands.len() == 0 || self.position == 0 {
138            None
139        } else {
140            self.position -= 1;
141            self.commands.get(self.position)
142        }
143    }
144
145    fn _append(&mut self, history: Vec<u8>) {
146        if self.limit > 0 && self.commands.len() == self.limit {
147            self.commands.pop_front();
148        }
149        self.commands.push_back(history);
150    }
151
152    /// Append a new command.
153    pub fn append(&mut self, history: Vec<u8>) {
154        self._append(history);
155        self.position = self.commands.len();
156    }
157
158    /// Load a history from the given `file_path`.
159    pub fn load<P: AsRef<Path>>(&mut self, file_path: P) -> io::Result<()> {
160        let mut file = match File::open(file_path) {
161            Ok(file) => file,
162            Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
163            Err(e) => return Err(e),
164        };
165        let mut buffer = vec![0; 4096];
166        let mut cmd = vec![0; 0];
167        loop {
168            let n = file.read(&mut buffer)?;
169            if n == 0 {
170                break;
171            }
172            for c in buffer[..n].iter() {
173                if *c == b'\n' && cmd.len() > 0 {
174                    self._append(cmd);
175                    cmd = vec![0; 0];
176                    continue;
177                }
178                cmd.push(*c);
179            }
180        }
181        if cmd.len() > 0 {
182            self._append(cmd);
183        }
184        if self.commands.len() > 0 {
185            self.position = self.commands.len();
186        }
187        Ok(())
188    }
189
190    /// Save the history to the given `file_path`.
191    pub fn save<P: AsRef<Path>>(&mut self, file_path: P) -> io::Result<()> {
192        File::create(file_path).and_then(|mut file| {
193            for cmd in self.commands.iter() {
194                file.write_all(cmd).and(file.write_all(b"\n"))?;
195            }
196            file.flush()
197        })
198    }
199}
200
201struct Line<'a> {
202    backup: Termios,
203    position: usize,
204    buffer: &'a mut Vec<u8>,
205    prompt: &'a [u8],
206    completion: &'a Option<Completion>,
207    multi: bool,
208    row: usize,
209    history: &'a mut History,
210}
211
212impl<'a> Line<'a> {
213    fn new(
214        buffer: &'a mut Vec<u8>,
215        prompt: &'a [u8],
216        completion: &'a Option<Completion>,
217        multi: bool,
218        history: &'a mut History,
219    ) -> Self {
220        let backup = Termios::from_fd(get_stdin_fd()).unwrap();
221        Line::enable_raw_mode().unwrap();
222        Line {
223            backup,
224            position: 0,
225            buffer,
226            prompt,
227            completion,
228            multi,
229            row: 0,
230            history,
231        }
232    }
233
234    fn enable_raw_mode() -> io::Result<()> {
235        let fd = get_stdin_fd();
236        Termios::from_fd(fd).and_then(|mut termios| {
237            termios.c_iflag &= !(BRKINT | INPCK | ISTRIP | ICRNL | IXON);
238            termios.c_oflag &= !OPOST;
239            termios.c_cflag |= CS8;
240            termios.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
241            termios.c_cc[VMIN] = 1;
242            termios.c_cc[VTIME] = 0;
243            tcsetattr(fd, TCSANOW, &termios).and(tcflush(fd, TCIOFLUSH))
244        })
245    }
246
247    fn disable_raw_mode(&self) -> io::Result<()> {
248        let fd = get_stdin_fd();
249        tcsetattr(fd, TCSANOW, &self.backup).and(tcflush(fd, TCIOFLUSH))
250    }
251
252    fn refresh_single_line(&self) -> io::Result<()> {
253        let mut stdout = io::stdout();
254        stdout
255            .write_all(
256                &[
257                    b"\x1b[0G\x1b[K",
258                    self.prompt,
259                    &self.buffer[..],
260                    format!("\r\x1b[{}C", self.position + self.prompt.len()).as_bytes(),
261                ]
262                .concat(),
263            )
264            .and(stdout.flush())
265    }
266
267    fn refresh_multi_line(&mut self) -> io::Result<()> {
268        let col = get_col() as usize;
269        let mut stdout = io::stdout();
270        if self.row == 0 {
271            stdout.write_all(b"\x1b[0G\x1b[J")?;
272        } else {
273            stdout.write_all(format!("\x1b[0G\x1b[{}A\x1b[J", self.row).as_bytes())?;
274        }
275        let mut cnt = 0;
276        let mut row: usize = 0;
277        for c in self.prompt.iter().chain(self.buffer.iter()) {
278            stdout.write_all(&[*c])?;
279            cnt += 1;
280            if cnt == col {
281                stdout.write_all(b"\n\x1b[0G")?;
282                cnt = 0;
283                row += 1;
284            }
285        }
286        stdout.write_all(b"\r")?;
287        if row == 0 {
288            stdout.write_all(b"\x1b[0G")?;
289        } else {
290            stdout.write_all(format!("\x1b[0G\x1b[{}A", row).as_bytes())?;
291        }
292        let pos = self.prompt.len() + self.position;
293        let m = pos % col;
294        self.row = pos / col;
295        if self.row > 0 {
296            stdout.write_all(format!("\x1b[{}B", self.row).as_bytes())?;
297        }
298        if m > 0 {
299            stdout.write_all(format!("\x1b[{}C", m).as_bytes())?;
300        }
301        stdout.flush()?;
302        Ok(())
303    }
304
305    fn refresh_line(&mut self) -> io::Result<()> {
306        if self.multi {
307            self.refresh_multi_line()
308        } else {
309            self.refresh_single_line()
310        }
311    }
312
313    fn completion(&mut self, callback: &Completion) -> io::Result<u8> {
314        let mut completions = Vec::new();
315        callback(self.buffer, &mut completions);
316        if completions.len() == 0 {
317            return Ok(0);
318        }
319        let mut stdin = io::stdin();
320        let bk = self.buffer.clone();
321        let mut buf = vec![0; 1];
322        loop {
323            for comp in completions.iter() {
324                self.buffer.clear();
325                self.buffer.extend(comp);
326                self.position = self.buffer.len();
327                self.refresh_line()?;
328
329                let n = stdin.read(&mut buf)?;
330                assert_eq!(n, 1);
331
332                match buf[0] {
333                    keys::CTRL_I => {
334                        continue;
335                    }
336                    keys::ESC => {
337                        self.buffer.clear();
338                        self.buffer.extend(&bk);
339                        self.position = self.buffer.len();
340                        self.refresh_line()?;
341                        return Ok(buf[0]);
342                    }
343                    _ => {
344                        return Ok(buf[0]);
345                    }
346                }
347            }
348        }
349    }
350
351    fn fetch(mut self) -> io::Result<()> {
352        let mut stdin = io::stdin();
353
354        self.refresh_line()?;
355
356        let mut buf = vec![0; 1];
357        let mut tmp = vec![0; 0];
358        let mut used = false;
359        loop {
360            let n = stdin.read(&mut buf)?;
361            assert_eq!(n, 1);
362
363            if buf[0] == keys::ESC {
364                let mut buf2 = vec![0; 3];
365                let n = stdin.read(&mut buf2[0..1])?;
366                assert_eq!(n, 1);
367                match buf2[0] {
368                    // arrows, home, end or del
369                    keys::LEFT_BRACKET => {
370                        let n = stdin.read(&mut buf2[1..2])?;
371                        assert_eq!(n, 1);
372                        match buf2[1] {
373                            // HOME
374                            keys::ONE => {
375                                let _ = stdin.read(&mut buf2[2..3])?;
376                                buf[0] = keys::CTRL_A;
377                            }
378                            // INS
379                            keys::TWO => {
380                                let _ = stdin.read(&mut buf2[2..3])?;
381                                continue;
382                            }
383                            // DEL
384                            keys::THREE => {
385                                let _ = stdin.read(&mut buf2[2..3])?;
386                                if self.position < self.buffer.len() {
387                                    buf[0] = keys::CTRL_D;
388                                } else {
389                                    continue;
390                                }
391                            }
392                            // END
393                            keys::FOUR => {
394                                let _ = stdin.read(&mut buf2[2..3])?;
395                                buf[0] = keys::CTRL_E;
396                            }
397                            // PgUp
398                            keys::FIVE => {
399                                let _ = stdin.read(&mut buf2[2..3])?;
400                                continue;
401                            }
402                            // PgDn
403                            keys::SIX => {
404                                let _ = stdin.read(&mut buf2[2..3])?;
405                                continue;
406                            }
407                            // Up
408                            keys::A => match self.history.prev() {
409                                Some(cmd) => {
410                                    if !used {
411                                        tmp.extend(&self.buffer[..]);
412                                        used = true;
413                                    }
414                                    self.buffer.clear();
415                                    self.buffer.extend(cmd);
416                                    self.position = self.buffer.len();
417                                    self.refresh_line()?;
418                                    continue;
419                                }
420                                None => {
421                                    continue;
422                                }
423                            },
424                            // Down
425                            keys::B => match self.history.next() {
426                                Some(cmd) => {
427                                    self.buffer.clear();
428                                    self.buffer.extend(cmd);
429                                    self.position = self.buffer.len();
430                                    self.refresh_line()?;
431                                    continue;
432                                }
433                                None => {
434                                    if used {
435                                        used = false;
436                                        self.buffer.clear();
437                                        self.buffer.extend(&tmp[..]);
438                                        self.position = self.buffer.len();
439                                        tmp.clear();
440                                        self.refresh_line()?;
441                                    }
442                                    continue;
443                                }
444                            },
445                            // Right
446                            keys::C => {
447                                buf[0] = keys::CTRL_F;
448                            }
449                            // Left
450                            keys::D => {
451                                buf[0] = keys::CTRL_B;
452                            }
453                            _ => {
454                                buf[0] = buf2[1];
455                            }
456                        }
457                    }
458                    _ => {
459                        // handle to esc
460                        // ...
461                        buf[0] = buf2[0];
462                    }
463                }
464            }
465
466            // Tab
467            if buf[0] == keys::CTRL_I {
468                match self.completion {
469                    Some(callback) => {
470                        let c = self.completion(callback)?;
471                        if c == 0 {
472                            continue;
473                        }
474                        buf[0] = c;
475                    }
476                    None => continue,
477                }
478            }
479
480            match buf[0] {
481                // Move the cursor start of line.
482                keys::CTRL_A => {
483                    self.position = 0;
484                    self.refresh_line()?;
485                }
486                // Move the cursor forward 1 column.
487                keys::CTRL_B => {
488                    if self.position == 0 {
489                        continue;
490                    }
491                    self.position -= 1;
492                    self.refresh_line()?;
493                }
494                // Exit the process.
495                keys::CTRL_C => {
496                    self.disable_raw_mode()?;
497                    return Err(io::ErrorKind::Interrupted.into());
498                }
499                keys::CTRL_D => {
500                    // If the buffer is empty, exit the process.
501                    if self.buffer.len() == 0 {
502                        self.disable_raw_mode()?;
503                        return Err(io::ErrorKind::Interrupted.into());
504                    // Delete a char at the cursor.
505                    } else if self.position < self.buffer.len() {
506                        self.buffer.remove(self.position);
507                        self.refresh_line()?;
508                    }
509                }
510                // Move the cursor end of line.
511                keys::CTRL_E => {
512                    self.position = self.buffer.len();
513                    self.refresh_line()?;
514                }
515                // Move the cursor backward 1 column.
516                keys::CTRL_F => {
517                    if self.position == self.buffer.len() {
518                        continue;
519                    }
520                    self.position += 1;
521                    self.refresh_line()?;
522                }
523                keys::CTRL_H | keys::BACKSPACE => {
524                    if self.position == 0 || self.buffer.len() == 0 {
525                        continue;
526                    }
527                    self.position -= 1;
528                    self.buffer.remove(self.position);
529                    self.refresh_line()?;
530                }
531                // Enter
532                keys::CTRL_J | keys::CTRL_M => {
533                    break;
534                }
535                keys::CTRL_K => {
536                    self.buffer.truncate(self.position);
537                    self.refresh_line()?;
538                }
539                keys::CTRL_L => {
540                    let mut stdout = io::stdout();
541                    stdout.write_all(b"\x1b[H\x1b[2J")?;
542                    self.refresh_line()?;
543                }
544                // esc,
545                keys::ESC => {
546                    continue;
547                }
548                _ => {
549                    if self.position < self.buffer.len() {
550                        self.buffer[self.position] = buf[0];
551                    } else {
552                        self.buffer.extend(&buf);
553                    }
554                    self.position += 1;
555                    self.refresh_line()?;
556                }
557            }
558        }
559        let mut stdout = io::stdout();
560        stdout
561            .write_all(format!("\n\x1b[{}D", self.prompt.len() + self.position).as_bytes())
562            .and(stdout.flush())
563    }
564}
565
566impl<'a> Drop for Line<'a> {
567    fn drop(&mut self) {
568        self.disable_raw_mode().unwrap()
569    }
570}
571
572/// A instance of interaction.
573pub struct Interaction {
574    prompt: Vec<u8>,
575    completion: Option<Completion>,
576    /// If true, the interaction mode is multi line.
577    pub multi: bool,
578    history: History,
579}
580
581impl Interaction {
582    /// Initialize a interaction.
583    pub fn new(
584        prompt: Vec<u8>,
585        completion: Option<Completion>,
586        multi: bool,
587        limit: usize,
588    ) -> Self {
589        Interaction {
590            prompt,
591            completion,
592            multi,
593            history: History::new(limit),
594        }
595    }
596
597    /// Initialize interaction from prompt.
598    pub fn from(prompt: &[u8]) -> Self {
599        Interaction::new(prompt.to_vec(), None, true, 0)
600    }
601
602    /// Initialize interaction from prompt.
603    pub fn from_str(prompt: &str) -> Self {
604        Interaction::new(prompt.as_bytes().to_vec(), None, true, 0)
605    }
606
607    /// Get the line of input.
608    pub fn line(&mut self) -> io::Result<Vec<u8>> {
609        let mut buffer = vec![0; 0];
610        Line::new(
611            &mut buffer,
612            &self.prompt,
613            &self.completion,
614            self.multi,
615            &mut self.history,
616        )
617        .fetch()
618        .and_then(|_| {
619            if buffer.len() > 0 {
620                self.history.append(buffer.clone());
621            }
622            Ok(buffer)
623        })
624    }
625
626    /// Set the prompt.
627    pub fn set_prompt(&mut self, prompt: &[u8]) {
628        self.prompt = prompt.to_vec();
629    }
630
631    /// Set the completion.
632    pub fn set_completion(&mut self, completion: Completion) {
633        self.completion = Some(completion);
634    }
635
636    /// Set the maximum size of history.
637    pub fn set_history_limit(&mut self, limit: usize) {
638        self.history = History::new(limit);
639    }
640
641    /// Load a history from `file_path`.
642    pub fn load_history<P: AsRef<Path>>(&mut self, file_path: P) -> io::Result<()> {
643        self.history.load(file_path)
644    }
645
646    /// Save the history to `file_path`.
647    pub fn save_history<P: AsRef<Path>>(&mut self, file_path: P) -> io::Result<()> {
648        self.history.save(file_path)
649    }
650}
651
652/// Builder of [Interaction](struct.Interaction.html).
653///
654/// # Example
655/// ```no_run
656/// use interaction::InteractionBuilder;
657///
658/// let history_file = "./.example_history";
659/// let inter = InteractionBuilder::new()
660///     .prompt_str(";;>")
661///     .history_limit(5)
662///     .completion(|_input, completions| {
663///         completions.push(b"foo".to_vec());
664///         completions.push(b"bar".to_vec());
665///     })
666///     .load_history(history_file)
667///     .unwrap()
668///     .build();
669/// ```
670pub struct InteractionBuilder {
671    prompt: Vec<u8>,
672    completion: Option<Completion>,
673    multi: bool,
674    history: History,
675}
676
677impl InteractionBuilder {
678    /// Initialize a builder.
679    pub fn new() -> Self {
680        InteractionBuilder {
681            prompt: vec![0; 0],
682            completion: None,
683            multi: true,
684            history: History::new(0),
685        }
686    }
687
688    /// Build a interaction.
689    pub fn build(self) -> Interaction {
690        Interaction {
691            prompt: self.prompt,
692            completion: self.completion,
693            multi: self.multi,
694            history: self.history,
695        }
696    }
697
698    /// Set a prompt.
699    pub fn prompt(mut self, prompt: &[u8]) -> Self {
700        self.prompt = prompt.to_vec();
701        self
702    }
703
704    /// Set a prompt.
705    pub fn prompt_str(mut self, prompt: &str) -> Self {
706        self.prompt = prompt.as_bytes().to_vec();
707        self
708    }
709
710    /// Set a completion.
711    pub fn completion(mut self, completion: Completion) -> Self {
712        self.completion = Some(completion);
713        self
714    }
715
716    /// Set a mode.
717    pub fn mode(mut self, multi: bool) -> Self {
718        self.multi = multi;
719        self
720    }
721
722    /// Set a maximum size of history.
723    pub fn history_limit(mut self, limit: usize) -> Self {
724        self.history = History::new(limit);
725        self
726    }
727
728    /// Load a history from `file_path`.
729    pub fn load_history<P: AsRef<Path>>(mut self, file_path: P) -> io::Result<Self> {
730        self.history.load(file_path).and(Ok(self))
731    }
732}