PowerShell in Emacs Shell Mode on Windows
I recently decided to give native Emacs on Windows another go (instead of using it with WSLg with its dysfunctional Wayland stack on WSL2 with Ubuntu 24.04). Since I have to use Windows for some of the classes I teach, I have tried to like WSL2. Microsoft implements Wayland in a very non-standard way that is totally broken, and even after applying a fix (not from Microsoft even if they have known for months that their default distro doesn't work with WSLg, but from users in the GitHub bug report), I had trouble with the Emacs window freezing after having locked my screen and unlocked it. I do this at every break during the day, and there are 7 of those every day, so this has to work. So I thought I would try to go native again with the Windows version of Emacs and just use what I need right now and gradually fix or work around the issues that needs fixing since Windows does things in incompatible and weird ways. For now, I need Shell Mode to work, so I have looked at that first.
By default, you get cmd.exe in Shell mode on Windows. I had some trouble finding good documentation of how to set it up with PowerShell. Some suggest just setting PowerShell to explicit-shell-file-name, but that only partially worked since it gave me an error that PowerShell was invoked with a wrong argument. A lot of people pointed to someone on the Microsoft blogs that set up PowerShell in Shell mode, but in a way that removed the prompt. He had made an Elisp workaround to get the prompt back, but it seemed like there should be an easier way. The trouble is that comint mode, the mode shell mode is built on, sends -i by default to the shells it interacts with as an argument. He had found a variable to send exactly the parameters he wanted to PowerShell, and sent something strange to make PowerShell remove the prompts. But that is the opposite of what I want. So I tried a couple of things and it turns out the solution is really simple.
(use-package shell :config (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on) (add-to-list 'comint-output-filter-functions 'ansi-color-process-output) (add-hook 'comint-output-filter-functions 'comint-osc-process-output) (setq explicit-shell-file-name "C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe") (setq explicit-powershell.exe-args '("")) (add-hook 'shell-mode-hook (lambda() (company-mode 0))))
In the code block above, I turn on ansi-color since PowerShell can work with those. I tell Emacs to run PowerShell and give it the arguments "" (ie, nothing) which remove the defautl argument -i, but adds nothing else. And now I have PowerShell in Shell mode. The last line is useful for me because I have turned on company globally, but I don't want it in shell mode.
I also discovered that if I use the command wsl inside PowerShell inside Shell Mode inside native Windows Emacs, I can use Bash on my WSL2 Ubuntu 24.04 from inside Powershell in Shell mode, although I don't get a prompt from Bash. This is actually useful for me since I want to keep the number of windows as small as possible when using Windows since its window handling is so poor (compared to my usual Sway tiling WM setup on GNU/Linux) and I need one program from WSL2 to copy some information that I then need to use inside PowerShell to do some of my daily tasks at work. With this hack, I don't need to launch a window with Ubuntu (or WSLg Emacs with Shell mode) to interact with that CLI program that runs in Bash on WSL2 (and not directly on Windows in PowerShell).