diff options
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/cell.rs | 13 | ||||
-rw-r--r-- | src/grid.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/row.rs | 18 | ||||
-rw-r--r-- | src/screen.rs | 52 | ||||
-rw-r--r-- | src/unicode.rs | 14 |
7 files changed, 96 insertions, 7 deletions
@@ -12,4 +12,5 @@ keywords = ["terminal", "vt100"] license = "MIT" [dependencies] +unicode-width = "0.1" vte = "0.3" diff --git a/src/cell.rs b/src/cell.rs index 2dc1ee4..3d2a9fa 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -14,6 +14,15 @@ impl Cell { self.attrs = a; } + pub(crate) fn append(&mut self, c: char) { + self.contents.push(c); + } + + pub(crate) fn reset(&mut self) { + self.contents = String::new(); + self.attrs = crate::attrs::Attrs::default(); + } + pub fn contents(&self) -> &str { &self.contents } @@ -22,6 +31,10 @@ impl Cell { self.contents != "" } + pub fn is_wide(&self) -> bool { + crate::unicode::str_width(&self.contents) > 1 + } + pub fn fgcolor(&self) -> crate::color::Color { self.attrs.fgcolor } diff --git a/src/grid.rs b/src/grid.rs index 7d6d487..8b03dac 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -276,8 +276,8 @@ impl Grid { self.col_clamp(); } - pub fn col_wrap(&mut self) { - if self.pos.col > self.size.cols - 1 { + pub fn col_wrap(&mut self, width: u16) { + if self.pos.col > self.size.cols - width { self.current_row_mut().unwrap().wrap(true); self.pos.col = 0; self.row_inc_scroll(1); @@ -9,6 +9,7 @@ mod color; mod grid; mod row; mod screen; +mod unicode; pub use cell::Cell; pub use color::Color; @@ -40,25 +40,39 @@ impl Row { self.wrapped = wrap; } + pub fn wrapped(&self) -> bool { + self.wrapped + } + pub fn contents(&self, col_start: u16, col_end: u16) -> String { + let mut prev_was_wide = false; // XXX very inefficient let mut max_col = None; for (col, cell) in self.cells.iter().enumerate() { - if cell.has_contents() { + if cell.has_contents() || prev_was_wide { max_col = Some(col); + prev_was_wide = cell.is_wide(); } } + prev_was_wide = false; let mut contents = String::new(); if let Some(max_col) = max_col { for col in col_start..=(col_end.min(max_col as u16)) { - let cell_contents = self.cells[col as usize].contents(); + if prev_was_wide { + prev_was_wide = false; + continue; + } + + let cell = &self.cells[col as usize]; + let cell_contents = cell.contents(); let cell_contents = if cell_contents == "" { " " } else { cell_contents }; contents += cell_contents; + prev_was_wide = cell.is_wide(); } } if !self.wrapped { diff --git a/src/screen.rs b/src/screen.rs index f26d8a7..cf7ab82 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -56,6 +56,10 @@ impl State { } } + fn row(&self, pos: crate::grid::Pos) -> Option<&crate::row::Row> { + self.grid().row(pos) + } + fn cell(&self, pos: crate::grid::Pos) -> Option<&crate::cell::Cell> { self.grid().cell(pos) } @@ -89,11 +93,53 @@ impl State { // control codes fn text(&mut self, c: char) { + let pos = *self.grid().pos(); + if pos.col > 0 { + let prev_cell = self + .cell_mut(crate::grid::Pos { + row: pos.row, + col: pos.col - 1, + }) + .unwrap(); + if prev_cell.is_wide() { + prev_cell.reset(); + } + } + + let width = crate::unicode::char_width(c); let attrs = self.attrs; - self.grid_mut().col_wrap(); + self.grid_mut().col_wrap(width as u16); if let Some(cell) = self.current_cell_mut() { - cell.set(c.to_string(), attrs); - self.grid_mut().col_inc(1); + if width == 0 { + if pos.col > 0 { + let prev_cell = self + .cell_mut(crate::grid::Pos { + row: pos.row, + col: pos.col - 1, + }) + .unwrap(); + prev_cell.append(c); + } else if pos.row > 0 { + let prev_row = self + .row(crate::grid::Pos { + row: pos.row - 1, + col: 0, + }) + .unwrap(); + if prev_row.wrapped() { + let prev_cell = self + .cell_mut(crate::grid::Pos { + row: pos.row - 1, + col: self.grid().size().cols - 1, + }) + .unwrap(); + prev_cell.append(c); + } + } + } else { + cell.set(c.to_string(), attrs); + self.grid_mut().col_inc(width as u16); + } } else { panic!("couldn't find current cell") } diff --git a/src/unicode.rs b/src/unicode.rs new file mode 100644 index 0000000..5cf597e --- /dev/null +++ b/src/unicode.rs @@ -0,0 +1,14 @@ +use unicode_width::UnicodeWidthChar as _; + +// soft hyphen is defined as width 1, but in a terminal setting it should +// always be width 0 +pub fn char_width(c: char) -> usize { + match c { + '\u{00ad}' => 0, + _ => c.width().unwrap_or(0), + } +} + +pub fn str_width(s: &str) -> usize { + s.chars().map(char_width).sum() +} |