Change summary
crates/task/src/lib.rs | 4 +
crates/terminal/src/terminal.rs | 14 ++++
crates/util/src/util.rs | 102 +++++++++++++++++++++++++++++++++++
3 files changed, 118 insertions(+), 2 deletions(-)
Detailed changes
@@ -434,8 +434,10 @@ impl ShellBuilder {
// `alacritty_terminal` uses this as default on Windows. See:
// https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130
+ // We could use `util::retrieve_system_shell()` here, but we are running tasks here, so leave it to `powershell.exe`
+ // should be okay.
fn system_shell() -> String {
- "powershell".to_owned()
+ "powershell.exe".to_string()
}
fn to_windows_shell_variable(&self, input: String) -> String {
@@ -376,7 +376,19 @@ impl TerminalBuilder {
let pty_options = {
let alac_shell = match shell.clone() {
- Shell::System => None,
+ Shell::System => {
+ #[cfg(target_os = "windows")]
+ {
+ Some(alacritty_terminal::tty::Shell::new(
+ util::retrieve_system_shell(),
+ Vec::new(),
+ ))
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ None
+ }
+ }
Shell::Program(program) => {
Some(alacritty_terminal::tty::Shell::new(program, Vec::new()))
}
@@ -416,6 +416,108 @@ pub fn iterate_expanded_and_wrapped_usize_range(
}
}
+#[cfg(target_os = "windows")]
+pub fn retrieve_system_shell() -> String {
+ use std::path::PathBuf;
+
+ fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option<PathBuf> {
+ #[cfg(target_pointer_width = "64")]
+ let env_var = if find_alternate {
+ "ProgramFiles(x86)"
+ } else {
+ "ProgramFiles"
+ };
+
+ #[cfg(target_pointer_width = "32")]
+ let env_var = if find_alternate {
+ "ProgramW6432"
+ } else {
+ "ProgramFiles"
+ };
+
+ let install_base_dir = PathBuf::from(std::env::var_os(env_var)?).join("PowerShell");
+ install_base_dir
+ .read_dir()
+ .ok()?
+ .filter_map(Result::ok)
+ .filter(|entry| matches!(entry.file_type(), Ok(ft) if ft.is_dir()))
+ .filter_map(|entry| {
+ let dir_name = entry.file_name();
+ let dir_name = dir_name.to_string_lossy();
+
+ let version = if find_preview {
+ let dash_index = dir_name.find('-')?;
+ if &dir_name[dash_index + 1..] != "preview" {
+ return None;
+ };
+ dir_name[..dash_index].parse::<u32>().ok()?
+ } else {
+ dir_name.parse::<u32>().ok()?
+ };
+
+ let exe_path = entry.path().join("pwsh.exe");
+ if exe_path.exists() {
+ Some((version, exe_path))
+ } else {
+ None
+ }
+ })
+ .max_by_key(|(version, _)| *version)
+ .map(|(_, path)| path)
+ }
+
+ fn find_pwsh_in_msix(find_preview: bool) -> Option<PathBuf> {
+ let msix_app_dir =
+ PathBuf::from(std::env::var_os("LOCALAPPDATA")?).join("Microsoft\\WindowsApps");
+ if !msix_app_dir.exists() {
+ return None;
+ }
+
+ let prefix = if find_preview {
+ "Microsoft.PowerShellPreview_"
+ } else {
+ "Microsoft.PowerShell_"
+ };
+ msix_app_dir
+ .read_dir()
+ .ok()?
+ .filter_map(|entry| {
+ let entry = entry.ok()?;
+ if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) {
+ return None;
+ }
+
+ if !entry.file_name().to_string_lossy().starts_with(prefix) {
+ return None;
+ }
+
+ let exe_path = entry.path().join("pwsh.exe");
+ exe_path.exists().then_some(exe_path)
+ })
+ .next()
+ }
+
+ fn find_pwsh_in_scoop() -> Option<PathBuf> {
+ let pwsh_exe =
+ PathBuf::from(std::env::var_os("USERPROFILE")?).join("scoop\\shims\\pwsh.exe");
+ pwsh_exe.exists().then_some(pwsh_exe)
+ }
+
+ static SYSTEM_SHELL: LazyLock<String> = LazyLock::new(|| {
+ find_pwsh_in_programfiles(false, false)
+ .or_else(|| find_pwsh_in_programfiles(true, false))
+ .or_else(|| find_pwsh_in_msix(false))
+ .or_else(|| find_pwsh_in_programfiles(false, true))
+ .or_else(|| find_pwsh_in_msix(true))
+ .or_else(|| find_pwsh_in_programfiles(true, true))
+ .or_else(find_pwsh_in_scoop)
+ .map(|p| p.to_string_lossy().to_string())
+ .unwrap_or("powershell.exe".to_string())
+ });
+
+ (*SYSTEM_SHELL).clone()
+}
+
pub trait ResultExt<E> {
type Ok;