diff --git a/cable/windows-channels.toml b/cable/windows-channels.toml index 281df8b..b511080 100644 --- a/cable/windows-channels.toml +++ b/cable/windows-channels.toml @@ -2,7 +2,7 @@ [[cable_channel]] name = "git-diff" source_command = "git diff --name-only" -preview_command = "git diff --color=always {0}" +preview_command = "git diff --color=always -- {0}" [[cable_channel]] name = "git-reflog" diff --git a/television/channels/alias.rs b/television/channels/alias.rs index fed01d0..ecf57da 100644 --- a/television/channels/alias.rs +++ b/television/channels/alias.rs @@ -4,7 +4,9 @@ use crate::channels::entry::Entry; use crate::channels::entry::PreviewType; use crate::channels::OnAir; use crate::matcher::{config::Config, injector::Injector, Matcher}; +use crate::utils::command::shell_command; use crate::utils::indices::sep_name_and_value_indices; +use crate::utils::shell::Shell; use devicons::FileIcon; use rustc_hash::FxBuildHasher; use rustc_hash::FxHashSet; @@ -32,19 +34,20 @@ pub struct Channel { const NUM_THREADS: usize = 1; const FILE_ICON_STR: &str = "nu"; -const SHELL_ENV_VAR: &str = "SHELL"; -fn get_current_shell() -> Option { - std::env::var(SHELL_ENV_VAR).ok() -} +fn get_raw_aliases(shell: Shell) -> Vec { + // this needs to be run in an interactive shell in order to get the aliases + let mut command = shell_command(true); -fn get_raw_aliases(shell: &str) -> Vec { - let output = std::process::Command::new(shell) - .arg("-i") - .arg("-c") - .arg("alias") - .output() - .expect("failed to execute process"); + let output = match shell { + Shell::PowerShell => { + command.arg("Get-Alias | Format-List -Property Name, Definition") + } + Shell::Cmd => command.arg("doskey /macros"), + _ => command.arg("-i").arg("alias").arg("2>/dev/null"), + } + .output() + .expect("failed to execute process"); let aliases = String::from_utf8_lossy(&output.stdout); aliases.lines().map(ToString::to_string).collect() @@ -151,8 +154,7 @@ impl OnAir for Channel { #[allow(clippy::unused_async)] async fn load_aliases(injector: Injector) { - let raw_shell = get_current_shell().unwrap_or("bash".to_string()); - let shell = raw_shell.split('/').last().unwrap(); + let shell = Shell::from_env().unwrap_or_default(); debug!("Current shell: {}", shell); let raw_aliases = get_raw_aliases(shell); @@ -167,6 +169,8 @@ async fn load_aliases(injector: Injector) { value.to_string(), )); } + } else { + debug!("Invalid alias format: {}", alias); } None }) diff --git a/television/channels/cable.rs b/television/channels/cable.rs index 839be19..26ebaf7 100644 --- a/television/channels/cable.rs +++ b/television/channels/cable.rs @@ -110,7 +110,7 @@ impl Channel { #[allow(clippy::unused_async)] async fn load_candidates(command: String, injector: Injector) { debug!("Loading candidates from command: {:?}", command); - let mut child = shell_command() + let mut child = shell_command(false) .arg(command) .stdout(Stdio::piped()) .stderr(Stdio::piped()) diff --git a/television/preview/previewers/command.rs b/television/preview/previewers/command.rs index b239fa7..61894b2 100644 --- a/television/preview/previewers/command.rs +++ b/television/preview/previewers/command.rs @@ -174,7 +174,7 @@ pub fn try_preview( let command = format_command(command, entry, command_re); debug!("Formatted preview command: {:?}", command); - let child = shell_command() + let child = shell_command(false) .arg(&command) .output() .expect("failed to execute process"); diff --git a/television/utils/command.rs b/television/utils/command.rs index 2926bb5..f971ac0 100644 --- a/television/utils/command.rs +++ b/television/utils/command.rs @@ -1,19 +1,21 @@ use std::process::Command; -#[cfg(not(windows))] -pub fn shell_command() -> Command { - let mut cmd = Command::new("sh"); +use super::shell::Shell; - cmd.arg("-c"); - - cmd -} - -#[cfg(windows)] -pub fn shell_command() -> Command { - let mut cmd = Command::new("cmd"); - - cmd.arg("/c"); +pub fn shell_command(interactive: bool) -> Command { + let shell = Shell::from_env().unwrap_or_default(); + let mut cmd = Command::new(shell.executable()); + + cmd.arg(match shell { + Shell::PowerShell => "-Command", + Shell::Cmd => "/C", + _ => "-c", + }); + + #[cfg(unix)] + if interactive { + cmd.arg("-i"); + } cmd } diff --git a/television/utils/shell.rs b/television/utils/shell.rs index 0f9efc3..aa351fa 100644 --- a/television/utils/shell.rs +++ b/television/utils/shell.rs @@ -1,8 +1,10 @@ use crate::cli::args::Shell as CliShell; use crate::config::shell_integration::ShellIntegrationConfig; use anyhow::Result; +use strum::Display; +use tracing::debug; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Display)] pub enum Shell { Bash, Zsh, @@ -11,6 +13,62 @@ pub enum Shell { Cmd, } +impl Default for Shell { + #[cfg(unix)] + fn default() -> Self { + Shell::Bash + } + + #[cfg(windows)] + fn default() -> Self { + Shell::PowerShell + } +} + +const SHELL_ENV_VAR: &str = "SHELL"; + +impl TryFrom<&str> for Shell { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + if value.contains("bash") { + Ok(Shell::Bash) + } else if value.contains("zsh") { + Ok(Shell::Zsh) + } else if value.contains("fish") { + Ok(Shell::Fish) + } else if value.contains("powershell") { + Ok(Shell::PowerShell) + } else if value.contains("cmd") { + Ok(Shell::Cmd) + } else { + Err(anyhow::anyhow!("Unsupported shell: {}", value)) + } + } +} + +impl Shell { + #[allow(clippy::borrow_interior_mutable_const)] + pub fn from_env() -> Result { + if let Ok(shell) = std::env::var(SHELL_ENV_VAR) { + Shell::try_from(shell.as_str()) + } else { + debug!("Environment variable {} not set", SHELL_ENV_VAR); + Ok(Shell::default()) + } + } + + pub fn executable(&self) -> &'static str { + match self { + Shell::Bash => "bash", + Shell::Zsh => "zsh", + Shell::Fish => "fish", + Shell::PowerShell => "powershell", + Shell::Cmd => "cmd", + } + } +} + impl From for Shell { fn from(val: CliShell) -> Self { match val {