rmux-server 0.7.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_core::formats::{
    DEFAULT_LIST_PANES_ALL_FORMAT, DEFAULT_LIST_PANES_SESSION_FORMAT,
    DEFAULT_LIST_PANES_WINDOW_FORMAT,
};
use rmux_core::{Pane, Session};
use rmux_proto::OptionName;

use crate::pane_terminals::HandlerState;
use crate::pane_visible_geometry::visible_pane_content_geometry;

#[derive(Clone, Copy)]
pub(super) enum DefaultListPanesFormat {
    Window,
    Session,
    All,
}

impl DefaultListPanesFormat {
    pub(super) fn from_format(format: &str) -> Option<Self> {
        match format {
            DEFAULT_LIST_PANES_WINDOW_FORMAT => Some(Self::Window),
            DEFAULT_LIST_PANES_SESSION_FORMAT => Some(Self::Session),
            DEFAULT_LIST_PANES_ALL_FORMAT => Some(Self::All),
            _ => None,
        }
    }
}

pub(super) fn push_default_list_panes_line(
    stdout: &mut Vec<u8>,
    context: DefaultListPanesLineContext<'_>,
) -> bool {
    use std::fmt::Write as _;

    let DefaultListPanesLineContext {
        format,
        state,
        session,
        attached_count,
        window_index,
        pane,
        pane_active,
    } = context;

    let Some(history_stats) = state.pane_history_size_stats(session.name(), pane.id()) else {
        return false;
    };
    let Some(history_bytes) = state.pane_history_bytes(session.name(), pane.id()) else {
        return false;
    };

    let geometry = list_panes_default_geometry(state, session, attached_count, window_index, pane);
    let mut line = String::new();
    match format {
        DefaultListPanesFormat::Window => {
            let _ = write!(&mut line, "{}: ", pane.index());
        }
        DefaultListPanesFormat::Session => {
            let _ = write!(&mut line, "{}.{}: ", window_index, pane.index());
        }
        DefaultListPanesFormat::All => {
            let _ = write!(
                &mut line,
                "{}:{}.{}: ",
                session.name(),
                window_index,
                pane.index()
            );
        }
    }
    let _ = write!(
        &mut line,
        "[{}x{}] [history {}/{}, {} bytes] {}",
        geometry.cols(),
        geometry.rows(),
        history_stats.size,
        history_stats.limit,
        history_bytes,
        pane.id()
    );
    if pane_active {
        line.push_str(" (active)");
    }
    if state.pane_is_dead(session.name(), pane.id()) {
        line.push_str(" (dead)");
    }
    stdout.extend_from_slice(line.as_bytes());
    true
}

pub(super) struct DefaultListPanesLineContext<'a> {
    pub(super) format: DefaultListPanesFormat,
    pub(super) state: &'a HandlerState,
    pub(super) session: &'a Session,
    pub(super) attached_count: usize,
    pub(super) window_index: u32,
    pub(super) pane: &'a Pane,
    pub(super) pane_active: bool,
}

fn list_panes_default_geometry(
    state: &HandlerState,
    session: &Session,
    attached_count: usize,
    window_index: u32,
    pane: &Pane,
) -> rmux_core::PaneGeometry {
    let geometry = pane.geometry();
    if attached_count == 0 {
        return geometry;
    }

    let size = session.window().size();
    if size.cols == 0 || size.rows == 0 {
        return geometry;
    }

    let content_rows = if matches!(
        state
            .options
            .resolve(Some(session.name()), OptionName::Status),
        Some("off")
    ) {
        size.rows
    } else {
        size.rows.saturating_sub(1)
    };
    visible_pane_content_geometry(
        &state.options,
        session.name(),
        window_index,
        geometry,
        content_rows,
    )
}