rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_core::{
    style::Style, text_width as tmux_text_width, truncate_to_width as tmux_truncate_to_width,
    Utf8Config,
};

use super::super::{cursor_position_bytes, style_sgr_bytes};

pub(in crate::renderer::status) fn render_status_runs(row: u16, runs: &[StatusRun]) -> Vec<u8> {
    if runs.is_empty() {
        return Vec::new();
    }

    let mut frame = Vec::new();
    let mut last_style: Option<StatusStyle> = None;
    frame.extend_from_slice(b"\x1b[s");
    frame.extend_from_slice(b"\x1b[0m");
    frame.extend_from_slice(cursor_position_bytes(row, 0).as_slice());

    for run in runs {
        if run.text.is_empty() {
            continue;
        }
        if last_style.as_ref() != Some(&run.style) {
            if last_style.is_some() {
                frame.extend_from_slice(b"\x1b[0m");
            }
            frame.extend_from_slice(style_sgr_bytes(&run.style, true).as_slice());
            last_style = Some(run.style.clone());
        }
        frame.extend_from_slice(run.text.as_bytes());
    }

    frame.extend_from_slice(b"\x1b[0m\x1b[u");
    frame
}

#[cfg_attr(not(test), allow(dead_code))]
pub(in crate::renderer::status) fn truncate_status_runs(
    runs: &[StatusRun],
    width: usize,
    utf8_config: &Utf8Config,
) -> Vec<StatusRun> {
    let mut truncated = Vec::new();
    let mut remaining = width;

    for run in runs {
        if remaining == 0 {
            break;
        }
        let text = tmux_truncate_to_width(&run.text, remaining, utf8_config);
        remaining = remaining.saturating_sub(tmux_text_width(&text, utf8_config));
        push_status_run(&mut truncated, text, run.style.clone());
    }

    truncated
}

pub(in crate::renderer::status) fn push_spaces(
    runs: &mut Vec<StatusRun>,
    count: usize,
    style: StatusStyle,
) {
    if count > 0 {
        push_status_run(runs, " ".repeat(count), style);
    }
}

pub(in crate::renderer::status) fn push_status_run(
    runs: &mut Vec<StatusRun>,
    text: String,
    style: StatusStyle,
) {
    let text = sanitize_status_text(text);
    if text.is_empty() {
        return;
    }
    if let Some(last) = runs.last_mut() {
        if last.style == style {
            last.text.push_str(&text);
            return;
        }
    }
    runs.push(StatusRun { text, style });
}

pub(in crate::renderer) fn sanitize_status_text(text: String) -> String {
    text.chars()
        .map(|character| {
            if character.is_control() {
                ' '
            } else {
                character
            }
        })
        .collect()
}

pub(in crate::renderer) fn status_runs_width(
    runs: &[StatusRun],
    utf8_config: &Utf8Config,
) -> usize {
    runs.iter()
        .map(|run| tmux_text_width(&run.text, utf8_config))
        .sum()
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(in crate::renderer) struct StatusRun {
    pub(in crate::renderer) text: String,
    pub(in crate::renderer) style: StatusStyle,
}

pub(in crate::renderer) type StatusStyle = Style;