PowerShell ISE v3: Keyboard Shortcut to Close Script Tab

Background

In the PowerShell Integrated Scripting Editor (ISE) v3, the common [Ctrl] + W keyboard shortcut is mapped to the “Close PowerShell Tab” action. Personally, I would like to see different behavior, whereby that shortcut is used to close the active script tab until there are none left, at which point it may then close the active PowerShell tab. Unfortunately that’s not how it works, and it probably won’t get changed for the final release of PowerShell v3. Either way, I did file a bug report for this issue on Microsoft Connect.

There is, in fact, a keyboard shortcut mapped to the “Close Script Tab” action, however it’s a keyboard shortcut that I’m personally not very fond of. The [Ctrl] + [F4] shortcut is rather convoluted, and although it may have a legacy in the Microsoft world, I find it to be very uncomfortable.

Solution

Since [Ctrl] + W and [Ctrl] + [F4] aren’t really options in my mind, I figured I would map a new, custom keyboard shortcut to close the current script tab. It’s very close to [Ctrl] + W, but just one key over … [Ctrl] + E. I wrote up a little PowerShell script that will create this mapping for you, and add a custom menu item under the Add-ons menu.

Windows PowerShell ISE v3 - Close Script Tab

[cc lang=”powershell”]$ScriptBlock = {
foreach ($File in $psise.CurrentPowerShellTab.Files) {
if ($psise.CurrentFile.DisplayName -eq $File.DisplayName) {
#write-host $File.DisplayName
$psise.CurrentPowerShellTab.Files.Remove($File);
}
}
}

$KeyGesture = New-Object `
-TypeName System.Windows.Input.KeyGesture `
-ArgumentList ([System.Windows.Input.Key]::E, [System.Windows.Input.ModifierKeys]::Control);

$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add(“Close script tab”, $ScriptBlock, $KeyGesture);

$KeyGesture, $ScriptBlock = $null;[/cc]

UPDATE

Here is a much more robust version of the script, that will:

  1. Automatically add the keyboard shortcut to any new PowerShell tabs (sweeeeet!)
  2. Adds the keyboard shortcut to all currently open PowerShell tabs
  3. Adds some error-checking to ensure that the Event Subscriber does not fail

[cc lang=”powershell” lines=”-1″]# Define the script block that will execute every time a new PowerShell tab is opened.
# We have to do this, because the keyboard mapping is configure per-PowerShell tab. Therefore,
# each time a new PowerShell tab is opened, we must perform create the keyboard mapping.

# NOTE: There is a bug
$ScriptBlock = {
# Define the script block that will execute for our ISE menu add-on
$CloseFileScriptBlock = {
foreach ($File in $psise.CurrentPowerShellTab.Files) {
if ($psise.CurrentFile.DisplayName -eq $File.DisplayName) {
#write-host $File.DisplayName
$FileToRemove = $File;
# Write-Host ‘breaking!’;
break;

}
}
[void] $psise.CurrentPowerShellTab.Files.Remove($FileToRemove);
}

# Define the key gesture we’ll use to close the active ISE script tab
$KeyGesture = New-Object `
-TypeName System.Windows.Input.KeyGesture `
-ArgumentList ([System.Windows.Input.Key]::E, [System.Windows.Input.ModifierKeys]::Control);

# IMPORTANT: Without this try .. catch block around the foreach loop, this code will likely fail
# IF someone tries to open too many PowerShell tabs in too short a period of time. If they do,
# the $psise.PowerShellTabs collection will be modified WHILE it is being iterated over. If
# this happens, then the event subscriber will be set to a FAILED state and will not execute
# as desired.
try {
# Iterate over PowerShell tabs and add the shortcut
foreach ($IseTab in $psise.PowerShellTabs) {
try {
[void] $IseTab.AddOnsMenu.Submenus.Add(“Close script tab”, $CloseFileScriptBlock, $KeyGesture);
}
catch {
# Write-Host -Object (‘Error occurred adding menu item and keyboard shortcut mapping for PowerShell tab: {0}’ -f $IseTab.DisplayName);
}
}
}
catch {
Write-Host -Object “Error occurred while iterating over PowerShell tabs.”;
}
}

# Unregister all event subscriptions. Can’t find a way to identify specifically which event
# subscription is being used for this purpose.
Get-EventSubscriber -SourceIdentifier IsePowerShellTabs -ErrorAction SilentlyContinue | Unregister-Event;
[void] (Register-ObjectEvent -InputObject $psise.PowerShellTabs -EventName CollectionChanged -Action $ScriptBlock -SourceIdentifier IsePowerShellTabs);
Invoke-Command -ScriptBlock $ScriptBlock;[/cc]