Skip to content

Instantly share code, notes, and snippets.

@gpduck
Created May 6, 2014 05:05
Show Gist options
  • Save gpduck/659dee8053cff25e095e to your computer and use it in GitHub Desktop.
Save gpduck/659dee8053cff25e095e to your computer and use it in GitHub Desktop.
A crude attempt at providing file change notifications for ISE.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic") > $Null
$Watchers = @{}
$TabCollectionChangedEvent = {
$EventArgs.NewItems | %{
Register-ObjectEvent -InputObject $_.Files -EventName CollectionChanged -Action $FileCollectionChangedEvent
}
}
$FileCollectionChangedEvent = {
Write-Debug "FileCollectionChanged"
if($EventArgs.Action -eq "Add") {
$NewFile = $EventArgs.NewItems[0]
$ParentFolder = Split-Path -Path $NewFile.FullPath -Parent
if(!$Watchers.ContainsKey($ParentFolder)) {
Write-Debug "Adding Watcher"
$Watcher = New-Object System.IO.FileSystemWatcher($ParentFolder)
$Watcher.NotifyFilter = [System.IO.NotifyFilters]::Size -bor [System.IO.NotifyFilters]::LastWrite
$Watcher.EnableRaisingEvents = $true
$Watchers[$ParentFolder] = [PSCustomObject]@{
Watcher = $Watcher;
WatchedFiles = (New-Object Collections.ArrayList)
EventSubscriber = $null;
}
Write-Debug $Watchers[$ParentFolder].WatchedFiles.GetType()
$Watchers[$ParentFolder].WatchedFiles.Add($NewFile.FullPath)
$Watchers[$ParentFolder].EventSubscriber = Register-ObjectEvent -InputObject $Watcher -EventName Changed -Action $FileChangedEvent
} else {
$Watchers[$ParentFolder].WatchedFiles.Add($NewFile.FullPath)
}
} elseif($EventArgs.Action -eq "Remove") {
$OldFile = $EventArgs.OldItems[0]
Write-Debug "Removing file $($OldFile.FullPath)"
$ParentFolder = Split-Path -Path $OldFile.FullPath -Parent
if($Watchers.ContainsKey($ParentFolder)) {
$Watchers[$ParentFolder].WatchedFiles.Remove($OldFile.FullPath)
if($Watchers[$ParentFolder].WatchedFiles.Count -eq 0) {
Unregister-Event -SourceIdentifier $Watchers[$ParentFolder].EventSubscriber.Name
$Watchers[$ParentFolder].Watcher.EnableRaisingEvents = $false
$Watchers[$ParentFolder].Watcher.Dispose()
if((Get-Job -Name $Watchers[$ParentFolder].EventSubscriber.Name).State -ne "Failed") {
Remove-Job -Name $Watchers[$ParentFolder].EventSubscriber.Name
}
$Watchers.Remove($ParentFolder)
}
}
}
}
$FileChangedEvent = {
Write-Debug "FileChanged $($EventArgs.Action)"
$ChangedFile = $EventArgs.FullPath
$ParentFolder = Split-Path -Path $ChangedFile -Parent
Write-Debug $psISE.CurrentPowershellTab.Files.SelectedFile.FullPath
Write-Debug $ChangedFile
# Don't provide notifications for the current file. This is the only way I could prevent self notifiying when ISE saved a file.
if($Watchers[$ParentFolder].WatchedFiles -contains $ChangedFile -and $psISE.CurrentPowershellTab.Files.SelectedFile.FullPath -ne $ChangedFile) {
$psISE.PowerShellTabs | %{
$Tab = $_
$FilesToReload = $Tab.Files | ?{$_.FullPath -eq $ChangedFile}
$FilesToReload | %{
$ShouldChange = [Microsoft.VisualBasic.Interaction]::MsgBox("File $($_.Fullpath) has changed outside the editor. Would you like to reload?","YesNo,Question","File Changed")
if($ShouldChange -eq "Yes") {
#I don't like the Text= method becuase it marks the file as changed
$_.Editor.Text = [System.IO.File]::ReadAllText($_.FullPath)
#I don't like the remove/add method because it re-orders the tabs and flashes
#$Tab.Files.Remove($_)
#$Tab.Files.Add($_.FullPath)
}
}
}
}
}
Register-ObjectEvent -InputObject $psISE.PowerShellTabs -EventName CollectionChanged -Action $TabCollectionChangedEvent > $null
$psise.PowerShellTabs | %{
Register-ObjectEvent -InputObject $_.Files -EventName CollectionChanged -Action $FileCollectionChangedEvent > $null
}
@gpduck
Copy link
Author

gpduck commented May 6, 2014

I'm getting double events so you get 2 popups, and then it "manually" updates the contents of the editor from disk, so ISE marks it as modified :(

And it only works for files other than the current one to prevent the ISE saving from triggering the change event.

@dfinke
Copy link

dfinke commented May 6, 2014

Thanks for working this up Chris.

Yeah, I was working with the FileSystemWatcher in .NET in PowerShell and getting double events and there are many who get the same issues in C#. It'd be nice to have a solid watcher to use from PS. We could build tools like grunt and gulp in the JavaScript world.

I think it's something for Microsoft to solve in ISE. Then again, the ISE editor is the Visual Studio editor. Too bad this isn't baked in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment