From 0b5aa7dde77bf4a537df8ec603f0fd9453dc3cd8 Mon Sep 17 00:00:00 2001 From: Quan Tong Date: Sun, 27 Aug 2023 17:12:25 +0700 Subject: [PATCH] add zoom-pane subcommand --- mux/src/tab.rs | 8 ++ wezterm-mux-server-impl/src/sessionhandler.rs | 9 +- wezterm/src/cli/mod.rs | 6 + wezterm/src/cli/zoom_pane.rs | 109 ++++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 wezterm/src/cli/zoom_pane.rs diff --git a/mux/src/tab.rs b/mux/src/tab.rs index 1b46ff170bb..f7c178b64ae 100644 --- a/mux/src/tab.rs +++ b/mux/src/tab.rs @@ -745,6 +745,10 @@ impl Tab { .lock() .split_and_insert(pane_index, request, pane) } + + pub fn get_zoomed_pane(&self) -> Option> { + self.inner.lock().get_zoomed_pane() + } } impl TabInner { @@ -2051,6 +2055,10 @@ impl TabInner { pane_index }) } + + fn get_zoomed_pane(&self) -> Option> { + self.zoomed.clone() + } } /// This type is used directly by the codec, take care to bump diff --git a/wezterm-mux-server-impl/src/sessionhandler.rs b/wezterm-mux-server-impl/src/sessionhandler.rs index 31df12dad8c..977c6a7b7fa 100644 --- a/wezterm-mux-server-impl/src/sessionhandler.rs +++ b/wezterm-mux-server-impl/src/sessionhandler.rs @@ -532,8 +532,11 @@ impl SessionHandler { let tab = mux .get_tab(containing_tab_id) .ok_or_else(|| anyhow!("no such tab {}", containing_tab_id))?; - tab.set_active_pane(&pane); - tab.set_zoomed(zoomed); + tab.set_zoomed(false); + if zoomed { + tab.set_active_pane(&pane); + tab.set_zoomed(zoomed); + } Ok(Pdu::UnitResponse(UnitResponse {})) }, send_response, @@ -1021,7 +1024,7 @@ async fn split_pane(split: SplitPane, client_id: Option>) -> anyho Ok::(Pdu::SpawnResponse(SpawnResponse { pane_id: pane.pane_id(), - tab_id: tab_id, + tab_id, window_id, size, })) diff --git a/wezterm/src/cli/mod.rs b/wezterm/src/cli/mod.rs index 0dbd5bd09cf..95e90bf89cb 100644 --- a/wezterm/src/cli/mod.rs +++ b/wezterm/src/cli/mod.rs @@ -22,6 +22,7 @@ mod set_window_title; mod spawn_command; mod split_pane; mod tls_creds; +mod zoom_pane; #[derive(Debug, Parser, Clone, Copy)] enum CliOutputFormatKind { @@ -159,6 +160,10 @@ Outputs the pane-id for the newly created pane on success" /// Rename a workspace #[command(name = "rename-workspace", rename_all = "kebab")] RenameWorkspace(rename_workspace::RenameWorkspace), + + /// Zoom, unzoom, or toggle zoom state + #[command(name = "zoom-pane", rename_all = "kebab")] + ZoomPane(zoom_pane::ZoomPane), } async fn run_cli_async(opts: &crate::Opt, cli: CliCommand) -> anyhow::Result<()> { @@ -194,6 +199,7 @@ async fn run_cli_async(opts: &crate::Opt, cli: CliCommand) -> anyhow::Result<()> CliSubCommand::SetTabTitle(cmd) => cmd.run(client).await, CliSubCommand::SetWindowTitle(cmd) => cmd.run(client).await, CliSubCommand::RenameWorkspace(cmd) => cmd.run(client).await, + CliSubCommand::ZoomPane(cmd) => cmd.run(client).await, } } diff --git a/wezterm/src/cli/zoom_pane.rs b/wezterm/src/cli/zoom_pane.rs new file mode 100644 index 00000000000..f1ce57f75a5 --- /dev/null +++ b/wezterm/src/cli/zoom_pane.rs @@ -0,0 +1,109 @@ +use crate::cli::resolve_pane_id; +use anyhow::{anyhow, Result}; +use clap::Parser; +use codec::SetPaneZoomed; +use mux::pane::PaneId; +use std::collections::HashMap; +use wezterm_client::client::Client; + +#[derive(Debug, Parser, Clone)] +pub struct ZoomPane { + /// Specify the target pane. + /// The default is to use the current pane based on the + /// environment variable WEZTERM_PANE. + #[arg(long)] + pane_id: Option, + + /// Zooms the pane if it wasn't already zoomed + #[arg(long, default_value = "true")] + zoom: bool, + + /// Unzooms the pane if it was zoomed + #[arg(long)] + unzoom: bool, + + /// Toggles the zoom state of the pane + #[arg(long)] + toggle: bool, +} + +impl ZoomPane { + pub async fn run(&self, client: Client) -> Result<()> { + let panes = client.list_panes().await?; + + let mut pane_id_to_tab_id = HashMap::new(); + let mut tab_id_to_active_zoomed_pane_id = HashMap::new(); + + for tabroot in panes.tabs { + let mut cursor = tabroot.into_tree().cursor(); + + loop { + if let Some(entry) = cursor.leaf_mut() { + pane_id_to_tab_id.insert(entry.pane_id, entry.tab_id); + if entry.is_active_pane && entry.is_zoomed_pane { + tab_id_to_active_zoomed_pane_id.insert(entry.tab_id, entry.pane_id); + } + } + match cursor.preorder_next() { + Ok(c) => cursor = c, + Err(_) => break, + } + } + } + + let pane_id = resolve_pane_id(&client, self.pane_id).await?; + let containing_tab_id = pane_id_to_tab_id + .get(&pane_id) + .copied() + .ok_or_else(|| anyhow!("unable to resolve current tab"))?; + + if self.zoom { + client + .set_zoomed(SetPaneZoomed { + containing_tab_id, + pane_id, + zoomed: true, + }) + .await?; + } + + if self.unzoom { + client + .set_zoomed(SetPaneZoomed { + containing_tab_id, + pane_id, + zoomed: false, + }) + .await?; + } + + if self.toggle { + if tab_id_to_active_zoomed_pane_id.contains_key(&containing_tab_id) { + let target_pane = tab_id_to_active_zoomed_pane_id + .get(&containing_tab_id) + .copied() + .ok_or_else(|| { + anyhow!("could not determine which pane should be active for tab {containing_tab_id}") + })?; + if target_pane == pane_id { + client + .set_zoomed(SetPaneZoomed { + containing_tab_id, + pane_id, + zoomed: false, + }) + .await?; + } + } else { + client + .set_zoomed(SetPaneZoomed { + containing_tab_id, + pane_id, + zoomed: true, + }) + .await?; + } + } + Ok(()) + } +}