rmux-core 0.6.0

Core session, pane, layout, format, hook, and buffer model for the RMUX terminal multiplexer.
Documentation
use super::*;
use crate::input::{CellState, GridAttr};

#[test]
fn render_without_trimming_preserves_explicit_trailing_spaces_but_not_cleared_cells() {
    let mut line = GridLine::new(6);
    let state = CellState::default();
    for (x, ch) in "A  ".chars().enumerate() {
        *line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }

    let mut render_state = GridStringState::default();
    let rendered = line.render_with_options(
        6,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: false,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "A  ");

    let mut render_state = GridStringState::default();
    let trimmed = line.render_with_options(
        6,
        GridRenderOptions {
            trim_spaces: true,
            include_empty_cells: false,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(trimmed, "A");
}

#[test]
fn render_with_tmux_cell_capacity_stops_at_allocation_bucket() {
    let mut line = GridLine::new(20);
    let state = CellState::default();
    for (x, ch) in "abcde".chars().enumerate() {
        *line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }

    let mut render_state = GridStringState::default();
    let rendered = line.render_with_options(
        20,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            use_tmux_cell_capacity: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "abcde     ");

    let mut styled_line = GridLine::new(20);
    let mut styled = CellState::default();
    styled.cell.attr = GridAttr::BRIGHT;
    for (x, ch) in "red".chars().enumerate() {
        *styled_line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &styled, GridCellFlags::default());
    }
    for (x, ch) in "  ".chars().enumerate() {
        *styled_line.cell_mut((x + 3) as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }
    let mut render_state = GridStringState::default();
    let rendered = styled_line.render_with_options(
        20,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            use_tmux_cell_capacity: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "red  ");

    let empty = GridLine::new(20);
    let mut render_state = GridStringState::default();
    let rendered = empty.render_with_options(
        20,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            use_tmux_cell_capacity: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "");
}

#[test]
fn render_with_tmux_cell_capacity_ignores_wide_padding_when_bucketing() {
    let mut line = GridLine::new(12);
    let state = CellState::default();

    *line.cell_mut(0).expect("wide cell exists") =
        GridCell::from_state('🙂', 2, &state, GridCellFlags::default());
    *line.cell_mut(1).expect("padding cell exists") =
        GridCell::from_state(' ', 1, &state, GridCellFlags::PADDING);
    *line.cell_mut(2).expect("narrow cell exists") =
        GridCell::from_state('x', 1, &state, GridCellFlags::default());

    let mut render_state = GridStringState::default();
    let rendered = line.render_with_options(
        12,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            use_tmux_cell_capacity: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );

    assert_eq!(rendered, "🙂x");
}

#[test]
fn compacted_history_line_renders_against_logical_width() {
    let mut line = GridLine::new(12);
    let state = CellState::default();
    for (x, ch) in "abc".chars().enumerate() {
        *line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }

    line.compact_for_history();
    assert_eq!(line.cells().len(), 0);
    assert_eq!(line.plain_text(), Some("abc"));

    let mut render_state = GridStringState::default();
    let rendered = line.render_with_options(
        12,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "abc         ");
}

#[test]
fn compact_plain_line_resets_carried_sgr_state_when_rendering_sequences() {
    let mut styled_line = GridLine::new(8);
    let mut styled = CellState::default();
    styled.cell.attr = GridAttr::BRIGHT;
    for (x, ch) in "red".chars().enumerate() {
        *styled_line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &styled, GridCellFlags::default());
    }
    let mut plain_line = GridLine::new(8);
    assert!(plain_line.write_plain_ascii_run(0, b"ok"));

    let mut render_state = GridStringState::default();
    let _ = styled_line.render_with_options(
        8,
        GridRenderOptions {
            with_sequences: true,
            include_empty_cells: false,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    let rendered = plain_line.render_with_options(
        8,
        GridRenderOptions {
            with_sequences: true,
            include_empty_cells: false,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );

    assert!(
        rendered.starts_with("\x1b[0m"),
        "plain compact line must reset carried SGR state: {rendered:?}"
    );
    assert!(rendered.ends_with("ok"));
}

#[test]
fn compacted_wrapped_history_line_keeps_wrap_without_cell_storage() {
    let mut line = GridLine::new(6);
    let state = CellState::default();
    for (x, ch) in "abcdef".chars().enumerate() {
        *line.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }
    line.set_wrapped(true);

    line.compact_for_history();

    assert_eq!(line.cells().len(), 0);
    assert_eq!(line.plain_text(), Some("abcdef"));
    assert!(line.flags().contains(GridLineFlags::WRAPPED));

    let mut render_state = GridStringState::default();
    let rendered = line.render_with_options(
        6,
        GridRenderOptions {
            trim_spaces: false,
            include_empty_cells: true,
            ..GridRenderOptions::default()
        },
        &mut render_state,
        None,
    );
    assert_eq!(rendered, "abcdef");
}

#[test]
fn capture_join_wrapped_keeps_spaces_at_wrapped_boundaries() {
    let mut grid = Grid::new(TerminalSize { cols: 6, rows: 2 }, 0);
    let state = CellState::default();
    let first = grid.visible_line_mut(0).expect("line exists");
    for (x, ch) in "user ".chars().enumerate() {
        *first.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }
    first.set_wrapped(true);
    let second = grid.visible_line_mut(1).expect("line exists");
    for (x, ch) in "root".chars().enumerate() {
        *second.cell_mut(x as u32).expect("cell exists") =
            GridCell::from_state(ch, 1, &state, GridCellFlags::default());
    }

    let mut render_state = GridStringState::default();
    let mut output = Vec::new();
    for absolute_y in 0..2 {
        let line = grid
            .render_absolute_line(
                absolute_y,
                GridRenderOptions {
                    join_wrapped: true,
                    trim_spaces: false,
                    include_empty_cells: false,
                    ..GridRenderOptions::default()
                },
                &mut render_state,
                None,
            )
            .expect("line renders");
        output.extend_from_slice(line.as_bytes());
        if !grid.absolute_line_wrapped(absolute_y).unwrap_or(false) {
            output.push(b'\n');
        }
    }

    assert_eq!(String::from_utf8(output).expect("utf8"), "user root\n");
}

#[test]
fn clear_visible_to_history_preserves_inner_blank_lines() {
    let mut grid = Grid::new(TerminalSize { cols: 8, rows: 4 }, 10);
    let state = CellState::default();
    for (y, text) in [(0, "AAA"), (2, "BBB")] {
        let line = grid.visible_line_mut(y).expect("line exists");
        for (x, ch) in text.chars().enumerate() {
            *line.cell_mut(x as u32).expect("cell exists") =
                GridCell::from_state(ch, 1, &state, GridCellFlags::default());
        }
    }

    grid.clear_visible_to_history(COLOUR_DEFAULT);

    let mut render_state = GridStringState::default();
    let lines = (0..3)
        .map(|absolute_y| {
            grid.render_absolute_line(
                absolute_y,
                GridRenderOptions::default(),
                &mut render_state,
                None,
            )
            .expect("history line renders")
        })
        .collect::<Vec<_>>();
    assert_eq!(lines, ["AAA", "", "BBB"]);
}