diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 6ad38e9829..51349464b1 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -575,12 +575,11 @@ fn retrieve_package_id_and_bin_name_from_metadata( metadata: CargoMetadata, abs_path: &Path, ) -> Option<(String, String)> { - let abs_path = abs_path.to_str()?; - for package in metadata.packages { for target in package.targets { let is_bin = target.kind.iter().any(|kind| kind == "bin"); - if target.src_path == abs_path && is_bin { + let target_path = PathBuf::from(target.src_path); + if target_path == abs_path && is_bin { return Some((package.id, target.name)); } } diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index 7ea5ccb8c0..0114ce2c5f 100644 --- a/crates/terminal/src/terminal_settings.rs +++ b/crates/terminal/src/terminal_settings.rs @@ -268,6 +268,48 @@ pub enum Shell { }, } +impl Shell { + pub fn retrieve_system_shell() -> Option { + #[cfg(not(target_os = "windows"))] + { + use anyhow::Context; + use util::ResultExt; + + return std::env::var("SHELL") + .context("Error finding SHELL in env.") + .log_err(); + } + // `alacritty_terminal` uses this as default on Windows. See: + // https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130 + #[cfg(target_os = "windows")] + return Some("powershell".to_owned()); + } + + /// Convert unix-shell variable syntax to windows-shell syntax. + /// `powershell` and `cmd` are considered valid here. + #[cfg(target_os = "windows")] + pub fn to_windows_shell_variable(shell_type: WindowsShellType, input: String) -> String { + match shell_type { + WindowsShellType::Powershell => to_powershell_variable(input), + WindowsShellType::Cmd => to_cmd_variable(input), + WindowsShellType::Other => input, + } + } + + #[cfg(target_os = "windows")] + pub fn to_windows_shell_type(shell: &str) -> WindowsShellType { + if shell == "powershell" || shell.ends_with("powershell.exe") { + WindowsShellType::Powershell + } else if shell == "cmd" || shell.ends_with("cmd.exe") { + WindowsShellType::Cmd + } else { + // Someother shell detected, the user might install and use a + // unix-like shell. + WindowsShellType::Other + } + } +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AlternateScroll { @@ -299,3 +341,55 @@ pub struct ToolbarContent { /// Default: true pub title: Option, } + +#[cfg(target_os = "windows")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WindowsShellType { + Powershell, + Cmd, + Other, +} + +/// Convert `${SOME_VAR}`, `$SOME_VAR` to `%SOME_VAR%`. +#[inline] +#[cfg(target_os = "windows")] +fn to_cmd_variable(input: String) -> String { + if let Some(var_str) = input.strip_prefix("${") { + if var_str.find(':').is_none() { + // If the input starts with "${", remove the trailing "}" + format!("%{}%", &var_str[..var_str.len() - 1]) + } else { + // `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation, + // which will result in the task failing to run in such cases. + input + } + } else if let Some(var_str) = input.strip_prefix('$') { + // If the input starts with "$", directly append to "$env:" + format!("%{}%", var_str) + } else { + // If no prefix is found, return the input as is + input + } +} + +/// Convert `${SOME_VAR}`, `$SOME_VAR` to `$env:SOME_VAR`. +#[inline] +#[cfg(target_os = "windows")] +fn to_powershell_variable(input: String) -> String { + if let Some(var_str) = input.strip_prefix("${") { + if var_str.find(':').is_none() { + // If the input starts with "${", remove the trailing "}" + format!("$env:{}", &var_str[..var_str.len() - 1]) + } else { + // `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation, + // which will result in the task failing to run in such cases. + input + } + } else if let Some(var_str) = input.strip_prefix('$') { + // If the input starts with "$", directly append to "$env:" + format!("$env:{}", var_str) + } else { + // If no prefix is found, return the input as is + input + } +} diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 1198642f49..064adfaaf4 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -350,24 +350,66 @@ impl TerminalPanel { let mut spawn_task = spawn_in_terminal.clone(); // Set up shell args unconditionally, as tasks are always spawned inside of a shell. let Some((shell, mut user_args)) = (match TerminalSettings::get_global(cx).shell.clone() { - Shell::System => std::env::var("SHELL").ok().map(|shell| (shell, Vec::new())), + Shell::System => Shell::retrieve_system_shell().map(|shell| (shell, Vec::new())), Shell::Program(shell) => Some((shell, Vec::new())), Shell::WithArguments { program, args } => Some((program, args)), }) else { return; }; + #[cfg(target_os = "windows")] + let windows_shell_type = Shell::to_windows_shell_type(&shell); + + #[cfg(not(target_os = "windows"))] + { + spawn_task.command_label = format!("{shell} -i -c `{}`", spawn_task.command_label); + } + #[cfg(target_os = "windows")] + { + use terminal::terminal_settings::WindowsShellType; + + match windows_shell_type { + WindowsShellType::Powershell => { + spawn_task.command_label = format!("{shell} -C `{}`", spawn_task.command_label) + } + WindowsShellType::Cmd => { + spawn_task.command_label = format!("{shell} /C `{}`", spawn_task.command_label) + } + WindowsShellType::Other => { + spawn_task.command_label = + format!("{shell} -i -c `{}`", spawn_task.command_label) + } + } + } - spawn_task.command_label = format!("{shell} -i -c `{}`", spawn_task.command_label); let task_command = std::mem::replace(&mut spawn_task.command, shell); let task_args = std::mem::take(&mut spawn_task.args); let combined_command = task_args .into_iter() .fold(task_command, |mut command, arg| { command.push(' '); + #[cfg(not(target_os = "windows"))] command.push_str(&arg); + #[cfg(target_os = "windows")] + command.push_str(&Shell::to_windows_shell_variable(windows_shell_type, arg)); command }); + + #[cfg(not(target_os = "windows"))] user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command]); + #[cfg(target_os = "windows")] + { + use terminal::terminal_settings::WindowsShellType; + + match windows_shell_type { + WindowsShellType::Powershell => { + user_args.extend(["-C".to_owned(), combined_command]) + } + WindowsShellType::Cmd => user_args.extend(["/C".to_owned(), combined_command]), + WindowsShellType::Other => { + user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command]) + } + } + } spawn_task.args = user_args; let spawn_task = spawn_task;