use super::{tmux_cmd, tmux_silent};
use anyhow::{bail, Result};
use std::path::{Path, PathBuf};
use std::process::Stdio;
pub fn is_available() -> bool {
tmux_cmd(&["-V"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn is_inside_tmux() -> bool {
std::env::var("TMUX").is_ok()
}
pub fn list_sessions_with_paths() -> Vec<(String, PathBuf)> {
let Ok(output) = tmux_cmd(&["list-sessions", "-F", "#{session_name}:#{session_path}"]).output()
else {
return vec![];
};
String::from_utf8_lossy(&output.stdout)
.lines()
.filter_map(|line| {
let mut parts = line.splitn(2, ':');
let name = parts.next()?.trim().to_string();
let path = parts.next()?.trim().to_string();
if name.is_empty() || path.is_empty() {
return None;
}
Some((name, PathBuf::from(path)))
})
.collect()
}
pub fn session_exists(name: &str) -> bool {
tmux_silent(&["has-session", "-t", name])
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn create_session(name: &str, start_dir: &Path) -> Result<()> {
let status = tmux_silent(&[
"new-session",
"-d",
"-s",
name,
"-c",
&start_dir.to_string_lossy(),
])
.status()?;
if !status.success() {
bail!("tmux new-session failed for {}", name);
}
Ok(())
}
pub fn kill_session(name: &str) -> Result<()> {
tmux_silent(&["kill-session", "-t", name]).status()?;
Ok(())
}
pub fn rename_session(old_name: &str, new_name: &str) -> Result<()> {
let status = tmux_silent(&["rename-session", "-t", old_name, new_name]).status()?;
if !status.success() {
bail!("tmux rename-session failed");
}
Ok(())
}
pub fn attach_session_cmd(name: &str) -> AttachCommand {
if is_inside_tmux() {
AttachCommand::SwitchClient(name.to_string())
} else {
AttachCommand::Attach(name.to_string())
}
}
pub enum AttachCommand {
SwitchClient(String),
Attach(String),
}
pub fn user_has_tmux_config() -> bool {
let xdg = std::env::var("XDG_CONFIG_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| dirs::home_dir().unwrap_or_default().join(".config"));
dirs::home_dir()
.map(|h| h.join(".tmux.conf").exists())
.unwrap_or(false)
|| xdg.join("tmux/tmux.conf").exists()
}
pub fn apply_session_defaults(session: &str) {
let _ = tmux_silent(&["set-option", "-t", session, "mouse", "on"]).status();
if !user_has_tmux_config() {
let _ = tmux_silent(&["set-option", "-t", session, "prefix", "C-a"]).status();
let _ = tmux_silent(&["bind-key", "-T", "prefix", "a", "send-prefix"]).status();
}
}
pub fn switch_client(name: &str) -> Result<()> {
let status = tmux_silent(&["switch-client", "-t", name]).status()?;
if !status.success() {
bail!("tmux switch-client failed for {}", name);
}
Ok(())
}
pub fn attach_foreground(name: &str) -> Result<()> {
tmux_cmd(&["attach-session", "-t", name]).status()?;
Ok(())
}
pub fn server_pid() -> Option<u32> {
let output = tmux_cmd(&["display-message", "-p", "#{pid}"]).output().ok()?;
String::from_utf8_lossy(&output.stdout).trim().parse().ok()
}
pub const OPT_MUTED: &str = "@wsx-muted";
pub const OPT_SUPPRESSED: &str = "@wsx-suppressed";
pub fn set_session_opt(session: &str, key: &str, value: &str) {
let _ = tmux_silent(&["set-option", "-t", session, key, value]).status();
}
pub fn send_keys(session: &str, keys: &str) -> Result<()> {
tmux_silent(&["send-keys", "-t", session, keys, "Enter"]).status()?;
Ok(())
}
pub fn send_keys_raw(session: &str, keys: &str) -> Result<()> {
tmux_silent(&["send-keys", "-t", session, keys]).status()?;
Ok(())
}
pub fn send_ctrl_c(session: &str) -> Result<()> {
tmux_silent(&["send-keys", "-t", session, "C-c"]).status()?;
Ok(())
}
pub fn unique_session_name(base: &str) -> String {
if !session_exists(base) {
return base.to_string();
}
let mut n = 2;
loop {
let candidate = format!("{}_{}", base, n);
if !session_exists(&candidate) {
return candidate;
}
n += 1;
}
}