1use std::io::{self, Read, Write};
3use libc;
4use unicode_segmentation::UnicodeSegmentation;
5use utf8parse::{Parser, Receiver};
6
7use config::{ColorMode, Config, OutputStreamType};
8use highlight::Highlighter;
9use keys::{self, KeyPress};
10use line_buffer::LineBuffer;
11use error;
12use Result;
13use super::{truncate, width, Position, RawMode, RawReader, Renderer, Term};
14use StdStream;
15
16const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
17const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
18const STDERR_FILENO: libc::c_int = libc::STDERR_FILENO;
19
20fn get_win_size() -> (usize, usize) {
21 (80, 24)
22}
23
24struct StdinRaw {}
25
26impl Read for StdinRaw {
27 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
28 loop {
29 let res = unsafe {
30 libc::read(
31 STDIN_FILENO as i32,
32 buf.as_mut_ptr() as *mut libc::c_void,
33 buf.len() as libc::size_t,
34 )
35 };
36 if res == -1 {
37 let error = io::Error::last_os_error();
38 if error.kind() != io::ErrorKind::Interrupted {
39 return Err(error);
40 }
41 } else {
42 return Ok(res as usize);
43 }
44 }
45 }
46}
47
48pub type Mode = ConsoleMode;
49
50#[derive(Clone, Copy, Debug)]
51pub struct ConsoleMode {}
52
53impl RawMode for Mode {
54 fn disable_raw_mode(&self) -> Result<()> {
56 Ok(())
57 }
58}
59
60pub type Terminal = FuchsiaTerminal;
61
62#[derive(Clone, Debug)]
63pub struct FuchsiaTerminal {
64 #[allow(unused)]
65 unsupported: bool,
66 stdin_isatty: bool,
67 #[allow(unused)]
68 stdstream_isatty: bool,
69 pub(crate) color_mode: ColorMode,
70 stream_type: OutputStreamType,
71}
72
73struct Utf8 {
74 c: Option<char>,
75 valid: bool,
76}
77
78pub struct FuchsiaRawReader {
79 stdin: StdinRaw,
80 _timeout_ms_unused: i32,
81 buf: [u8; 1],
82 parser: Parser,
83 receiver: Utf8,
84}
85
86impl FuchsiaRawReader {
87 pub fn new(config: &Config) -> Result<FuchsiaRawReader> {
88 Ok(FuchsiaRawReader {
89 stdin: StdinRaw {},
90 _timeout_ms_unused: config.keyseq_timeout(),
91 buf: [0; 1],
92 parser: Parser::new(),
93 receiver: Utf8 {
94 c: None,
95 valid: true,
96 }
97 })
98 }
99
100 fn escape_sequence(&mut self) -> Result<KeyPress> {
102 let seq1 = try!(self.next_char());
104 if seq1 == '[' {
105 self.escape_csi()
107 } else if seq1 == 'O' {
108 self.escape_o()
111 } else if seq1 == '\x1b' {
112 Ok(KeyPress::Esc)
114 } else {
115 Ok(KeyPress::Meta(seq1))
117 }
118 }
119
120 fn escape_csi(&mut self) -> Result<KeyPress> {
122 let seq2 = try!(self.next_char());
123 if seq2.is_digit(10) {
124 match seq2 {
125 '0' | '9' => {
126 debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
127 Ok(KeyPress::UnknownEscSeq)
128 }
129 _ => {
130 self.extended_escape(seq2)
132 }
133 }
134 } else if seq2 == '[' {
135 let seq3 = try!(self.next_char());
136 Ok(match seq3 {
138 'A' => KeyPress::F(1),
139 'B' => KeyPress::F(2),
140 'C' => KeyPress::F(3),
141 'D' => KeyPress::F(4),
142 'E' => KeyPress::F(5),
143 _ => {
144 debug!(target: "rustyline", "unsupported esc sequence: ESC [ [ {:?}", seq3);
145 KeyPress::UnknownEscSeq
146 }
147 })
148 } else {
149 Ok(match seq2 {
151 'A' => KeyPress::Up, 'B' => KeyPress::Down, 'C' => KeyPress::Right, 'D' => KeyPress::Left, 'F' => KeyPress::End,
156 'H' => KeyPress::Home, 'Z' => KeyPress::BackTab,
158 _ => {
159 debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
160 KeyPress::UnknownEscSeq
161 }
162 })
163 }
164 }
165
166 fn extended_escape(&mut self, seq2: char) -> Result<KeyPress> {
168 let seq3 = try!(self.next_char());
169 if seq3 == '~' {
170 Ok(match seq2 {
171 '1' | '7' => KeyPress::Home, '2' => KeyPress::Insert,
173 '3' => KeyPress::Delete, '4' | '8' => KeyPress::End, '5' => KeyPress::PageUp, '6' => KeyPress::PageDown, _ => {
178 debug!(target: "rustyline",
179 "unsupported esc sequence: ESC [ {} ~", seq2);
180 KeyPress::UnknownEscSeq
181 }
182 })
183 } else if seq3.is_digit(10) {
184 let seq4 = try!(self.next_char());
185 if seq4 == '~' {
186 Ok(match (seq2, seq3) {
187 ('1', '1') => KeyPress::F(1), ('1', '2') => KeyPress::F(2), ('1', '3') => KeyPress::F(3), ('1', '4') => KeyPress::F(4), ('1', '5') => KeyPress::F(5), ('1', '7') => KeyPress::F(6), ('1', '8') => KeyPress::F(7), ('1', '9') => KeyPress::F(8), ('2', '0') => KeyPress::F(9), ('2', '1') => KeyPress::F(10), ('2', '3') => KeyPress::F(11), ('2', '4') => KeyPress::F(12), _ => {
200 debug!(target: "rustyline",
201 "unsupported esc sequence: ESC [ {}{} ~", seq2, seq3);
202 KeyPress::UnknownEscSeq
203 }
204 })
205 } else if seq4 == ';' {
206 let seq5 = try!(self.next_char());
207 if seq5.is_digit(10) {
208 let seq6 = try!(self.next_char()); debug!(target: "rustyline",
210 "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6);
211 } else {
212 debug!(target: "rustyline",
213 "unsupported esc sequence: ESC [ {}{} ; {:?}", seq2, seq3, seq5);
214 }
215 Ok(KeyPress::UnknownEscSeq)
216 } else {
217 debug!(target: "rustyline",
218 "unsupported esc sequence: ESC [ {}{} {:?}", seq2, seq3, seq4);
219 Ok(KeyPress::UnknownEscSeq)
220 }
221 } else if seq3 == ';' {
222 let seq4 = try!(self.next_char());
223 if seq4.is_digit(10) {
224 let seq5 = try!(self.next_char());
225 if seq2 == '1' {
226 Ok(match (seq4, seq5) {
227 ('5', 'A') => KeyPress::ControlUp,
228 ('5', 'B') => KeyPress::ControlDown,
229 ('5', 'C') => KeyPress::ControlRight,
230 ('5', 'D') => KeyPress::ControlLeft,
231 ('2', 'A') => KeyPress::ShiftUp,
232 ('2', 'B') => KeyPress::ShiftDown,
233 ('2', 'C') => KeyPress::ShiftRight,
234 ('2', 'D') => KeyPress::ShiftLeft,
235 _ => {
236 debug!(target: "rustyline",
237 "unsupported esc sequence: ESC [ 1 ; {} {:?}", seq4, seq5);
238 KeyPress::UnknownEscSeq
239 }
240 })
241 } else {
242 debug!(target: "rustyline",
243 "unsupported esc sequence: ESC [ {} ; {} {:?}", seq2, seq4, seq5);
244 Ok(KeyPress::UnknownEscSeq)
245 }
246 } else {
247 debug!(target: "rustyline",
248 "unsupported esc sequence: ESC [ {} ; {:?}", seq2, seq4);
249 Ok(KeyPress::UnknownEscSeq)
250 }
251 } else {
252 Ok(match (seq2, seq3) {
253 ('5', 'A') => KeyPress::ControlUp,
254 ('5', 'B') => KeyPress::ControlDown,
255 ('5', 'C') => KeyPress::ControlRight,
256 ('5', 'D') => KeyPress::ControlLeft,
257 _ => {
258 debug!(target: "rustyline",
259 "unsupported esc sequence: ESC [ {} {:?}", seq2, seq3);
260 KeyPress::UnknownEscSeq
261 }
262 })
263 }
264 }
265
266 fn escape_o(&mut self) -> Result<KeyPress> {
268 let seq2 = try!(self.next_char());
269 Ok(match seq2 {
270 'A' => KeyPress::Up, 'B' => KeyPress::Down, 'C' => KeyPress::Right, 'D' => KeyPress::Left, 'F' => KeyPress::End, 'H' => KeyPress::Home, 'P' => KeyPress::F(1), 'Q' => KeyPress::F(2), 'R' => KeyPress::F(3), 'S' => KeyPress::F(4), 'a' => KeyPress::ControlUp,
281 'b' => KeyPress::ControlDown,
282 'c' => KeyPress::ControlRight, 'd' => KeyPress::ControlLeft, _ => {
285 debug!(target: "rustyline", "unsupported esc sequence: ESC O {:?}", seq2);
286 KeyPress::UnknownEscSeq
287 }
288 })
289 }
290}
291
292pub struct FuchsiaRenderer {
293 out: StdStream,
294 cols: usize,
295 buffer: String,
296}
297
298impl FuchsiaRenderer {
299 pub fn new(stream_type: OutputStreamType) -> FuchsiaRenderer {
300 let out = StdStream::from_stream_type(stream_type);
301 let (cols, _) = get_win_size();
302 FuchsiaRenderer {
303 out,
304 cols,
305 buffer: String::with_capacity(1024),
306 }
307 }
308}
309
310impl Renderer for FuchsiaRenderer {
311 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
312 use std::fmt::Write;
313 let mut ab = String::new();
314 if new.row > old.row {
315 let row_shift = new.row - old.row;
317 if row_shift == 1 {
318 ab.push_str("\x1b[B");
319 } else {
320 write!(ab, "\x1b[{}B", row_shift).unwrap();
321 }
322 } else if new.row < old.row {
323 let row_shift = old.row - new.row;
325 if row_shift == 1 {
326 ab.push_str("\x1b[A");
327 } else {
328 write!(ab, "\x1b[{}A", row_shift).unwrap();
329 }
330 }
331 if new.col > old.col {
332 let col_shift = new.col - old.col;
334 if col_shift == 1 {
335 ab.push_str("\x1b[C");
336 } else {
337 write!(ab, "\x1b[{}C", col_shift).unwrap();
338 }
339 } else if new.col < old.col {
340 let col_shift = old.col - new.col;
342 if col_shift == 1 {
343 ab.push_str("\x1b[D");
344 } else {
345 write!(ab, "\x1b[{}D", col_shift).unwrap();
346 }
347 }
348 self.write_and_flush(ab.as_bytes())
349 }
350
351 fn refresh_line(
353 &mut self,
354 prompt: &str,
355 prompt_size: Position,
356 line: &LineBuffer,
357 hint: Option<String>,
358 current_row: usize,
359 old_rows: usize,
360 highlighter: Option<&dyn Highlighter>,
361 ) -> Result<(Position, Position)> {
362 use std::fmt::Write;
363 self.buffer.clear();
364
365 let end_pos = self.calculate_position(line, prompt_size);
367 let cursor = self.calculate_position(&line[..line.pos()], prompt_size);
369
370 let cursor_row_movement = old_rows.checked_sub(current_row).unwrap_or(0);
373 if cursor_row_movement > 0 {
375 write!(self.buffer, "\x1b[{}B", cursor_row_movement).unwrap();
376 }
377 for _ in 0..old_rows {
379 self.buffer.push_str("\r\x1b[0K\x1b[A");
380 }
381 self.buffer.push_str("\r\x1b[0K");
383
384 if let Some(highlighter) = highlighter {
385 self.buffer.push_str(&highlighter.highlight_prompt(prompt));
387 self.buffer
389 .push_str(&highlighter.highlight(line, line.pos()));
390 } else {
391 self.buffer.push_str(prompt);
393 self.buffer.push_str(line);
395 }
396 if let Some(hint) = hint {
398 let truncate = truncate(&hint, end_pos.col, self.cols);
399 if let Some(highlighter) = highlighter {
400 self.buffer.push_str(&highlighter.highlight_hint(truncate));
401 } else {
402 self.buffer.push_str(truncate);
403 }
404 }
405 if end_pos.col == 0 && end_pos.row > 0 {
407 self.buffer.push_str("\n");
408 }
409 let cursor_row_movement = end_pos.row - cursor.row;
411 if cursor_row_movement > 0 {
413 write!(self.buffer, "\x1b[{}A", cursor_row_movement).unwrap();
414 }
415 if cursor.col > 0 {
417 write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap();
418 } else {
419 self.buffer.push('\r');
420 }
421
422 try!(self.out.write_all(self.buffer.as_bytes()));
423 try!(self.out.flush());
424 Ok((cursor, end_pos))
425 }
426
427 fn calculate_position(&self, s: &str, orig: Position) -> Position {
430 let mut pos = orig;
431 let mut esc_seq = 0;
432 for c in s.graphemes(true) {
433 if c == "\n" {
434 pos.row += 1;
435 pos.col = 0;
436 continue;
437 }
438 let cw = width(c, &mut esc_seq);
439 pos.col += cw;
440 if pos.col > self.cols {
441 pos.row += 1;
442 pos.col = cw;
443 }
444 }
445 if pos.col == self.cols {
446 pos.col = 0;
447 pos.row += 1;
448 }
449 pos
450 }
451
452 fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
453 try!(self.out.write_all(buf));
454 try!(self.out.flush());
455 Ok(())
456 }
457
458 fn clear_screen(&mut self) -> Result<()> {
460 self.write_and_flush(b"\x1b[H\x1b[2J")
461 }
462
463 fn sigwinch(&self) -> bool {
465 false
466 }
467
468 fn update_size(&mut self) {
470 let (cols, _) = get_win_size();
471 self.cols = cols;
472 }
473
474 fn get_columns(&self) -> usize {
476 let (cols, _) = get_win_size();
477 cols
478 }
479
480 fn get_rows(&self) -> usize {
482 let (_, rows) = get_win_size();
483 rows
484 }
485}
486
487
488impl RawReader for FuchsiaRawReader {
491 fn next_key(&mut self, _single_esc_abort: bool) -> Result<KeyPress> {
492 let c = try!(self.next_char());
493
494 let key = keys::char_to_key_press(c);
495 if key == KeyPress::Esc {
496 return self.escape_sequence();
497 }
498
499 Ok(key)
500 }
501
502 fn next_char(&mut self) -> Result<char> {
503 loop {
504 let n = try!(self.stdin.read(&mut self.buf));
505 if n == 0 {
506 return Err(error::ReadlineError::Eof);
507 }
508 let b = self.buf[0];
509 self.parser.advance(&mut self.receiver, b);
510 if !self.receiver.valid {
511 return Err(error::ReadlineError::Utf8Error);
512 } else if self.receiver.c.is_some() {
513 return Ok(self.receiver.c.take().unwrap());
514 }
515 }
516 }
517}
518
519impl Receiver for Utf8 {
520 fn codepoint(&mut self, c: char) {
522 self.c = Some(c);
523 self.valid = true;
524 }
525
526 fn invalid_sequence(&mut self) {
528 self.c = None;
529 self.valid = false;
530 }
531}
532
533fn is_a_tty(fd: libc::c_int) -> bool {
534 unsafe { libc::isatty(fd) != 0 }
535}
536
537impl Term for FuchsiaTerminal {
538 type Reader = FuchsiaRawReader;
539 type Writer = FuchsiaRenderer;
540 type Mode = Mode;
541
542 fn new(color_mode: ColorMode, stream_type: OutputStreamType) -> FuchsiaTerminal {
543 FuchsiaTerminal {
544 unsupported: false,
545 stdin_isatty: is_a_tty(STDIN_FILENO as i32),
546 stdstream_isatty: is_a_tty(if stream_type == OutputStreamType::Stdout {
547 STDOUT_FILENO as i32
548 } else {
549 STDERR_FILENO as i32
550 }),
551 color_mode,
552 stream_type,
553 }
554 }
555 fn is_unsupported(&self) -> bool {
558 false
559 }
560 fn is_stdin_tty(&self) -> bool {
562 self.stdin_isatty
563 }
564 fn colors_enabled(&self) -> bool {
566 true
567 }
568 fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
570 Ok(Mode {})
571 }
572 fn create_reader(&self, config: &Config) -> Result<Self::Reader> {
574 FuchsiaRawReader::new(config)
575 }
576 fn create_writer(&self) -> Self::Writer {
578 FuchsiaRenderer::new(self.stream_type)
579 }
580}