fix(pwsh): use adequate quoting when formatting preview commands for pwsh

This commit is contained in:
Alex Pasmantier 2025-04-08 00:12:39 +02:00
parent 6d26db2993
commit e2f83bdaac
6 changed files with 94 additions and 30 deletions

View File

@ -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"

View File

@ -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<String> {
std::env::var(SHELL_ENV_VAR).ok()
}
fn get_raw_aliases(shell: Shell) -> Vec<String> {
// 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<String> {
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<Alias>) {
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<Alias>) {
value.to_string(),
));
}
} else {
debug!("Invalid alias format: {}", alias);
}
None
})

View File

@ -110,7 +110,7 @@ impl Channel {
#[allow(clippy::unused_async)]
async fn load_candidates(command: String, injector: Injector<String>) {
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())

View File

@ -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");

View File

@ -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
}

View File

@ -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<Self> {
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<Self> {
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<CliShell> for Shell {
fn from(val: CliShell) -> Self {
match val {