The PowerShell ISE is a handy tool for editing and testing your scripts, functions and modules. If you can't afford a good commercial editor then you should at least be using the ISE. One benefit of the ISE is that it has its own object model which means it is extensible. One thing I always missed in the ISE was a most recently used list. So I created one.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
I've been thinking about this for awhile and Oisin Grehan's recent post on an ISE Session module got me finally going. But rather than deal with restoring the ISE to a previous state, I simply wanted a list of files that I had recently opened. My solution was to take advantage of the CollectionChanged event of the $psISE.CurrentPowerShellTab.Files object. Whenever a file is added (or removed) this event is triggered, or "fired". Using the Register-ObjectEvent cmdlet, I created an event handler.
[cc lang="PowerShell"]
Register-ObjectEvent $psise.CurrentPowerShellTab.Files -EventName collectionchanged `
-sourceIdentifier "ISEChangeLog"-action {...
[/cc]
This is done within the ISE. The code in the Action script block will execute every time the collection changes. So that's the core of this code. When the event fires, there is a new object, $event, which contains information about the event. In my case, I can use the Sender property which will be a psISE File object for every file in the current collection. My solution was to get the file's name and path and write it to a CSV file. I also decided to include a datetime stamp. This is the date and time the file was added, not necessarily edited or saved.
[cc lang="PowerShell"]
#initialize an array to hold recent file paths
$recent=@()
#get a list of the $Count most recent files and add each path to the $recent array
Import-CSV $global:ISERecent | Sort LastEdit | Select -Last $count | foreach {$recent+=$_.Fullpath}
#iterate ISEFile objects but only those that have been saved, not Untitled and not already in the list.
$event.sender |
where {($_.IsSaved) -AND (-Not $_.IsUntitled) -AND ($recent -notContains $_.Fullpath)} |
foreach {
#add the filename to the list
"$($_.Fullpath),$($_.Displayname),$(Get-Date)" | out-file $global:ISERecent -append -Encoding ASCII
} #foreach
[/cc]
With this CSV, I could then get the last X number of items, my code has a default of 5, and add them to a custom menu under AddOns. My script includes a function to handle this.
[cc lang="PowerShell"]
Function Update-ISEMRU {
Param()
#remove the five most recent files
$mru=$psise.CurrentPowerShellTab.AddOnsMenu.Submenus | where {$_.Displayname -eq "Most Recent"}
$mru.submenus.clear()
#get the five most recent files and add them to the menu
#resort them so that the last closed file is on the top of the menu
Import-CSV $global:ISERecent | Sort LastEdit | Select -Last $count | Sort LastEdit -Descending | foreach {
#create a string with the filepath
[string]$cmd="`$psise.CurrentPowerShelltab.Files.Add(""$($_.fullpath)"")"
#turn it into a script block
$action=$executioncontext.InvokeCommand.NewScriptBlock($cmd)
#add it to the menu
$mru.submenus.Add($_.Displayname,$action,$null)
}
} #end function
[/cc]
In my ISE profile script, I call my script, passing it the number of most recent items I want to keep.
[cc lang="PowerShell"]
. c:\scripts\Set-ISEMostRecent.ps1 -count 7
[/cc]
When I run the ISE, I'll get a new menu like this.
To keep the CSV file from getting out of control, I have code to check it and if there are twice as many items as fit in the menu, then only the most recent X number are kept. This is the final script.
[cc lang="PowerShell"]
Param([int]$Count=5)
#Create the add-on menu
$mru=$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Most Recent",$null,$null)
$global:ISERecent=(Join-Path -path $env:Userprofile\Documents\WindowsPowershell -child "$($psise.CurrentPowerShellTab.displayname) ISE Most Recent.csv")
if (-Not (Test-Path $global:ISERecent))
{
#create the csv file header
"Fullpath,DisplayName,LastEdit" | out-File $global:ISERecent -Encoding ASCII
}
else
{
#check for log file and trim it to $Count items if there are more than twice the $count items
$save=Import-CSV -Path $global:ISERecent | Sort LastEdit -descending
if ($save.count -ge ($count*2))
{
$save[0..$count] | Export-Csv -Path $global:ISERecent -Encoding ASCII
}
Import-CSV $global:ISERecent | Select -Last $count | Sort LastEdit -descending | foreach {
#create a string with the filepath
[string]$cmd="`$psise.CurrentPowerShelltab.Files.Add(""$($_.fullpath)"")"
#turn it into a script block
$action=$executioncontext.InvokeCommand.NewScriptBlock($cmd)
#add it to the menu
$mru.submenus.Add($_.Displayname,$action,$null) | out-Null
}
}
#define a function to update the MRU
Function Update-ISEMRU {
Param()
#remove the five most recent files
$mru=$psise.CurrentPowerShellTab.AddOnsMenu.Submenus | where {$_.Displayname -eq "Most Recent"}
$mru.submenus.clear()
#get the five most recent files and add them to the menu
#resort them so that the last closed file is on the top of the menu
Import-CSV $global:ISERecent | Sort LastEdit | Select -Last $count | Sort LastEdit -Descending | foreach {
#create a string with the filepath
[string]$cmd="`$psise.CurrentPowerShelltab.Files.Add(""$($_.fullpath)"")"
#turn it into a script block
$action=$executioncontext.InvokeCommand.NewScriptBlock($cmd)
#add it to the menu
$mru.submenus.Add($_.Displayname,$action,$null)
}
} #end function
#register the event to watch for changes
Register-ObjectEvent $psise.CurrentPowerShellTab.Files -EventName collectionchanged `
-sourceIdentifier "ISEChangeLog"-action {
#initialize an array to hold recent file paths
$recent=@()
#get a list of the $Count most recent files and add each path to the $recent array
Import-CSV $global:ISERecent | Sort LastEdit | Select -Last $count | foreach {$recent+=$_.Fullpath}
#iterate ISEFile objects but only those that have been saved, not Untitled and not already in the list.
$event.sender |
where {($_.IsSaved) -AND (-Not $_.IsUntitled) -AND ($recent -notContains $_.Fullpath)} |
foreach {
#add the filename to the list
"$($_.Fullpath),$($_.Displayname),$(Get-Date)" | out-file $global:ISERecent -append -Encoding ASCII
} #foreach
#refresh the menu
Update-ISEMRU
} #close -action
[/cc]
If you open a second PowerShell tab, you'll get a new recent menu. However, if you rename the tab the menu updating will stop. But, the next time you open the ISE and open that 2nd tab, you'll get the menu back. The other limitation is that when you have an untitled script, right now I don't save them, when you save the script with a name it is not added to the most recent list. The next time you add open a file, as long as you new script is still open it will get added. But I have no good way now of handling new scripts. And if you save and close and untitled script, it won't make the list. I've experimented with some ugly workarounds but haven't found anything I like so I've left that out of this version.
You'll have to download the script and let me know what you think.
Wonderful Thanks ! Works like a charm. Regards, 32yr old network admin from Belgium. Cheers.
By the way Jeffrey : you don’t happen to know a way to hide the output of Register-ObjectEvent $psise.CurrentPowerShellTab.Files -EventName collectionchanged `
-sourceIdentifier “ISEChangeLog”-action {… ?
I’ve added your script to my profile file and it annoys me a bit that it outputs the the object ISEChangeLog of type PSEventJob.
Kind Regards, Vincent Deneve
I had that as well and just tried modifying profile. I think this does the trick.
. c:\scripts\Set-ISEMostRecent.ps1 -count 10 | Out-Null
Thanks ! I didn’t know the Out-Null command until now :p