This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
pub mod config;
|
||||
pub mod feature;
|
||||
pub mod terminal_model;
|
||||
pub mod workspace;
|
||||
|
||||
pub use config::{AppConfig, ShortcutBinding};
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Cell {
|
||||
pub ch: char,
|
||||
}
|
||||
|
||||
impl Default for Cell {
|
||||
fn default() -> Self {
|
||||
Self { ch: ' ' }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TerminalGrid {
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
cursor_col: u16,
|
||||
cursor_row: u16,
|
||||
cells: Vec<Cell>,
|
||||
}
|
||||
|
||||
impl TerminalGrid {
|
||||
pub fn new(cols: u16, rows: u16) -> Self {
|
||||
let size = usize::from(cols) * usize::from(rows);
|
||||
Self {
|
||||
cols,
|
||||
rows,
|
||||
cursor_col: 0,
|
||||
cursor_row: 0,
|
||||
cells: vec![Cell::default(); size],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_utf8(&mut self, bytes: &[u8]) {
|
||||
let text = String::from_utf8_lossy(bytes);
|
||||
for ch in text.chars() {
|
||||
match ch {
|
||||
'\r' => self.cursor_col = 0,
|
||||
'\n' => self.newline(),
|
||||
_ => self.put_char(ch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visible_text(&self) -> String {
|
||||
self.line_text(0)
|
||||
}
|
||||
|
||||
pub fn line_text(&self, row: u16) -> String {
|
||||
if row >= self.rows {
|
||||
return String::new();
|
||||
}
|
||||
let start = usize::from(row) * usize::from(self.cols);
|
||||
let end = start + usize::from(self.cols);
|
||||
self.cells[start..end].iter().map(|cell| cell.ch).collect()
|
||||
}
|
||||
|
||||
fn put_char(&mut self, ch: char) {
|
||||
if self.cursor_row >= self.rows || self.cursor_col >= self.cols {
|
||||
return;
|
||||
}
|
||||
let index =
|
||||
usize::from(self.cursor_row) * usize::from(self.cols) + usize::from(self.cursor_col);
|
||||
self.cells[index].ch = ch;
|
||||
self.cursor_col += 1;
|
||||
if self.cursor_col >= self.cols {
|
||||
self.newline();
|
||||
}
|
||||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
self.cursor_col = 0;
|
||||
if self.cursor_row + 1 < self.rows {
|
||||
self.cursor_row += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn writes_ascii_text_to_grid() {
|
||||
let mut grid = TerminalGrid::new(8, 2);
|
||||
|
||||
grid.write_utf8(b"hello");
|
||||
|
||||
assert_eq!(grid.visible_text(), "hello ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handles_newline_and_carriage_return() {
|
||||
let mut grid = TerminalGrid::new(6, 2);
|
||||
|
||||
grid.write_utf8(b"abc\rZ\ndef");
|
||||
|
||||
assert_eq!(grid.line_text(0), "Zbc ");
|
||||
assert_eq!(grid.line_text(1), "def ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tolerates_utf8_double_width_input() {
|
||||
let mut grid = TerminalGrid::new(6, 1);
|
||||
|
||||
grid.write_utf8("한A".as_bytes());
|
||||
|
||||
assert!(grid.line_text(0).starts_with("한A"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user