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
CodePudding user response:
This code has some issues:
- Temp path is hardcoded, it should use
$env:temp
or better yet[IO.Path]::GetTempPath()
(for cross-platform compatibility). - After editing the line, it doesn't replace the whole line, only the text to the left of the cursor. As noted by mklement0, we can simply replace the existing buffer instead of erasing it, which fixes the problem.
- When using the right parameters for the editor, it is not required to create a job to wait for it. For VSCode this is
--wait
(-w
) and for gvim this is--nofork
(-f
), which prevents these processes to detach from the console process so the PowerShell code waits until the user has closed the editor. - The temporary file is not deleted after closing the editor.
Here is my attempt at fixing the code. I don't use gvim
, so I tested it with VSCode code.exe
. The code below contains a commented line for gvim
too (confirmed working by the OP).
Set-PSReadLineKeyHandler -Chord "Alt e" -ScriptBlock {
$CurrentInput = $null
# Copy current console line
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
# Save current console line to temp file
$tempFilePath = Join-Path ([IO.Path]::GetTempPath()) "ps_$PID.ps1"
Set-Content $tempFilePath -Value $CurrentInput -Encoding utf8
# Edit the console line using VSCode
code --new-window --wait $tempFilePath
# Uncomment for using gvim editor instead
# gvim -f $tempFilePath
# The console doesn't like the CR character, so rejoin lines using LF only.
$editedInput = ((Get-Content -LiteralPath $tempFilePath) -join "`n").Trim()
# Replace current console line with the content of the temp file
[Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, $currentInput.Length, $editedInput)
Remove-Item $tempFilePath
}
Notes:
- A default installation of VSCode adds the directory of the VSCode binaries to
$env:PATH
, which enables us to write justcode
to launch the editor. - While UTF-8 is the default encoding for cmdlets like
Set-Content
on PowerShell Core, for Windows PowerShell the parameter-Encoding utf8
is required to correctly save commands that contain Unicode characters. ForGet-Content
specifying the encoding isn't necessary, because Windows PowerShell adds a BOM whichGet-Content
detects and PowerShell Core defaults to UTF-8 again. - To have Alt E always available when you open a console, just add this code to your
$profile
file. Makes quick testing and editing of small code samples a breeze. - VSCode settings - for most streamlined experience, enable "Auto Save: onWindowChange". Allows you to close the editor and save the file (update the console line) with a single press to Ctrl W.
CodePudding user response:
OP here. With the help of zett42, I have now refined the function to this:
Set-PSReadLineKeyHandler -Chord "Alt e" -ScriptBlock {
$CurrentInput = $null
$tmpfile = "$env:Temp\ps_${PID}.txt"
# Copy current command-line input, save it to a file and clear it
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
Set-Content -Path $tmpfile -Value "$CurrentInput"
[Microsoft.PowerShell.PSConsoleReadLine]::BackwardKillLine()
[Microsoft.PowerShell.PSConsoleReadLine]::KillLine()
gvim -f "$tmpfile"
# Get command back from file the temporary file and insert it into the command-line
$NewInput = (Get-Content -Raw -Path "$tmpfile").Trim()
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($NewInput)
}