use crate::types::AppState;
pub enum WarmPaneSync {
Noop,
Patch(WarmPanePatch),
Respawn(&'static str),
}
#[derive(Clone)]
pub enum WarmPanePatch {
HistoryLimit(usize),
AllowAlternateScreen(bool),
}
pub fn for_option_change(name: &str, app: &AppState) -> WarmPaneSync {
match name {
"history-limit" => WarmPaneSync::Patch(WarmPanePatch::HistoryLimit(app.history_limit)),
"alternate-screen" => {
WarmPaneSync::Patch(WarmPanePatch::AllowAlternateScreen(app.allow_alternate_screen))
}
"default-shell" => WarmPaneSync::Respawn("default-shell changed"),
"allow-predictions" => WarmPaneSync::Respawn("allow-predictions changed"),
"default-terminal" => WarmPaneSync::Respawn("default-terminal changed"),
"claude-code-fix-tty" | "claude-code-force-interactive" => {
WarmPaneSync::Respawn("claude-code option changed")
}
_ => WarmPaneSync::Noop,
}
}
pub fn for_env_change() -> WarmPaneSync {
WarmPaneSync::Respawn("environment changed")
}
pub fn for_resize(app: &AppState, new_rows: u16, new_cols: u16) -> WarmPaneSync {
match app.warm_pane.as_ref() {
Some(wp) if wp.rows == new_rows && wp.cols == new_cols => WarmPaneSync::Noop,
_ => WarmPaneSync::Respawn("client resized"),
}
}
pub fn for_post_config(app: &AppState) -> WarmPaneSync {
if !app.warm_enabled {
return WarmPaneSync::Respawn("warm panes disabled by config");
}
if !app.default_shell.is_empty() {
return WarmPaneSync::Respawn("post-config: custom default-shell");
}
let needs_env = app.environment.iter().any(|(k, _)| {
!k.starts_with("PSMUX_TARGET_SESSION") && k != "TMUX" && k != "TMUX_PANE"
});
if needs_env {
return WarmPaneSync::Respawn("post-config: env vars set");
}
if app.allow_predictions {
return WarmPaneSync::Respawn("post-config: predictions enabled");
}
if app.history_limit != 2000 {
return WarmPaneSync::Patch(WarmPanePatch::HistoryLimit(app.history_limit));
}
if !app.allow_alternate_screen {
return WarmPaneSync::Patch(WarmPanePatch::AllowAlternateScreen(false));
}
WarmPaneSync::Noop
}
pub fn apply(
app: &mut AppState,
pty_system: &dyn portable_pty::PtySystem,
sync: WarmPaneSync,
) {
match sync {
WarmPaneSync::Noop => {}
WarmPaneSync::Patch(patch) => apply_patch(app, patch),
WarmPaneSync::Respawn(_reason) => respawn(app, pty_system),
}
}
fn apply_patch(app: &mut AppState, patch: WarmPanePatch) {
apply_patch_to_existing_panes(app, &patch);
let wp = match app.warm_pane.as_ref() {
Some(wp) => wp,
None => return,
};
match patch {
WarmPanePatch::HistoryLimit(n) => {
if let Ok(mut parser) = wp.term.lock() {
if parser.screen().scrollback_len() != n {
parser.screen_mut().set_scrollback_len(n);
}
}
}
WarmPanePatch::AllowAlternateScreen(allowed) => {
if let Ok(mut parser) = wp.term.lock() {
if parser.screen().allow_alternate_screen() != allowed {
parser.screen_mut().set_allow_alternate_screen(allowed);
}
}
}
}
}
fn apply_patch_to_existing_panes(app: &mut AppState, patch: &WarmPanePatch) {
use crate::types::Node;
fn walk(node: &mut Node, patch: &WarmPanePatch) {
match node {
Node::Leaf(p) => {
if let Ok(mut parser) = p.term.lock() {
match patch {
WarmPanePatch::HistoryLimit(n) => {
if parser.screen().scrollback_len() != *n {
parser.screen_mut().set_scrollback_len(*n);
}
}
WarmPanePatch::AllowAlternateScreen(allowed) => {
if parser.screen().allow_alternate_screen() != *allowed {
parser.screen_mut().set_allow_alternate_screen(*allowed);
}
}
}
}
}
Node::Split { children, .. } => {
for child in children.iter_mut() {
walk(child, patch);
}
}
}
}
for win in app.windows.iter_mut() {
walk(&mut win.root, patch);
}
}
fn respawn(app: &mut AppState, pty_system: &dyn portable_pty::PtySystem) {
if let Some(mut old) = app.warm_pane.take() {
old.child.kill().ok();
}
if !app.warm_enabled {
return;
}
match crate::pane::spawn_warm_pane(pty_system, app) {
Ok(wp) => {
app.warm_pane = Some(wp);
}
Err(_) => {
}
}
}
pub fn reconcile_consumed_parser(parser: &mut vt100::Parser, app: &AppState) {
let screen = parser.screen();
let need_history = screen.scrollback_len() != app.history_limit;
let need_alt = screen.allow_alternate_screen() != app.allow_alternate_screen;
if need_history || need_alt {
let s = parser.screen_mut();
if need_history {
s.set_scrollback_len(app.history_limit);
}
if need_alt {
s.set_allow_alternate_screen(app.allow_alternate_screen);
}
}
}
#[cfg(test)]
#[path = "../tests-rs/test_warm_pane_sync.rs"]
mod test_warm_pane_sync;