From 119c7d2856aea24d9d29040b97b778e50c88e70d Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Wed, 1 Mar 2017 22:24:37 -0800 Subject: [PATCH] Add support for wide characters --- Cargo.lock | 1 + Cargo.toml | 1 + src/lib.rs | 20 ++++-------- src/term/cell.rs | 12 ++++--- src/term/mod.rs | 85 ++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8c8fd42..86c6dc54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,7 @@ dependencies = [ "serde_derive 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "vte 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 45a7a496..a1b135cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ xdg = "2.0.0" log = "0.3" clap = "2.20" fnv = "1.0.5" +unicode-width = "0.1.4" clippy = { version = "0.0.104", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 58ebb07b..be4cae33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,19 +21,18 @@ #![cfg_attr(feature = "clippy", deny(wrong_pub_self_convention))] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] -#[macro_use] -extern crate serde_derive; +#[macro_use] extern crate bitflags; +#[macro_use] extern crate clap; +#[macro_use] extern crate lazy_static; +#[macro_use] extern crate log; +#[macro_use] extern crate serde_derive; extern crate cgmath; -#[macro_use] -extern crate clap; extern crate copypasta; extern crate errno; -extern crate font; extern crate fnv; +extern crate font; extern crate glutin; -#[macro_use] -extern crate lazy_static; extern crate libc; extern crate mio; extern crate notify; @@ -41,15 +40,10 @@ extern crate parking_lot; extern crate serde; extern crate serde_json; extern crate serde_yaml; +extern crate unicode_width; extern crate vte; extern crate xdg; -#[macro_use] -extern crate bitflags; - -#[macro_use] -extern crate log; - #[macro_use] pub mod macros; diff --git a/src/term/cell.rs b/src/term/cell.rs index e6b8fc9a..cb966156 100644 --- a/src/term/cell.rs +++ b/src/term/cell.rs @@ -18,11 +18,13 @@ use index::Column; bitflags! { #[derive(Serialize, Deserialize)] pub flags Flags: u32 { - const INVERSE = 0b00000001, - const BOLD = 0b00000010, - const ITALIC = 0b00000100, - const UNDERLINE = 0b00001000, - const WRAPLINE = 0b00010000, + const INVERSE = 0b00000001, + const BOLD = 0b00000010, + const ITALIC = 0b00000100, + const UNDERLINE = 0b00001000, + const WRAPLINE = 0b00010000, + const WIDE_CHAR = 0b00100000, + const WIDE_CHAR_SPACER = 0b01000000, } } diff --git a/src/term/mod.rs b/src/term/mod.rs index ac450bec..08299987 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -20,6 +20,8 @@ use std::cmp::min; use std::io; use std::time::{Duration, Instant}; +use unicode_width::UnicodeWidthChar; + use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset}; use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange, Indexed}; use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive, Side}; @@ -49,7 +51,7 @@ pub struct RenderableCellsIter<'a> { config: &'a Config, colors: &'a color::List, selection: Option>, - cursor_original: Option> + cursor_original: (Option>, Option>), } impl<'a> RenderableCellsIter<'a> { @@ -77,25 +79,52 @@ impl<'a> RenderableCellsIter<'a> { selection: selection, config: config, colors: colors, - cursor_original: None, + cursor_original: (None, None), }.initialize() } fn initialize(mut self) -> Self { if self.cursor_is_visible() { - self.cursor_original = Some(Indexed { + self.cursor_original.0 = Some(Indexed { line: self.cursor.line, column: self.cursor.col, inner: self.grid[self.cursor] }); + let mut spacer = false; + let mut location = *self.cursor; + + if self.grid[self.cursor].flags.contains(cell::WIDE_CHAR) && + self.cursor.col + 1 < self.grid.num_cols() + { + spacer = true; + location.col += 1; + self.cursor_original.1 = Some(Indexed { + line: location.line, + column: location.col, + inner: self.grid[&location] + }); + } if self.config.custom_cursor_colors() { - let cell = &mut self.grid[self.cursor]; - cell.fg = Color::Named(NamedColor::CursorText); - cell.bg = Color::Named(NamedColor::Cursor); + { + let cell = &mut self.grid[self.cursor]; + cell.fg = Color::Named(NamedColor::CursorText); + cell.bg = Color::Named(NamedColor::Cursor); + } + if spacer { + let cell = &mut self.grid[&location]; + cell.fg = Color::Named(NamedColor::CursorText); + cell.bg = Color::Named(NamedColor::Cursor); + } } else { - let cell = &mut self.grid[self.cursor]; - mem::swap(&mut cell.fg, &mut cell.bg); + { + let cell = &mut self.grid[self.cursor]; + mem::swap(&mut cell.fg, &mut cell.bg); + } + if spacer { + let cell = &mut self.grid[&location]; + mem::swap(&mut cell.fg, &mut cell.bg); + } } } self @@ -112,9 +141,14 @@ impl<'a> Drop for RenderableCellsIter<'a> { /// Resets temporary render state on the grid fn drop(&mut self) { if self.cursor_is_visible() { - if let Some(ref original) = self.cursor_original { + if let Some(ref original) = self.cursor_original.0 { self.grid[self.cursor] = original.inner; } + if let Some(ref original) = self.cursor_original.1 { + let mut location = *self.cursor; + location.col += 1; + self.grid[&location] = original.inner; + } } } } @@ -702,7 +736,9 @@ impl Term { None } else { for cell in &line[cols.start..line_end] { - self.push(cell.c); + if !cell.flags.contains(cell::WIDE_CHAR_SPACER) { + self.push(cell.c); + } } Some(cols.start..line_end) @@ -1001,7 +1037,6 @@ impl ansi::Handler for Term { #[inline] fn input(&mut self, c: char) { if self.input_needs_wrap { - if !self.mode.contains(mode::LINE_WRAP) { return; } @@ -1029,9 +1064,31 @@ impl ansi::Handler for Term { } { - let cell = &mut self.grid[&self.cursor.point]; - *cell = self.cursor.template; - cell.c = self.cursor.charsets[self.active_charset].map(c); + // Number of cells the char will occupy + let width = c.width(); + + // Sigh, borrowck making us check the width twice. Hopefully the + // optimizer can fix it. + { + let cell = &mut self.grid[&self.cursor.point]; + *cell = self.cursor.template; + cell.c = self.cursor.charsets[self.active_charset].map(c); + + // Handle wide chars + if let Some(2) = width { + cell.flags.insert(cell::WIDE_CHAR); + } + } + + // Set spacer cell for wide chars. + if let Some(2) = width { + if self.cursor.point.col + 1 < self.grid.num_cols() { + self.cursor.point.col += 1; + let spacer = &mut self.grid[&self.cursor.point]; + *spacer = self.cursor.template; + spacer.flags.insert(cell::WIDE_CHAR_SPACER); + } + } } if (self.cursor.point.col + 1) < self.grid.num_cols() {