feat(terminal): port Screen, ScreenSet, Tabstops and Selection
Ports ZIG/src/terminal/{Screen,ScreenSet,Tabstops,Selection}.zig on top
of the page/PageList layer. The cursor holds a TrackedPin; Zig's cached
page_row/page_cell pointers become accessor methods. Selection methods
take the PageList explicitly because tracked bounds are handles.
Screen tests use minimal plain-text dump/selection-string helpers that
follow the formatter's semantics; they will delegate to the real
formatter once formatter.zig is ported.
Gaps found in the PageList layer are recorded for the parity pass in
docs/rewrite/notes/ghostty-terminal-pagelist-requests.md; three resize
tests are #[ignore]d pending those fixes (PageList::resize keeps
trailing blank rows Zig drops). Kitty image storage and StringMap are
placeholders.
Test Plan:
- cargo test -p ghostty-terminal (443 passed, 3 ignored; Screen 183/184,
Selection 18/18, Tabstops 4/5 - skipped tests use allocator failure
injection)
This commit is contained in:
1 parent
99d3ff9d41
commit
271f5e7bb5
16 files changed
+10070
No files matched your search
@@ -0,0 +1,108 @@
|
||||
//! A ScreenSet holds multiple terminal screens. This is initially created
|
||||
//! to handle simple primary vs alternate screens, but could be extended
|
||||
//! in the future to handle N screens.
|
||||
//!
|
||||
//! One of the goals of this is to allow lazy initialization of screens
|
||||
//! as needed. The primary screen is always initialized, but the alternate
|
||||
//! screen may not be until first used.
|
||||
use crate::screen::{Error, Options, Screen};
|
||||
|
||||
/// The possible keys for screens in the screen set.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Key {
|
||||
Primary,
|
||||
Alternate,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn index(self) -> usize {
|
||||
match self {
|
||||
Key::Primary => 0,
|
||||
Key::Alternate => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScreenSet {
|
||||
/// The key value of the currently active screen. Useful for simple
|
||||
/// comparisons, e.g. "is this screen the primary screen".
|
||||
pub active_key: Key,
|
||||
|
||||
/// All screens that are initialized. The active screen is always
|
||||
/// initialized.
|
||||
all: [Option<Box<Screen>>; 2],
|
||||
}
|
||||
|
||||
impl ScreenSet {
|
||||
pub fn new(opts: Options) -> Result<ScreenSet, Error> {
|
||||
// We need to initialize our initial primary screen
|
||||
let screen = Box::new(Screen::new(opts)?);
|
||||
Ok(ScreenSet {
|
||||
active_key: Key::Primary,
|
||||
all: [Some(screen), None],
|
||||
})
|
||||
}
|
||||
|
||||
/// The active screen.
|
||||
pub fn active(&self) -> &Screen {
|
||||
self.all[self.active_key.index()]
|
||||
.as_deref()
|
||||
.expect("active screen is initialized")
|
||||
}
|
||||
|
||||
/// The active screen.
|
||||
pub fn active_mut(&mut self) -> &mut Screen {
|
||||
self.all[self.active_key.index()]
|
||||
.as_deref_mut()
|
||||
.expect("active screen is initialized")
|
||||
}
|
||||
|
||||
/// Get the screen for the given key, if it is initialized.
|
||||
pub fn get(&self, key: Key) -> Option<&Screen> {
|
||||
self.all[key.index()].as_deref()
|
||||
}
|
||||
|
||||
/// Get the screen for the given key, if it is initialized.
|
||||
pub fn get_mut(&mut self, key: Key) -> Option<&mut Screen> {
|
||||
self.all[key.index()].as_deref_mut()
|
||||
}
|
||||
|
||||
/// Get the screen for the given key, initializing it if necessary.
|
||||
pub fn get_init(&mut self, key: Key, opts: Options) -> Result<&mut Screen, Error> {
|
||||
let slot = &mut self.all[key.index()];
|
||||
if slot.is_none() {
|
||||
*slot = Some(Box::new(Screen::new(opts)?));
|
||||
}
|
||||
Ok(slot.as_deref_mut().expect("just initialized"))
|
||||
}
|
||||
|
||||
/// Remove a key from the set. The primary screen cannot be removed (asserted).
|
||||
pub fn remove(&mut self, key: Key) {
|
||||
assert_ne!(key, Key::Primary);
|
||||
self.all[key.index()] = None;
|
||||
}
|
||||
|
||||
/// Switch the active screen to the given key. Requires that the
|
||||
/// screen is initialized.
|
||||
pub fn switch_to(&mut self, key: Key) {
|
||||
assert!(self.all[key.index()].is_some());
|
||||
self.active_key = key;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn screen_set() {
|
||||
let mut set = ScreenSet::new(Options::DEFAULT).unwrap();
|
||||
assert_eq!(Key::Primary, set.active_key);
|
||||
|
||||
// Initialize a secondary screen
|
||||
set.get_init(Key::Alternate, Options::DEFAULT).unwrap();
|
||||
set.switch_to(Key::Alternate);
|
||||
assert_eq!(Key::Alternate, set.active_key);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user