From 3106472bf3388a2f96684fd518533cbf16e3b6c3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Oct 2025 19:30:09 +0200 Subject: [PATCH] terminal: Escape strings with backticks rather than backslashes in PowerShell (#39657) Closes #39007 Strings should be escaped with backticks in PowerShell, so the following ``` \"pwsh.exe -C pytest -m \\\"some_test\\\"\" ``` becomes ``` \"pwsh.exe -C pytest -m `\"some_test`\"\" ``` Otherwise PowerShell will misinterpret the invocation resulting in weirdness all-around such as the issue linked above. Release Notes: - N/A --- crates/project/src/terminals.rs | 5 +++-- crates/util/src/shell.rs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 30090b26e146d0be6b7bc1c03b53235c22e478b2..dba842bf94b9e372fe08e17273e158270145e294 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -145,7 +145,7 @@ impl Project { project.update(cx, move |this, cx| { let format_to_run = || { if let Some(command) = &spawn_task.command { - let mut command: Option> = shlex::try_quote(command).ok(); + let mut command: Option> = shell_kind.try_quote(command); if let Some(command) = &mut command && command.starts_with('"') && let Some(prefix) = shell_kind.command_prefix() @@ -156,7 +156,8 @@ impl Project { let args = spawn_task .args .iter() - .filter_map(|arg| shlex::try_quote(arg).ok()); + .filter_map(|arg| shell_kind.try_quote(&arg)); + command.into_iter().chain(args).join(" ") } else { // todo: this breaks for remotes to windows diff --git a/crates/util/src/shell.rs b/crates/util/src/shell.rs index 95f7953ede40729749592d173800f9ddf159ceda..5c1837d822ddab4e3c1c73f95d34f71307ffccad 100644 --- a/crates/util/src/shell.rs +++ b/crates/util/src/shell.rs @@ -1,4 +1,4 @@ -use std::{fmt, path::Path, sync::LazyLock}; +use std::{borrow::Cow, fmt, path::Path, sync::LazyLock}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum ShellKind { @@ -241,6 +241,7 @@ impl ShellKind { input.into() } } + fn to_powershell_variable(input: &str) -> String { if let Some(var_str) = input.strip_prefix("${") { if var_str.find(':').is_none() { @@ -359,4 +360,14 @@ impl ShellKind { _ => None, } } + + pub fn try_quote<'a>(&self, arg: &'a str) -> Option> { + shlex::try_quote(arg).ok().map(|arg| match self { + // If we are running in PowerShell, we want to take extra care when escaping strings. + // In particular, we want to escape strings with a backtick (`) rather than a backslash (\). + // TODO double escaping backslashes is not necessary in PowerShell and probably CMD + ShellKind::PowerShell => Cow::Owned(arg.replace("\\\"", "`\"")), + _ => arg, + }) + } }