tl;dr
I want to find a Powershell version of the bash edit-and-execute-command widget or the zsh edit-command-line widget.
Background
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.
My current attempt
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)
}
Questions
My current solution feels clunky and a somewhat fragile. Are there other solutions? Can the current solution be improved?
Environment
- OS Windows 10.0.19043.0
- Powershell version 5.1.19041.1320
- PSReadLine version 2.0.0
The solution I went with
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 comes with the feature you're looking for, namely the
ViEditVisuallyfunction, and on Unix-like platforms it even has a default key binding.For the feature to work, you need to set
$env:VISUALor$env:EDITORto the name / path of your editor executable.As of PSReadLine v2.1, if you also need to include options for your editor - such as
-fforgvim- 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
ViEditVisuallyfunction.Its default key bindings, if any, depend on PSReadLine's edit mode, which you can set with
Set-PSReadLineOption -EditMode <mode>:Windowsmode (default on Windows): not boundEmacsmode (default on macOS and Linux): Ctrl-xCtrl-eVimode: v in command mode (press Esc to enter it)Use
Set-PSReadLineKeyHandlerto establish a custom key binding, analogous to the approach in the question; e.g., to bind Alt-e:Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisuallyFor the function to work, you must define the editor executable to use, via either of the following environment variables, in order of precedence:
$env:VISUALor$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
nanoeditor 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.ps1scripts 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 --waitforcode(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 proposed adding support for allowing 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:
On Unix-like platforms:
Add a definition of
$env:VISUALpointing to the helper executable to your$PROFILEfile, along with defining a custom key binding, if desired: