feat(core): add terminal grid model
test / workspace (push) Successful in 11m53s

This commit is contained in:
Tom You
2026-07-07 00:03:40 -05:00
parent 7bd60ebce0
commit ad50784919
2 changed files with 110 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@
pub mod config;
pub mod feature;
pub mod terminal_model;
pub mod workspace;
pub use config::{AppConfig, ShortcutBinding};
+109
View File
@@ -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"));
}
}