Is it possible to make Powershell ISE behave like vim with some vim-like editing mode or plugin?
In netbeans I'm using jVi, and in Visual Studio I'm using VsVim, is there something similar for PowerShell ISE?
Or should I drop Powershell ISE altogether and just use vim + powershell command line?
I may be misunderstanding the question, but the closest I've come to getting vim-like ability while using PS ISE is to install gvim74 for Windows, and create this function:
function vim
{
if ($args)
{
start-process 'C:\Program Files (x86)\Vim\vim74\vim.exe' $args
}
else
{
start-process 'C:\Program Files (x86)\Vim\vim74\vim.exe'
}
}
Unfortunately, ISE does not play nicely with console input, so the best I can get is starting vi in a new PS window. Not perfect, but I get my ability to "vim fileToEdit", which I do A LOT in non-Windows world.
The last weaks i searched for a way to use psISE in combination with vim (i found out its possible but hard for me to code without the ISE-comfort)
In the end i created a function/submenu in the ISE that:
With a keyboard-shortcut ([AltGr]+[v]) starts vim with the current file and waits for the process to end
When I'm finished editing in vim and saved the file its removed from the ISE
And loaded again (bacause in v3 there is no file "refreshing")
# one-line
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("edit with Vim",{$cur=$psISE.CurrentFile; saps "C:\Program Files (x86)\vim\vim74\gvim.exe" $cur.FullPath -wait; $psise.currentpowershelltab.files.remove($cur); $psISE.currentpowershelltab.files.add($cur.fullpath) },'Ctrl+Alt+v')
You have to save it first or it will not be removed
(maybe you add the $psise.CurrentFile.Save()-function)
(for permanent ISE-changes you have to put it into the $profile...)
*saps --> start-process alias
The best thing I've found is VSCode with the PowerShell and VIM plugin installed. The Powershell plugin used to be a lot worse than ISE, but recently the Intellisense has gotten much cleaner.
Related
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 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 bound
Emacs mode (default on macOS and Linux): Ctrl-xCtrl-e
Vi 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
Note: .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 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:
'#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
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
}
Update:
This GitHub Gist includes an updated version of the code. New features including saving/restoring the cursor position, passing the cursor position to VSCode and showing a message while the console is blocked.
Notes:
A default installation of VSCode adds the directory of the VSCode binaries to $env:PATH, which enables us to write just code 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. For Get-Content specifying the encoding isn't necessary, because Windows PowerShell adds a BOM which Get-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.
first image is the powershell code ~/.bashrc
Powershell
second image is the wsl ubunti code ~/.bashrc
wsl ubuntu
my question really goes, does this not work on powershell? is it a linux feature?
thanks, salamat, arigatou
ps: oof need 10 rep for images, this my first question so yeahhh
.bashrc is a file that contain code that is executed when WSL or Linux Bash shell is invoked. PowerShell has nothing to do with it. In Powershell, you can use Profiles for that. You can place powershell code in Profile and that will work like bashrc. To create a profile use new-item $profile -type file and then to open it in Notepad notepad $profile. Then you can add powershell code there.
So I'm using this console command to open a file using a specific application and then passing in a parameter (spacing used to make it more legible)
start C:\"Documents and Settings"\User\"My Documents"\Application_folder\Application.exe
"C:\Documents and Settings\user\My Documents\file_folder\file.type"
"Parameter1"
Now I want to be able to call this same command but in PowerShell ISE. I've tried a few variations but none work. I'm not all that familiar with PowerShell, so would anyone be able to school me on how to do this?
How about
& 'C:\Documents and Settings\User\My Documents\Application_folder\Application.exe' "C:\Documents and Settings\user\My Documents\file_folder\file.type" "Parameter1"
I'm using the console version of VIM (not gvim) in Windows, and I have a question about using PowerShell as the shell.
I have set my shell to be powershell and I can run commands, but there's one thing I don't know. I have a bunch of things going on in my PowerShell profile, and I was wondering if it's possible to somehow pass something to PowerShell using VIM, so that in the profile I can check and decide what to do.
I know there's an option -NoProfile we can pass to powershell.exe, but my problem is that I don't want to ignore my whole profile, because I need some parts, such as aliases etc.
Thanks,
I got some strange behaviour when executing a powershell script.
When I run my script using the ISE it works just fine.
When I open Powershell.exe and run my script it works just fine.
When I open cmd, and start my script using powershell.exe -noexit
./myscript.ps1, myscript works just fine.
When I double-click myscript however, powershell opens for some milliseconds, I see that it shows some error (red font) and the powershell window closes. I'm unable to track down the error causing this problem since the powershell windows closes to fast.
I even tried one single big try-catch block around my hole script, catching any [Exception] and writing it down to a log file. However: the log file is not generated (catch is not called).
How can I track that issue? What could possibly be causing the trouble?
Please note that my execution-policy is set to unrestricted.
Before trying the suggestion invoke this to see your current settings (if you want restore them later):
cmd /c FType Microsoft.PowerShellScript.1
Then invoke this (note that you will change how your scripts are invoked "from explorer" by this):
cmd /c #"
FType Microsoft.PowerShellScript.1=$PSHOME\powershell.exe -NoExit . "'%1'" %*
"#
Then double-click the script, it should not exit, -NoExit does the trick. See your error messages and solve the problems.
But now all your scripts invoked "from explorer" keep their console opened. You may then
remove -NoExit from the above command and run it again or restore your
original settings.
Some details and one good way to invoke scripts in PS v2 is here.
Unfortunately it is broken in PS v3 - submitted issue.
by default, for security reason when you double clic on a .ps1 file the action is : Edit file, not Run file .
to execute your script : right-click on it and choose run with powershell
I also wasn’t able to run a script by double clicking it although running it manually worked without a problem. I have found out that the problem was in the path. When I ran a script from a path that contained spaces, such as:
C:\Users\john doe\Documents\Sample.ps1
The scipt failed to run. Moving the script to:
C:\Scripts\Sample.ps1
Which has no spaces, solved the problem.
This is most likely an issue with your local Execution Policy.
By default, Powershell is configured to NOT run scripts that are unsigned (even local ones). If you've not signed your scripts, then changing your default double-click 'action' in Windows will have no effect - Powershell will open, read the execution policy, check the script's signature, and finding none, will abort with an error.
In Powershell:
Help about_execution_policies
gives you all the gory details, as well as ways to allow unsigned scripts to run (within reason - you'd probably not want to run remote ones, only ones you've saved onto the system).
EDIT: I see at the tail end of your question that you've set Execution Policy to 'unrestricted' which SHOULD allow the script to run. However, this might be useful info for others running into execution policy issues.
If you would catch the error you will most likely see this
The file cannot be loaded. The file is not
digitally signed. The script will not execute on the system. Please
see "Get-Help about_signing" for more details.
Because you are able to run it from the shell you started yourself, and not with the right mouse button click "Run With PowerShell", I bet you have x64 system. Manually you are starting the one version of PowerShell where execution policy is configured, while with the right click the other version of the PowerShell is started.
Try to start both version x64 and x86 version and check for security policies in each
Get-ExecutionPolicy
I was in exactly the same situation as described in the question : my script worked everywhere except when double-clicking.* When I double-clicked a powershell windows would open but then it will close after a second or so. My execution-policy is also set to unrestricted.
I tried the selected answer concerning FType Microsoft.PowerShellScript.1 but it didn't change anything.
The only solution I found was a work around: create a bat file which start the powershell.
Create a file, copy this and modify the path : powershell.exe -File "C:\Users\user\script\myscript.ps1"
Save it as a .bat
Double-click the bat
I also used .ahk to start my powershell with a shorcut and it didn't work when pointing directly to the powershell. I had to point to the .bat