use crate::config::Config;
use crate::terminal::TerminalManager;
pub(crate) fn configure_terminal_from_config(terminal: &mut TerminalManager, config: &Config) {
terminal.set_theme(config.load_theme());
terminal.set_max_clipboard_sync_events(config.clipboard_max_sync_events);
terminal.set_max_clipboard_event_bytes(config.clipboard_max_event_bytes);
if !config.answerback_string.is_empty() {
terminal.set_answerback_string(Some(config.answerback_string.clone()));
}
let width_config = par_term_emu_core_rust::WidthConfig::new(
config.unicode.unicode_version,
config.unicode.ambiguous_width,
);
terminal.set_width_config(width_config);
terminal.set_normalization_form(config.unicode.normalization_form);
use crate::config::CursorStyle as ConfigCursorStyle;
use par_term_emu_core_rust::cursor::CursorStyle as TermCursorStyle;
let term_style = if config.cursor_blink {
match config.cursor_style {
ConfigCursorStyle::Block => TermCursorStyle::BlinkingBlock,
ConfigCursorStyle::Underline => TermCursorStyle::BlinkingUnderline,
ConfigCursorStyle::Beam => TermCursorStyle::BlinkingBar,
}
} else {
match config.cursor_style {
ConfigCursorStyle::Block => TermCursorStyle::SteadyBlock,
ConfigCursorStyle::Underline => TermCursorStyle::SteadyUnderline,
ConfigCursorStyle::Beam => TermCursorStyle::SteadyBar,
}
};
terminal.set_cursor_style(term_style);
}
#[cfg(target_os = "windows")]
const PATH_SEPARATOR: char = ';';
#[cfg(not(target_os = "windows"))]
const PATH_SEPARATOR: char = ':';
pub(crate) fn build_shell_env(
config_env: Option<&std::collections::HashMap<String, String>>,
) -> Option<std::collections::HashMap<String, String>> {
let mut env = std::collections::HashMap::new();
env.insert("TERM_PROGRAM".to_string(), "iTerm.app".to_string());
env.insert("TERM_PROGRAM_VERSION".to_string(), "3.6.6".to_string());
env.insert("LC_TERMINAL".to_string(), "iTerm2".to_string());
env.insert("LC_TERMINAL_VERSION".to_string(), "3.6.6".to_string());
env.insert("__PAR_TERM".to_string(), "1".to_string());
let session_uuid = uuid::Uuid::new_v4();
env.insert(
"ITERM_SESSION_ID".to_string(),
format!("w0t0p0:{session_uuid}"),
);
for key in &["LANG", "LC_ALL", "LC_CTYPE", "LC_COLLATE", "LC_MESSAGES"] {
if let Ok(val) = std::env::var(key) {
env.insert((*key).to_string(), val);
}
}
if !env.contains_key("LANG") && !env.contains_key("LC_ALL") && !env.contains_key("LC_CTYPE") {
env.insert("LANG".to_string(), "en_US.UTF-8".to_string());
}
if let Some(config) = config_env {
for (key, value) in config {
env.insert(key.clone(), value.clone());
}
}
let current_path = std::env::var("PATH").unwrap_or_default();
let extra_paths = build_platform_extra_paths();
let new_paths: Vec<String> = extra_paths
.into_iter()
.filter(|p| !p.is_empty() && !current_path.contains(p) && std::path::Path::new(p).exists())
.collect();
let augmented_path = if new_paths.is_empty() {
current_path
} else {
format!(
"{}{}{}",
new_paths.join(&PATH_SEPARATOR.to_string()),
PATH_SEPARATOR,
current_path
)
};
env.insert("PATH".to_string(), augmented_path);
Some(env)
}
#[cfg(target_os = "windows")]
fn build_platform_extra_paths() -> Vec<String> {
let mut paths = Vec::new();
if let Some(home) = dirs::home_dir() {
paths.push(
home.join(".cargo")
.join("bin")
.to_string_lossy()
.to_string(),
);
paths.push(
home.join("scoop")
.join("shims")
.to_string_lossy()
.to_string(),
);
paths.push(home.join("go").join("bin").to_string_lossy().to_string());
}
paths.push(r"C:\ProgramData\chocolatey\bin".to_string());
if let Some(local_app_data) = dirs::data_local_dir() {
paths.push(
local_app_data
.join("Programs")
.join("Python")
.join("Python312")
.join("Scripts")
.to_string_lossy()
.to_string(),
);
paths.push(
local_app_data
.join("Programs")
.join("Python")
.join("Python311")
.join("Scripts")
.to_string_lossy()
.to_string(),
);
}
paths
}
#[cfg(not(target_os = "windows"))]
fn build_platform_extra_paths() -> Vec<String> {
let mut paths = Vec::new();
if let Some(home) = dirs::home_dir() {
paths.push(
home.join(".local")
.join("bin")
.to_string_lossy()
.to_string(),
);
paths.push(
home.join(".cargo")
.join("bin")
.to_string_lossy()
.to_string(),
);
paths.push(home.join("go").join("bin").to_string_lossy().to_string());
paths.push(
home.join(".nix-profile")
.join("bin")
.to_string_lossy()
.to_string(),
);
}
paths.push("/nix/var/nix/profiles/default/bin".to_string());
#[cfg(target_os = "macos")]
{
paths.push("/opt/homebrew/bin".to_string());
paths.push("/opt/homebrew/sbin".to_string());
paths.push("/usr/local/bin".to_string());
paths.push("/usr/local/sbin".to_string());
paths.push("/opt/local/bin".to_string());
}
#[cfg(target_os = "linux")]
{
paths.push("/usr/local/bin".to_string());
paths.push("/snap/bin".to_string());
if let Some(home) = dirs::home_dir() {
paths.push(
home.join(".local")
.join("share")
.join("flatpak")
.join("exports")
.join("bin")
.to_string_lossy()
.to_string(),
);
}
paths.push("/var/lib/flatpak/exports/bin".to_string());
}
paths
}
pub(crate) fn get_shell_command(config: &Config) -> (String, Option<Vec<String>>) {
if let Some(ref custom) = config.custom_shell {
(custom.clone(), config.shell_args.clone())
} else {
#[cfg(target_os = "windows")]
{
("powershell.exe".to_string(), None)
}
#[cfg(not(target_os = "windows"))]
{
(
std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_string()),
None,
)
}
}
}
#[cfg(not(target_os = "windows"))]
pub(crate) fn apply_login_shell_flag(shell_args: &mut Option<Vec<String>>, config: &Config) {
if config.login_shell {
let args = shell_args.get_or_insert_with(Vec::new);
if !args.iter().any(|a| a == "-l" || a == "--login") {
args.insert(0, "-l".to_string());
}
}
}
#[cfg(target_os = "windows")]
pub(crate) fn apply_login_shell_flag(_shell_args: &mut Option<Vec<String>>, _config: &Config) {
}
pub(crate) fn create_base_terminal(
config: &Config,
grid_size: Option<(usize, usize)>,
) -> anyhow::Result<(TerminalManager, usize, usize)> {
let (cols, rows) = grid_size.unwrap_or((config.cols, config.rows));
let mut terminal =
TerminalManager::new_with_scrollback(cols, rows, config.scrollback.scrollback_lines)?;
configure_terminal_from_config(&mut terminal, config);
Ok((terminal, cols, rows))
}