use std::collections::HashMap;
use crate::layout::{Direction, Layout, NavDir};
use crate::pane::Pane;
use crate::settings::Settings;
use super::{RenderUpdate, TabAction};
#[allow(clippy::too_many_arguments)]
pub(super) fn execute_command(
cmd: &str,
layout: &mut Layout,
panes: &mut HashMap<usize, Pane>,
active: &mut usize,
settings: &mut Settings,
update: &mut RenderUpdate,
default_shell: &str,
tw: u16,
th: u16,
scrollback: usize,
zoomed_pane: &mut Option<usize>,
broadcast: &mut bool,
tab_action: &mut TabAction,
) -> Result<Option<String>, String> {
use crate::commands::{self, Command, Dir, ParseError};
let parsed = match commands::parse(cmd) {
Ok(c) => c,
Err(ParseError::Empty) => {
update.full_redraw = true;
return Ok(None);
}
Err(e) => {
update.full_redraw = true;
return Err(e.to_string());
}
};
let dir_to_nav = |d: Dir| match d {
Dir::Up => NavDir::Up,
Dir::Down => NavDir::Down,
Dir::Left => NavDir::Left,
Dir::Right => NavDir::Right,
};
let mut flash: Option<String> = None;
match parsed {
Command::SplitHorizontal => {
let _ = crate::do_split(
layout,
panes,
*active,
Direction::Horizontal,
default_shell,
tw,
th,
settings,
scrollback,
);
update.mark_all(layout);
update.border_dirty = true;
}
Command::SplitVertical => {
let _ = crate::do_split(
layout,
panes,
*active,
Direction::Vertical,
default_shell,
tw,
th,
settings,
scrollback,
);
update.mark_all(layout);
update.border_dirty = true;
}
Command::KillPane => {
let target = *active;
crate::close_pane(layout, panes, active, target);
crate::resize_all(panes, layout, tw, th, settings);
update.mark_all(layout);
update.border_dirty = true;
}
Command::KillWindow => {
*tab_action = TabAction::CloseTab;
}
Command::NewWindow { name } => {
*tab_action = TabAction::NewTab;
if let Some(n) = name {
if !n.is_empty() {
flash = Some(format!("new-window: name `{n}` (rename pending)"));
}
}
}
Command::RenameWindow { name } => {
*tab_action = TabAction::Rename(name);
}
Command::SelectPane { dir } => {
let inner = crate::make_inner(tw, th, settings.show_status_bar);
if let Some(next) = layout.navigate(*active, dir_to_nav(dir), &inner) {
*active = next;
update.full_redraw = true;
}
}
Command::ResizePane { dir, amount } => {
let delta = 0.05 * amount.max(1) as f32;
if layout.resize_pane(*active, dir_to_nav(dir), delta) {
crate::resize_all(panes, layout, tw, th, settings);
update.mark_all(layout);
update.border_dirty = true;
}
}
Command::SwapPane { up } => {
let other = if up {
layout.prev_pane(*active)
} else {
layout.next_pane(*active)
};
if other != *active {
layout.swap_panes(*active, other);
update.mark_all(layout);
update.border_dirty = true;
}
}
Command::SelectLayout { name } => {
let new_layout = Layout::from_spec(&name).map_err(|e| format!("select-layout: {e}"))?;
let new_panes = crate::spawn_layout_panes(
&new_layout,
HashMap::new(),
default_shell,
tw,
th,
settings,
scrollback,
)
.map_err(|e| format!("select-layout: spawn failed: {e}"))?;
crate::kill_all_panes(panes);
*layout = new_layout;
*panes = new_panes;
*active = *layout.pane_ids().first().unwrap_or(&0);
update.mark_all(layout);
update.border_dirty = true;
}
Command::SetOption { key, value } => match key.as_str() {
"status" | "status-bar" | "show-status-bar" => {
let on = parse_bool_opt(&value)
.ok_or_else(|| format!("set-option {key}: expected on/off, got `{value}`"))?;
settings.show_status_bar = on;
crate::resize_all(panes, layout, tw, th, settings);
update.mark_all(layout);
update.border_dirty = true;
}
"tab-bar" | "show-tab-bar" => {
let on = parse_bool_opt(&value)
.ok_or_else(|| format!("set-option {key}: expected on/off, got `{value}`"))?;
settings.show_tab_bar = on;
crate::resize_all(panes, layout, tw, th, settings);
update.mark_all(layout);
update.border_dirty = true;
}
"broadcast" => {
let on = parse_bool_opt(&value)
.ok_or_else(|| format!("set-option {key}: expected on/off, got `{value}`"))?;
*broadcast = on;
update.full_redraw = true;
}
other => {
return Err(format!("set-option: unknown key `{other}`"));
}
},
Command::DisplayMessage { text } => {
flash = Some(text);
}
}
let _ = zoomed_pane;
update.full_redraw = true;
Ok(flash)
}
pub(super) fn parse_bool_opt(s: &str) -> Option<bool> {
match s.trim().to_ascii_lowercase().as_str() {
"on" | "true" | "1" | "yes" => Some(true),
"off" | "false" | "0" | "no" => Some(false),
_ => None,
}
}