rmux-server 0.7.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
//! Automatic window-name refresh and synchronization.

use rmux_proto::{PaneTarget, SessionName, Target, WindowTarget};

use super::super::{scripting_support::format_context_for_target, RequestHandler};
use crate::format_runtime::render_automatic_window_name;
use crate::pane_terminals::HandlerState;

impl RequestHandler {
    pub(in crate::handler) async fn refresh_automatic_window_name_for_pane_target(
        &self,
        target: &PaneTarget,
    ) -> bool {
        self.refresh_automatic_window_name_for_window_target(&WindowTarget::with_window(
            target.session_name().clone(),
            target.window_index(),
        ))
        .await
    }

    pub(in crate::handler) async fn sync_automatic_window_name_for_pane_target(
        &self,
        target: &PaneTarget,
    ) -> bool {
        !self
            .sync_automatic_window_name_for_window_target(&WindowTarget::with_window(
                target.session_name().clone(),
                target.window_index(),
            ))
            .await
            .is_empty()
    }

    pub(in crate::handler) async fn refresh_automatic_window_name_for_window_target(
        &self,
        target: &WindowTarget,
    ) -> bool {
        let sessions_to_refresh = self
            .sync_automatic_window_name_for_window_target(target)
            .await;
        for session_name in &sessions_to_refresh {
            self.refresh_attached_session(session_name).await;
        }

        !sessions_to_refresh.is_empty()
    }

    async fn sync_automatic_window_name_for_window_target(
        &self,
        target: &WindowTarget,
    ) -> Vec<SessionName> {
        let rendered_window_name = {
            let state = self.state.lock().await;
            format_context_for_target(&state, &Target::Window(target.clone()), 0)
                .ok()
                .and_then(|runtime| render_automatic_window_name(&runtime))
        };
        let fallback_window_name = if rendered_window_name.is_none() {
            let state = self.state.lock().await;
            mode_marker_fallback_window_name(&state, target)
        } else {
            None
        };
        let Some(window_name) = rendered_window_name
            .as_deref()
            .or(fallback_window_name.as_deref())
        else {
            return Vec::new();
        };

        let sessions_to_refresh = {
            let mut state = self.state.lock().await;
            let tracked =
                state.tracks_auto_named_window(target.session_name(), target.window_index());
            let should_update = {
                let Some(session) = state.sessions.session(target.session_name()) else {
                    return Vec::new();
                };
                match session.window_at(target.window_index()) {
                    Some(window) => {
                        !window_name.is_empty()
                            && window.name() != Some(window_name)
                            && crate::automatic_rename::window_allows_automatic_rename(
                                &state.options,
                                target.session_name(),
                                target.window_index(),
                                window,
                                tracked,
                            )
                    }
                    None => return Vec::new(),
                }
            };
            if !should_update {
                Vec::new()
            } else {
                state
                    .sessions
                    .session_mut(target.session_name())
                    .expect("existing session must accept automatic rename update")
                    .window_at_mut(target.window_index())
                    .expect("existing window must accept automatic rename update")
                    .set_automatic_name(window_name.to_owned());
                state.mark_auto_named_window(target.session_name(), target.window_index());
                let _ = state.synchronize_linked_window_from_slot(
                    target.session_name(),
                    target.window_index(),
                );
                state
                    .synchronize_session_group_from(target.session_name())
                    .unwrap_or_else(|_| vec![target.session_name().clone()])
            }
        };
        sessions_to_refresh
    }
}

fn mode_marker_fallback_window_name(state: &HandlerState, target: &WindowTarget) -> Option<String> {
    let session = state.sessions.session(target.session_name())?;
    let window = session.window_at(target.window_index())?;
    if window.name() != Some("[tmux]")
        || !state.tracks_auto_named_window(target.session_name(), target.window_index())
    {
        return None;
    }

    let pane = window.active_pane()?;
    state
        .pane_runtime_window_name_in_window(
            target.session_name(),
            target.window_index(),
            pane.index(),
        )
        .ok()
        .flatten()
        .or_else(|| {
            state
                .pane_profile_in_window(target.session_name(), target.window_index(), pane.index())
                .ok()
                .and_then(|profile| {
                    profile
                        .shell()
                        .file_name()
                        .and_then(|name| name.to_str())
                        .map(str::to_owned)
                })
        })
        .filter(|name| !name.is_empty() && name != "[tmux]")
}