/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/TestUtilities2/MockVsFileChangeEx.vb
264 строки
12 KB
Todd Grunke
Utilize IVsAsyncFileChangeEx2 (#70936)
06 дек 2023, 06:53
Не верифицирован
06 дек 2023, 06:53
92a930f
Код
Авторство
О чём код?
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Shell.Interop Imports Task = System.Threading.Tasks.Task Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Friend Class MockVsFileChangeEx Implements IVsFileChangeEx Implements IVsAsyncFileChangeEx2 Private ReadOnly _lock As New Object Private _watchedFiles As ImmutableList(Of WatchedEntity) = ImmutableList(Of WatchedEntity).Empty Private _watchedDirectories As ImmutableList(Of WatchedEntity) = ImmutableList(Of WatchedEntity).Empty Private _nextCookie As UInteger Public Function AdviseDirChange(pszDir As String, fWatchSubDir As Integer, pFCE As IVsFileChangeEvents, ByRef pvsCookie As UInteger) As Integer Implements IVsFileChangeEx.AdviseDirChange If fWatchSubDir = 0 Then Throw New NotImplementedException("Watching a single directory but not subdirectories is not implemented in this mock.") End If pvsCookie = AdviseDirectoryOrFileChange(_watchedDirectories, pszDir, pFCE) Return VSConstants.S_OK End Function Public Function AdviseFileChange(pszMkDocument As String, grfFilter As UInteger, pFCE As IVsFileChangeEvents, ByRef pvsCookie As UInteger) As Integer Implements IVsFileChangeEx.AdviseFileChange If (grfFilter And _VSFILECHANGEFLAGS.VSFILECHG_Time) <> _VSFILECHANGEFLAGS.VSFILECHG_Time Then Throw New NotImplementedException() End If pvsCookie = AdviseDirectoryOrFileChange(_watchedFiles, pszMkDocument, pFCE) Return VSConstants.S_OK End Function Private Function AdviseDirectoryOrFileChange(ByRef watchedList As ImmutableList(Of WatchedEntity), pszMkDocument As String, pFCE As IVsFileChangeEvents) As UInteger SyncLock _lock Dim cookie = _nextCookie watchedList = watchedList.Add(New WatchedEntity(cookie, pszMkDocument, DirectCast(pFCE, IVsFreeThreadedFileChangeEvents2))) _nextCookie += 1UI Return cookie End SyncLock End Function Public Function IgnoreFile(VSCOOKIE As UInteger, pszMkDocument As String, fIgnore As Integer) As Integer Implements IVsFileChangeEx.IgnoreFile Throw New NotImplementedException() End Function Public Function SyncFile(pszMkDocument As String) As Integer Implements IVsFileChangeEx.SyncFile Throw New NotImplementedException() End Function Public Function UnadviseDirChange(VSCOOKIE As UInteger) As Integer Implements IVsFileChangeEx.UnadviseDirChange SyncLock _lock _watchedDirectories = _watchedDirectories.RemoveAll(Function(t) t.Cookie = VSCOOKIE) Return VSConstants.S_OK End SyncLock End Function Public Function UnadviseFileChange(VSCOOKIE As UInteger) As Integer Implements IVsFileChangeEx.UnadviseFileChange SyncLock _lock _watchedFiles = _watchedFiles.RemoveAll(Function(t) t.Cookie = VSCOOKIE) Return VSConstants.S_OK End SyncLock End Function Public Sub FireUpdate(filename As String) FireUpdate(filename, _watchedFiles, _watchedDirectories) End Sub ''' <summary> ''' Raises a file change that raised after <paramref name="unsubscribingAction"/> is ran. ''' </summary> ''' <remarks> ''' File change notifications are inherently asynchronous -- it's always possible that a file change ''' notification may have been started for something we unsubscribe a moment later -- there's always a window of time ''' between the final check and the entry into Roslyn code which is unavoidable as long as notifications aren't called ''' by the shell under a lock, which they aren't for deadlock reasons. ''' </remarks> Public Sub FireStaleUpdate(filename As String, unsubscribingAction As Action) Dim watchedFiles = _watchedFiles Dim watchedDirectories = _watchedDirectories unsubscribingAction() FireUpdate(filename, watchedFiles, watchedDirectories) End Sub Private Shared Sub FireUpdate(filename As String, watchedFiles As ImmutableList(Of WatchedEntity), watchedDirectories As ImmutableList(Of WatchedEntity)) Dim actionsToFire As List(Of Action) = New List(Of Action)() For Each watchedFile In watchedFiles If String.Equals(watchedFile.Path, filename, StringComparison.OrdinalIgnoreCase) Then actionsToFire.Add(Sub() watchedFile.Sink.FilesChanged(1, {watchedFile.Path}, {CType(_VSFILECHANGEFLAGS.VSFILECHG_Time, UInteger)}) End Sub) End If Next For Each watchedDirectory In watchedDirectories If FileNameMatchesFilter(filename, watchedDirectory) Then actionsToFire.Add(Sub() watchedDirectory.Sink.DirectoryChangedEx2(watchedDirectory.Path, 1, {filename}, {CType(_VSFILECHANGEFLAGS.VSFILECHG_Time, UInteger)}) End Sub) End If Next If actionsToFire.Count > 0 Then For Each actionToFire In actionsToFire actionToFire() Next Else Throw New InvalidOperationException($"There is no subscription for file {filename}. Is the test authored correctly?") End If End Sub Private Shared Function FileNameMatchesFilter(filename As String, watchedDirectory As WatchedEntity) As Boolean If Not filename.StartsWith(watchedDirectory.Path, StringComparison.OrdinalIgnoreCase) Then Return False End If If watchedDirectory.ExtensionFilters Is Nothing Then ' We have no extension filter, so good Return True End If For Each extension In watchedDirectory.ExtensionFilters If filename.EndsWith(extension) Then Return True End If Next ' Didn't match the extension Return False End Function Public Function AdviseFileChangeAsync(filename As String, filter As _VSFILECHANGEFLAGS, sink As IVsFreeThreadedFileChangeEvents2, Optional cancellationToken As CancellationToken = Nothing) As Task(Of UInteger) Implements IVsAsyncFileChangeEx.AdviseFileChangeAsync Dim cookie As UInteger Marshal.ThrowExceptionForHR(AdviseFileChange(filename, CType(filter, UInteger), sink, cookie)) Return Task.FromResult(cookie) End Function Public Function AdviseFileChangesAsync(filenames As IReadOnlyCollection(Of String), filter As _VSFILECHANGEFLAGS, sink As IVsFreeThreadedFileChangeEvents2, cancellationToken As CancellationToken) As Task(Of UInteger()) Implements IVsAsyncFileChangeEx2.AdviseFileChangesAsync Dim cookies As New List(Of UInteger)() SyncLock _lock For Each filename In filenames Dim cookie As UInteger Marshal.ThrowExceptionForHR(AdviseFileChange(filename, CType(filter, UInteger), sink, cookie)) cookies.Add(cookie) Next End SyncLock Return Task.FromResult(cookies.ToArray()) End Function Public Function UnadviseFileChangeAsync(cookie As UInteger, Optional cancellationToken As CancellationToken = Nothing) As Task(Of String) Implements IVsAsyncFileChangeEx.UnadviseFileChangeAsync SyncLock _lock Dim path = _watchedFiles.FirstOrDefault(Function(t) t.Cookie = cookie).Path Marshal.ThrowExceptionForHR(UnadviseFileChange(cookie)) Return Task.FromResult(path) End SyncLock End Function Public Function UnadviseFileChangesAsync(cookies As IReadOnlyCollection(Of UInteger), Optional cancellationToken As CancellationToken = Nothing) As Task(Of String()) Implements IVsAsyncFileChangeEx.UnadviseFileChangesAsync Dim paths As New List(Of String)() SyncLock _lock For Each cookie In cookies Dim path = _watchedFiles.FirstOrDefault(Function(t) t.Cookie = cookie).Path Marshal.ThrowExceptionForHR(UnadviseFileChange(cookie)) paths.Add(path) Next End SyncLock Return Task.FromResult(paths.ToArray()) End Function Public Function AdviseDirChangeAsync(directory As String, watchSubdirectories As Boolean, sink As IVsFreeThreadedFileChangeEvents2, Optional cancellationToken As CancellationToken = Nothing) As Task(Of UInteger) Implements IVsAsyncFileChangeEx.AdviseDirChangeAsync Dim cookie As UInteger Marshal.ThrowExceptionForHR(AdviseDirChange(directory, If(watchSubdirectories, 1, 0), sink, cookie)) Return Task.FromResult(cookie) End Function Public Function UnadviseDirChangeAsync(cookie As UInteger, Optional cancellationToken As CancellationToken = Nothing) As Task(Of String) Implements IVsAsyncFileChangeEx.UnadviseDirChangeAsync SyncLock _lock Dim path = _watchedFiles.FirstOrDefault(Function(t) t.Cookie = cookie).Path Marshal.ThrowExceptionForHR(UnadviseFileChange(cookie)) Return Task.FromResult(path) End SyncLock End Function Public Function UnadviseDirChangesAsync(cookies As IReadOnlyCollection(Of UInteger), Optional cancellationToken As CancellationToken = Nothing) As Task(Of String()) Implements IVsAsyncFileChangeEx.UnadviseDirChangesAsync Dim paths As New List(Of String)() SyncLock _lock For Each cookie In cookies Dim path = _watchedFiles.FirstOrDefault(Function(t) t.Cookie = cookie).Path Marshal.ThrowExceptionForHR(UnadviseFileChange(cookie)) paths.Add(path) Next End SyncLock Return Task.FromResult(paths.ToArray()) End Function Public Function SyncFileAsync(filename As String, Optional cancellationToken As CancellationToken = Nothing) As Tasks.Task Implements IVsAsyncFileChangeEx.SyncFileAsync Throw New NotImplementedException() End Function Public Function IgnoreFileAsync(cookie As UInteger, filename As String, ignore As Boolean, Optional cancellationToken As CancellationToken = Nothing) As Tasks.Task Implements IVsAsyncFileChangeEx.IgnoreFileAsync Throw New NotImplementedException() End Function Public Function IgnoreDirAsync(directory As String, ignore As Boolean, Optional cancellationToken As CancellationToken = Nothing) As Tasks.Task Implements IVsAsyncFileChangeEx.IgnoreDirAsync Throw New NotImplementedException() End Function Public Function FilterDirectoryChangesAsync(cookie As UInteger, extensions As String(), cancellationToken As CancellationToken) As Task Implements IVsAsyncFileChangeEx.FilterDirectoryChangesAsync _watchedDirectories.FirstOrDefault(Function(t) t.Cookie = cookie).ExtensionFilters = extensions Return Task.CompletedTask End Function Public ReadOnly Property WatchedFileCount As Integer Get SyncLock _lock Return _watchedFiles.Count End SyncLock End Get End Property End Class Friend Class WatchedEntity Public ReadOnly Cookie As UInteger Public ReadOnly Path As String Public ReadOnly Sink As IVsFreeThreadedFileChangeEvents2 Public ExtensionFilters As String() Public Sub New(cookie As UInteger, path As String, sink As IVsFreeThreadedFileChangeEvents2) Me.Cookie = cookie Me.Path = path Me.Sink = sink End Sub End Class End Namespace