shellshot 0.5.0

Transform your command-line output into clean, shareable images with a single command.
Documentation
use std::io::{self};

use termwiz::{
    escape::{CSI, csi::Edit},
    surface::{Change, Position, SEQ_ZERO, SequenceNo, Surface},
};

mod cursor;
mod sgr;

use crate::terminal_builder::action::csi::{cursor::process_cursor, sgr::process_sgr};

pub fn process_csi(surface: &mut Surface, writer: &mut dyn io::Write, csi: &CSI) -> SequenceNo {
    match csi {
        CSI::Sgr(sgr) => process_sgr(surface, sgr),
        CSI::Cursor(cursor) => process_cursor(surface, writer, cursor),
        CSI::Edit(edit) => process_edit(surface, edit),
        CSI::Mode(_)
        | CSI::Device(_)
        | CSI::Mouse(_)
        | CSI::Window(_)
        | CSI::Keyboard(_)
        | CSI::SelectCharacterPath(_, _)
        | CSI::Unspecified(_) => SEQ_ZERO,
    }
}

fn process_edit(surface: &mut Surface, edit: &Edit) -> SequenceNo {
    match edit {
        Edit::EraseCharacter(n) => {
            let (x, y) = surface.cursor_position();
            surface.add_change(Change::Text(" ".repeat(*n as usize)));
            surface.add_change(Change::CursorPosition {
                x: Position::Absolute(x),
                y: Position::Absolute(y),
            })
        }
        Edit::EraseInLine(_)
        | Edit::InsertCharacter(_)
        | Edit::InsertLine(_)
        | Edit::ScrollDown(_)
        | Edit::ScrollUp(_)
        | Edit::EraseInDisplay(_)
        | Edit::Repeat(_)
        | Edit::DeleteCharacter(_)
        | Edit::DeleteLine(_) => SEQ_ZERO,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use termwiz::{
        cell::Intensity,
        escape::{
            CSI,
            csi::{Cursor as CsiCursor, Sgr},
        },
        surface::Surface,
    };

    fn make_surface() -> Surface {
        Surface::new(10, 3)
    }

    #[test]
    fn csi_sgr_applies_intensity() {
        let mut s = make_surface();
        let csi = CSI::Sgr(Sgr::Intensity(Intensity::Bold));
        process_csi(&mut s, &mut std::io::sink(), &csi);
        s.add_change("A");
        let screen = s.screen_cells();
        let cell = &screen[0][0];
        assert_eq!(cell.attrs().intensity(), Intensity::Bold);
    }

    #[test]
    fn csi_cursor_moves_right() {
        let mut s = make_surface();
        s.add_change("X");
        let csi = CSI::Cursor(CsiCursor::Right(3));
        process_csi(&mut s, &mut std::io::sink(), &csi);
        let (x, y) = s.cursor_position();
        assert_eq!(x, 4);
        assert_eq!(y, 0);
    }

    #[test]
    fn csi_edit_erase_character() {
        let mut s = make_surface();
        s.add_change("ABCDE");
        let csi = CSI::Edit(Edit::EraseCharacter(3));
        process_csi(&mut s, &mut std::io::sink(), &csi);
        let content = s.screen_chars_to_string();
        println!("{content:?}");
        assert!(content.starts_with("ABCDE"));
    }
}