rustyline/tty/
mod.rs
1use std::io::{self, Write};
3use unicode_segmentation::UnicodeSegmentation;
4use unicode_width::UnicodeWidthStr;
5
6use config::{ColorMode, Config, OutputStreamType};
7use highlight::Highlighter;
8use keys::KeyPress;
9use line_buffer::LineBuffer;
10use Result;
11
12pub trait RawMode: Sized {
14 fn disable_raw_mode(&self) -> Result<()>;
16}
17
18pub trait RawReader {
20 fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyPress>;
22 #[cfg(unix)]
24 fn next_char(&mut self) -> Result<char>;
25}
26
27#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
28pub struct Position {
29 pub col: usize,
30 pub row: usize,
31}
32
33pub trait Renderer {
35 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()>;
36
37 fn refresh_line(
39 &mut self,
40 prompt: &str,
41 prompt_size: Position,
42 line: &LineBuffer,
43 hint: Option<String>,
44 current_row: usize,
45 old_rows: usize,
46 highlighter: Option<&dyn Highlighter>,
47 ) -> Result<(Position, Position)>;
48
49 fn calculate_position(&self, s: &str, orig: Position) -> Position;
52
53 fn write_and_flush(&mut self, buf: &[u8]) -> Result<()>;
54
55 fn beep(&mut self) -> Result<()> {
58 try!(io::stderr().write_all(b"\x07"));
60 try!(io::stderr().flush());
61 Ok(())
62 }
63
64 fn clear_screen(&mut self) -> Result<()>;
66
67 fn sigwinch(&self) -> bool;
69 fn update_size(&mut self);
71 fn get_columns(&self) -> usize;
73 fn get_rows(&self) -> usize;
75}
76
77impl<'a, R: Renderer + ?Sized> Renderer for &'a mut R {
78 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
79 (**self).move_cursor(old, new)
80 }
81
82 fn refresh_line(
83 &mut self,
84 prompt: &str,
85 prompt_size: Position,
86 line: &LineBuffer,
87 hint: Option<String>,
88 current_row: usize,
89 old_rows: usize,
90 highlighter: Option<&dyn Highlighter>,
91 ) -> Result<(Position, Position)> {
92 (**self).refresh_line(
93 prompt,
94 prompt_size,
95 line,
96 hint,
97 current_row,
98 old_rows,
99 highlighter,
100 )
101 }
102
103 fn calculate_position(&self, s: &str, orig: Position) -> Position {
104 (**self).calculate_position(s, orig)
105 }
106
107 fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
108 (**self).write_and_flush(buf)
109 }
110
111 fn beep(&mut self) -> Result<()> {
112 (**self).beep()
113 }
114
115 fn clear_screen(&mut self) -> Result<()> {
116 (**self).clear_screen()
117 }
118
119 fn sigwinch(&self) -> bool {
120 (**self).sigwinch()
121 }
122
123 fn update_size(&mut self) {
124 (**self).update_size()
125 }
126
127 fn get_columns(&self) -> usize {
128 (**self).get_columns()
129 }
130
131 fn get_rows(&self) -> usize {
132 (**self).get_rows()
133 }
134}
135
136pub trait Term {
138 type Reader: RawReader; type Writer: Renderer; type Mode: RawMode;
141
142 fn new(color_mode: ColorMode, stream: OutputStreamType) -> Self;
143 fn is_unsupported(&self) -> bool;
146 fn is_stdin_tty(&self) -> bool;
148 fn colors_enabled(&self) -> bool;
150 fn enable_raw_mode(&mut self) -> Result<Self::Mode>;
152 fn create_reader(&self, config: &Config) -> Result<Self::Reader>;
154 fn create_writer(&self) -> Self::Writer;
156}
157
158fn truncate(text: &str, col: usize, max_col: usize) -> &str {
159 let mut col = col;
160 let mut esc_seq = 0;
161 let mut end = text.len();
162 for (i, s) in text.grapheme_indices(true) {
163 col += width(s, &mut esc_seq);
164 if col > max_col {
165 end = i;
166 break;
167 }
168 }
169 &text[..end]
170}
171
172fn width(s: &str, esc_seq: &mut u8) -> usize {
173 if *esc_seq == 1 {
174 if s == "[" {
175 *esc_seq = 2;
177 } else {
178 *esc_seq = 0;
180 }
181 0
182 } else if *esc_seq == 2 {
183 if s == ";" || (s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9') {
184 } else {
188 *esc_seq = 0;
190 }
191 0
192 } else if s == "\x1b" {
193 *esc_seq = 1;
194 0
195 } else if s == "\n" {
196 0
197 } else {
198 s.width()
199 }
200}
201
202#[cfg(all(windows, not(test)))]
205mod windows;
206#[cfg(all(windows, not(test)))]
207pub use self::windows::*;
208
209#[cfg(all(unix, not(any(test, target_os = "fuchsia"))))]
212mod unix;
213#[cfg(all(unix, not(any(test, target_os = "fuchsia"))))]
214pub use self::unix::*;
215
216#[cfg(target_os = "fuchsia")]
219mod fuchsia;
220#[cfg(target_os = "fuchsia")]
221pub use self::fuchsia::*;
222
223#[cfg(test)]
224mod test;
225#[cfg(test)]
226pub use self::test::*;