I want to find a Powershell version of the bash
edit-and-execute-command
widget or the zsh
edit-command-line
widget.
Short commands get executed directly on the command-line, long complicated commands get executed from scripts. However, before they become "long", it helps to be able to test medium length commands on the command-line. To assist in this effort, editing the command in an external editor becomes very helpful. AFAIK Powershell does not support this natively as e.g. bash
and zsh
do.
I am new to Powershell, so I'm bound to make many mistakes, but I have come up with a working solution using the features of the [Microsoft.Powershell.PSConsoleReadLine]
class. I am able to copy the current command-line to a file, edit the file, and then re-inject the edited version back into the command-line:
Set-PSReadLineKeyHandler -Chord "Alt+e" -ScriptBlock {
$CurrentInput = $null
# Copy current command-line input, save it to a file and clear it
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
Set-Content -Path "C:\Temp\ps_${PID}.txt" -Value "$CurrentInput"
[Microsoft.PowerShell.PSConsoleReadLine]::KillRegion()
# Edit the command with gvim
Start-Job -Name EditCMD -ScriptBlock { gvim "C:\Temp\ps_${Using:PID}.txt" }
Wait-Job -Name EditCMD
# Get command back from file the temporary file and insert it into the command-line
$NewInput = (Get-Content -Path "C:\Temp\ps_${PID}.txt") -join "`n"
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($NewInput)
}
My current solution feels clunky and a somewhat fragile. Are there other solutions? Can the current solution be improved?
Create a "baked" executable similar to what mklement0 showed. I prefer vim
for this instead of `gvim, as it runs directly in the console:
'@vim -f %*' > psvim.cmd
$env:EDITOR = "psvim"
Set-PSReadLineKeyHandler -Chord "Alt+e" -Function ViEditVisually
tl;dr
PSReadLine already comes with the feature you're looking for, namely the ViEditVisually
function, and on Unix-like platforms it even has a default key binding.
For the feature to work, you need to set $env:VISUAL
or $env:EDITOR
to the name / path of your editor executable.
As of PSReadLine v2.1, if you also need to include options for your editor - such as -f
for gvim
- you need to create a helper executable that has the options "baked in".
Since creating a helper executable is cumbersome, a custom emulation of the feature, as you have attempted and as refined in zett42's helpful answer, may be the simpler solution for now. zett42's answer additionally links to a Gist that improves the original functionality by preserving cursor positions.
GitHub issue #3214 suggests adding support for recognizing executables plus their options in these environment variables.
Details below.
The PSReadLine module ships with such a feature, namely the ViEditVisually
function.
Its default key bindings, if any, depend on PSReadLine's edit mode, which you can set with Set-PSReadLineOption -EditMode <mode>
:
Windows
mode (default on Windows): not boundEmacs
mode (default on macOS and Linux): Ctrl-xCtrl-eVi
mode: v in command mode (press Esc to enter it)Use Set-PSReadLineKeyHandler
to establish a custom key binding, analogous to the approach in the question; e.g., to bind Alt-e:
Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually
For the function to work, you must define the editor executable to use, via either of the following environment variables, in order of precedence: $env:VISUAL
or $env:EDITOR
; if no (valid) editor is defined, a warning beep is emitted and no action is taken when the function is invoked.
E.g., to use the nano
editor on macOS: $env:VISUAL = 'nano'
The value must refer to an executable file - either by full path or, more typically, by name only, in which case it must be located in a directory listed in $env:PATH
.ps1
scripts do not qualify as executable files, but batch files on Windows and shebang-line-based shell scripts on Unix-like platforms do.As of PSReadLine 2.1, including options for the executable - such as --newindow --wait
for code
(Visual Studio Code) is not supported.
Therefore, for now, if your editor of choice requires options, you need to create a helper executable that has these options "baked in"; see examples below.
GitHub issue #3214 proposes adding support for being able to specify the editor executable plus options as the environment-variable value, which is something that Git (which recognizes the same variables) already supports; for instance, you could then define:
$env:VISUAL = 'code --new-window --wait'
Example configuration with a helper executable for code
(Visual Studio Code):
Create a helper executable in the user's home directory in this example:
On Windows:
'@code --new-window --wait %*' > "$HOME\codewait.cmd"
On Unix-like platforms:
"#!/bin/sh`ncode --new-window --wait `"$@`"" > "$HOME/codewait"; chmod a+x "$HOME/codewait"
Add a definition of $env:VISUAL
pointing to the helper executable to your $PROFILE
file, along with defining a custom key binding, if desired:
$env:VISUAL = "$HOME/codewait"
# Custom key binding
Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually