diff options
-rw-r--r-- | alacritty.yml | 32 | ||||
-rw-r--r-- | src/io.rs | 194 | ||||
-rw-r--r-- | src/main.rs | 9 |
3 files changed, 232 insertions, 3 deletions
diff --git a/alacritty.yml b/alacritty.yml index e0d1d3fa..7e3d81a1 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -19,3 +19,35 @@ font: # Should display the render timer render_timer: false + +# Colors +colors: + # Default colors + default: + background: 0x000000 + foreground: 0xeaeaea + + # Normal colors + normal: + black: 0x000000 + red: 0xd54e53 + green: 0xb9ca4a + yellow: 0xe6c547 + blue: 0x7aa6da + magenta: 0xc397d8 + cyan: 0x70c0ba + white: 0x424242 + + # Bright colors + bright: + black: 0x666666 + red: 0xff3334 + green: 0x9ec400 + yellow: 0xe7c547 + blue: 0x7aa6da + magenta: 0xb77ee0 + cyan: 0x54ced6 + white: 0x2a2a2a + +# Display tabs using this many cells +tabspaces: 8 diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 00000000..688e72a4 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,194 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unmerged utf8 chars iterator vendored from std::io +//! +use std::io::{BufRead, ErrorKind, Error}; +use std::fmt; +use std::error as std_error; +use std::result; +use std::char; + +static UTF8_CHAR_WIDTH: [u8; 256] = [ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF +0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF +4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF +]; + +/// Given a first byte, determine how many bytes are in this UTF-8 character +#[inline] +pub fn utf8_char_width(b: u8) -> usize { + return UTF8_CHAR_WIDTH[b as usize] as usize; +} + +/// An iterator over the `char`s of a reader. +/// +/// This struct is generally created by calling [`utf8_chars()`][utf8_chars] on a reader. +/// Please see the documentation of `utf8_chars()` for more details. +/// +/// [utf8_chars]: trait.BufRead.html#method.utf8_chars +pub struct Utf8Chars<R> { + inner: R, +} + +impl<R> Utf8Chars<R> { + pub fn new(inner: R) -> Utf8Chars<R> { + Utf8Chars { inner: inner } + } +} + +/// An enumeration of possible errors that can be generated from the `Utf8Chars` +/// adapter. +#[derive(Debug)] +pub enum Utf8CharsError { + /// Variant representing that the underlying stream was read successfully + /// but contains a byte sequence ill-formed in UTF-8. + InvalidUtf8, + + /// Variant representing that the underlying stream contains the start + /// of a byte sequence well-formed in UTF-8, but ends prematurely. + /// + /// Contains number of unused bytes + IncompleteUtf8(u8), + + /// Variant representing that an I/O error occurred. + Io(Error), +} + +impl<R: BufRead> Iterator for Utf8Chars<R> { + type Item = result::Result<char, Utf8CharsError>; + + // allow(unused_assignments) because consumed += 1 is not recognized as being used + #[allow(unused_assignments)] + fn next(&mut self) -> Option<result::Result<char, Utf8CharsError>> { + macro_rules! read_byte { + (EOF => $on_eof: expr) => { + { + let byte; + loop { + match self.inner.fill_buf() { + Ok(buffer) => { + if let Some(&b) = buffer.first() { + byte = b; + break + } else { + $on_eof + } + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Some(Err(Utf8CharsError::Io(e))), + } + } + byte + } + } + } + + let first = read_byte!(EOF => return None); + self.inner.consume(1); + + let mut consumed = 1; + + macro_rules! continuation_byte { + ($range: pat) => { + { + match read_byte!(EOF => return Some(Err(Utf8CharsError::IncompleteUtf8(consumed)))) { + byte @ $range => { + self.inner.consume(1); + consumed += 1; + (byte & 0b0011_1111) as u32 + } + _ => return Some(Err(Utf8CharsError::InvalidUtf8)) + } + } + } + } + + // Ranges can be checked against https://tools.ietf.org/html/rfc3629#section-4 + let code_point = match utf8_char_width(first) { + 1 => return Some(Ok(first as char)), + 2 => { + let second = continuation_byte!(0x80...0xBF); + ((first & 0b0001_1111) as u32) << 6 | second + } + 3 => { + let second = match first { + 0xE0 => continuation_byte!(0xA0...0xBF), + 0xE1...0xEC => continuation_byte!(0x80...0xBF), + 0xED => continuation_byte!(0x80...0x9F), + 0xEE...0xEF => continuation_byte!(0x80...0xBF), + _ => unreachable!(), + }; + let third = continuation_byte!(0x80...0xBF); + ((first & 0b0000_1111) as u32) << 12 | second << 6 | third + } + 4 => { + let second = match first { + 0xF0 => continuation_byte!(0x90...0xBF), + 0xF0...0xF3 => continuation_byte!(0x80...0xBF), + 0xF4 => continuation_byte!(0x80...0x8F), + _ => unreachable!(), + }; + let third = continuation_byte!(0x80...0xBF); + let fourth = continuation_byte!(0x80...0xBF); + ((first & 0b0000_0111) as u32) << 18 | second << 12 | third << 6 | fourth + } + _ => return Some(Err(Utf8CharsError::InvalidUtf8)) + }; + unsafe { + Some(Ok(char::from_u32_unchecked(code_point))) + } + } +} + +impl std_error::Error for Utf8CharsError { + fn description(&self) -> &str { + match *self { + Utf8CharsError::InvalidUtf8 => "invalid UTF-8 byte sequence", + Utf8CharsError::IncompleteUtf8(_) => { + "stream ended in the middle of an UTF-8 byte sequence" + } + Utf8CharsError::Io(ref e) => std_error::Error::description(e), + } + } + fn cause(&self) -> Option<&std_error::Error> { + match *self { + Utf8CharsError::InvalidUtf8 | Utf8CharsError::IncompleteUtf8(_) => None, + Utf8CharsError::Io(ref e) => e.cause(), + } + } +} + +impl fmt::Display for Utf8CharsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Utf8CharsError::InvalidUtf8 => { + "invalid UTF-8 byte sequence".fmt(f) + } + Utf8CharsError::IncompleteUtf8(_) => { + "stream ended in the middle of an UTF-8 byte sequence".fmt(f) + } + Utf8CharsError::Io(ref e) => e.fmt(f), + } + } +} diff --git a/src/main.rs b/src/main.rs index b699fe44..cbfa7425 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ #![feature(question_mark)] #![feature(range_contains)] #![feature(inclusive_range_syntax)] -#![feature(io)] #![feature(drop_types_in_const)] #![feature(unicode)] #![feature(custom_derive, plugin)] @@ -47,8 +46,9 @@ mod tty; pub mod ansi; mod term; mod util; +mod io; -use std::io::{Read, Write, BufWriter}; +use std::io::{Write, BufWriter, BufReader}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc}; @@ -62,6 +62,8 @@ use term::Term; use tty::process_should_exit; use util::thread; +use io::Utf8Chars; + /// Things that the render/update thread needs to respond to #[derive(Debug)] enum Event { @@ -189,7 +191,8 @@ fn main() { resize_sender = Some(tx.clone()); } let reader_thread = thread::spawn_named("TTY Reader", move || { - for c in reader.chars() { + let chars = Utf8Chars::new(BufReader::new(reader)); + for c in chars { let c = c.unwrap(); reader_tx.send(Event::PtyChar(c)).unwrap(); } |