Skip to content

Commit ef29901

Browse files
author
Paul van Brenk
committed
Revert "Merge pull request #1903 from paulvanbrenk/fileWatcherUpdate"
This reverts commit 92b02af, reversing changes made to c7fc560.
1 parent 92b02af commit ef29901

File tree

9 files changed

+142
-140
lines changed

9 files changed

+142
-140
lines changed

Nodejs/Product/Nodejs/Nodejs.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@
8484
<Reference Include="envdte80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
8585
<EmbedInteropTypes>False</EmbedInteropTypes>
8686
</Reference>
87-
<Reference Include="Microsoft.VisualStudio.Shell.Interop.15.7.DesignTime, Version=15.7.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
88-
<EmbedInteropTypes>True</EmbedInteropTypes>
89-
</Reference>
9087
<Reference Include="microsoft.visualstudio.textmanager.interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
9188
<EmbedInteropTypes>True</EmbedInteropTypes>
9289
</Reference>

Nodejs/Product/Nodejs/SharedProject/AssemblyReferenceNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ protected virtual void OnAssemblyReferenceChangedOnDisk(object sender, FileChang
400400
}
401401

402402
// We only care about file deletes, so check for one before enumerating references.
403-
if (e.FileChange != WatcherChangeTypes.Deleted)
403+
if ((e.FileChangeFlag & _VSFILECHANGEFLAGS.VSFILECHG_Del) == 0)
404404
{
405405
return;
406406
}

Nodejs/Product/Nodejs/SharedProject/CommonProjectNode.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public CommonProjectNode(IServiceProvider serviceProvider, ImageList imageList)
8080
this.idleManager.OnIdle += this.OnIdle;
8181

8282
this.fileWatcher = new FileChangeManager(serviceProvider);
83-
this.fileWatcher.FileChangedOnDisk += this.FileChangedOnDisk;
83+
this.fileWatcher.FolderChangedOnDisk += this.FolderChangedOnDisk;
8484
}
8585

8686
public override int QueryService(ref Guid guidService, out object result)
@@ -462,7 +462,7 @@ protected override void Dispose(bool disposing)
462462
this.idleManager.OnIdle -= this.OnIdle;
463463
this.idleManager.Dispose();
464464

465-
this.fileWatcher.FileChangedOnDisk -= this.FileChangedOnDisk;
465+
this.fileWatcher.FolderChangedOnDisk -= this.FolderChangedOnDisk;
466466
this.fileWatcher.Dispose();
467467
}
468468

@@ -728,10 +728,17 @@ protected override ReferenceContainerNode CreateReferenceContainerNode()
728728
return new CommonReferenceContainerNode(this);
729729
}
730730

731-
private void FileChangedOnDisk(object sender, FileChangedOnDiskEventArgs e)
731+
private void FolderChangedOnDisk(object sender, FolderChangedEventArgs e)
732732
{
733733
var file = e.FileName;
734-
this.QueueFileSystemChanges(new FileSystemChange(this, e.FileChange, file));
734+
if (File.Exists(file) || Directory.Exists(file))
735+
{
736+
this.QueueFileSystemChanges(new FileSystemChange(this, WatcherChangeTypes.Changed, file));
737+
}
738+
else
739+
{
740+
this.QueueFileSystemChanges(new FileSystemChange(this, WatcherChangeTypes.Deleted, file));
741+
}
735742
}
736743

737744
private void QueueFileSystemChanges(params FileSystemChange[] changes)

Nodejs/Product/Nodejs/SharedProject/FileChangeManager.cs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@
99

1010
namespace Microsoft.VisualStudioTools.Project
1111
{
12+
public sealed class FolderChangedEventArgs : EventArgs
13+
{
14+
public readonly string FolderName;
15+
public readonly string FileName;
16+
public _VSFILECHANGEFLAGS FileChangeFlag;
17+
18+
public FolderChangedEventArgs(string folderName, string fileName, _VSFILECHANGEFLAGS fileChangeFlag)
19+
{
20+
this.FolderName = folderName;
21+
this.FileName = fileName;
22+
this.FileChangeFlag = fileChangeFlag;
23+
}
24+
}
25+
1226
/// <summary>
1327
/// This object is in charge of watching for changes to files and folders.
1428
/// </summary>
1529
internal sealed class FileChangeManager
1630
{
17-
private sealed class FileChangeEvents : IVsFreeThreadedFileChangeEvents2
31+
private sealed class FileChangeEvents : IVsFreeThreadedFileChangeEvents
1832
{
1933
private readonly FileChangeManager fileChangeManager;
2034

@@ -42,65 +56,58 @@ public int FilesChanged(uint numberOfFilesChanged, string[] filesChanged, uint[]
4256
throw new ArgumentNullException(nameof(flags));
4357
}
4458

45-
Debug.Assert(numberOfFilesChanged == filesChanged.Length && numberOfFilesChanged == flags.Length, "number of files changed doesn't match actual files reported.");
46-
47-
ProcessFileChanges(filesChanged, flags);
59+
for (var i = 0; i < numberOfFilesChanged; i++)
60+
{
61+
var fullFileName = Utilities.CanonicalizeFileName(filesChanged[i]);
62+
if (this.fileChangeManager.observedFiles.TryGetValue(fullFileName, out var value))
63+
{
64+
var (ItemID, FileChangeCookie) = value;
65+
this.fileChangeManager.FileChangedOnDisk?.Invoke(this, new FileChangedOnDiskEventArgs(fullFileName, ItemID, (_VSFILECHANGEFLAGS)flags[i]));
66+
}
67+
}
4868

4969
return VSConstants.S_OK;
5070
}
5171

72+
/// <summary>
73+
/// Notifies clients of changes made to a directory.
74+
/// </summary>
75+
/// <param name="directory">Name of the directory that had a change.</param>
76+
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
5277
public int DirectoryChanged(string directory)
5378
{
54-
// not called since we implement DirectoryChangedEx2
55-
return VSConstants.E_NOTIMPL;
79+
return VSConstants.S_OK;
5680
}
5781

5882
public int DirectoryChangedEx(string pszDirectory, string pszFile)
5983
{
60-
// not called since we implement DirectoryChangedEx2
61-
return VSConstants.E_NOTIMPL;
62-
}
63-
64-
/// <summary>
65-
/// Notifies clients of changes made to a directory.
66-
/// </summary>
67-
/// <param name="directory">Name of the directory that had a change.</param>
68-
/// <param name="numberOfFilesChanged">Number of files changed.</param>
69-
/// <param name="filesChanged">Array of file names.</param>
70-
/// <param name="flags">Array of flags indicating the type of changes. See _VSFILECHANGEFLAGS.</param>
71-
public int DirectoryChangedEx2(string directory, uint numberOfFilesChanged, string[] filesChanged, uint[] flags)
72-
{
73-
if (filesChanged == null)
84+
if (pszDirectory == null)
7485
{
75-
throw new ArgumentNullException(nameof(filesChanged));
86+
throw new ArgumentNullException(nameof(pszDirectory));
7687
}
7788

78-
if (flags == null)
89+
if (pszFile == null)
7990
{
80-
throw new ArgumentNullException(nameof(flags));
91+
throw new ArgumentNullException(nameof(pszFile));
8192
}
8293

83-
Debug.Assert(numberOfFilesChanged == filesChanged.Length && numberOfFilesChanged == flags.Length, "number of files changed doesn't match actual files reported.");
84-
85-
ProcessFileChanges(filesChanged, flags);
94+
this.fileChangeManager.FolderChangedOnDisk?.Invoke(this, new FolderChangedEventArgs(pszDirectory, pszFile,
95+
(_VSFILECHANGEFLAGS)0 /* default for now, until VS implements API that returns actual change */));
8696

8797
return VSConstants.S_OK;
8898
}
89-
90-
private void ProcessFileChanges(string[] filesChanged, uint[] flags)
91-
{
92-
for (var i = 0; i < filesChanged.Length; i++)
93-
{
94-
this.fileChangeManager.FileChangedOnDisk?.Invoke(this, new FileChangedOnDiskEventArgs(filesChanged[i], (_VSFILECHANGEFLAGS)flags[i]));
95-
}
96-
}
9799
}
98100

99101
/// <summary>
100102
/// Event that is raised when one of the observed files have changed on disk.
101103
/// </summary>
102104
public event EventHandler<FileChangedOnDiskEventArgs> FileChangedOnDisk;
103105

106+
/// <summary>
107+
/// Event that is raised when one of the observed folders have changed on disk.
108+
/// </summary>
109+
public event EventHandler<FolderChangedEventArgs> FolderChangedOnDisk;
110+
104111
/// <summary>
105112
/// Reference to the FileChange service.
106113
/// </summary>
@@ -110,7 +117,7 @@ private void ProcessFileChanges(string[] filesChanged, uint[] flags)
110117
/// Maps between the observed file identified by its filename (in canonicalized form) and the cookie used for subscribing
111118
/// to the events.
112119
/// </summary>
113-
private readonly ConcurrentDictionary<string, uint> observedFiles = new ConcurrentDictionary<string, uint>();
120+
private readonly ConcurrentDictionary<string, (uint ItemID, uint FileChangeCookie)> observedFiles = new ConcurrentDictionary<string, (uint, uint)>();
114121

115122
/// <summary>
116123
/// Maps between the observer folder identified by its foldername (in canonicalized form) and the cookie used for subscribing
@@ -164,9 +171,9 @@ public void Dispose()
164171
this.disposed = true;
165172

166173
// Unsubscribe from the observed source files.
167-
foreach (var fileChangeCookie in this.observedFiles.Values)
174+
foreach (var (ItemID, FileChangeCookie) in this.observedFiles.Values)
168175
{
169-
var hr = this.fileChangeService.UnadviseFileChange(fileChangeCookie);
176+
var hr = this.fileChangeService.UnadviseFileChange(FileChangeCookie);
170177
// don't want to crash VS during cleanup
171178
Debug.Assert(ErrorHandler.Succeeded(hr), "UnadviseFileChange failed");
172179
if (ErrorHandler.Failed(hr)) { break; }
@@ -188,12 +195,23 @@ public void Dispose()
188195
this.observedFolders.Clear();
189196
}
190197

198+
/// <summary>
199+
/// Observe when the given file is updated on disk. In this case we do not care about the item id that represents the file in the hierarchy.
200+
/// </summary>
201+
/// <param name="fileName">File to observe.</param>
202+
public void ObserveFile(string fileName)
203+
{
204+
this.CheckDisposed();
205+
206+
this.ObserveFile(fileName, VSConstants.VSITEMID_NIL);
207+
}
208+
191209
/// <summary>
192210
/// Observe when the given file is updated on disk.
193211
/// </summary>
194212
/// <param name="fileName">File to observe.</param>
195213
/// <param name="id">The item id of the item to observe.</param>
196-
public void ObserveFile(string fileName)
214+
public void ObserveFile(string fileName, uint id)
197215
{
198216
this.CheckDisposed();
199217

@@ -209,7 +227,7 @@ public void ObserveFile(string fileName)
209227
ErrorHandler.ThrowOnFailure(this.fileChangeService.AdviseFileChange(fullFileName, (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Del), this.fileChangeEvents, out var fileChangeCookie));
210228

211229
// Remember that we're observing this file (used in FilesChanged event handler)
212-
this.observedFiles.TryAdd(fullFileName, fileChangeCookie);
230+
this.observedFiles.TryAdd(fullFileName, (id, fileChangeCookie));
213231
}
214232
}
215233

@@ -273,10 +291,13 @@ public bool StopObservingFile(string fileName)
273291
// Remove the file from our observed list. It's important that this is done before the call to
274292
// UnadviseFileChange, because for some reason, the call to UnadviseFileChange can trigger a
275293
// FilesChanged event, and we want to be able to filter that event away.
276-
if (this.observedFiles.TryRemove(fullFileName, out var fileChangeCookie))
294+
if (this.observedFiles.TryRemove(fullFileName, out var value))
277295
{
296+
// Get the cookie that was used for this.observedItems to this file.
297+
var (ItemID, FileChangeCookie) = value;
298+
278299
// Stop observing the file
279-
return ErrorHandler.Succeeded(this.fileChangeService.UnadviseFileChange(fileChangeCookie));
300+
return ErrorHandler.Succeeded(this.fileChangeService.UnadviseFileChange(FileChangeCookie));
280301
}
281302
return false;
282303
}

Nodejs/Product/Nodejs/SharedProject/StructuresEnums.cs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4-
using System.Diagnostics;
5-
using System.IO;
64
using System.Runtime.InteropServices;
75
using Microsoft.VisualStudio.Shell.Interop;
86

@@ -298,39 +296,26 @@ public sealed class FileChangedOnDiskEventArgs : EventArgs
298296
/// </summary>
299297
public readonly string FileName;
300298

299+
/// <summary>
300+
/// The item ide of the file that has changed.
301+
/// </summary>
302+
public readonly uint ItemID;
303+
301304
/// <summary>
302305
/// The reason the file has changed on disk.
303306
/// </summary>
304-
public readonly WatcherChangeTypes FileChange;
307+
public readonly _VSFILECHANGEFLAGS FileChangeFlag;
305308

306309
/// <summary>
307310
/// Constructs a new event args.
308311
/// </summary>
309312
/// <param name="fileName">File name that was changed on disk.</param>
310313
/// <param name="id">The item id of the file that was changed on disk.</param>
311-
internal FileChangedOnDiskEventArgs(string fileName, _VSFILECHANGEFLAGS flag)
314+
internal FileChangedOnDiskEventArgs(string fileName, uint id, _VSFILECHANGEFLAGS flag)
312315
{
313316
this.FileName = fileName;
314-
this.FileChange = ConvertVSFILECHANGEFLAGS((uint)flag);
315-
}
316-
317-
private static WatcherChangeTypes ConvertVSFILECHANGEFLAGS(uint flag)
318-
{
319-
if ((flag & (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Size | _VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Attr)) != 0)
320-
{
321-
return WatcherChangeTypes.Changed;
322-
}
323-
if ((flag & (uint)_VSFILECHANGEFLAGS.VSFILECHG_Add) != 0)
324-
{
325-
return WatcherChangeTypes.Created;
326-
}
327-
if ((flag & (uint)_VSFILECHANGEFLAGS.VSFILECHG_Del) != 0)
328-
{
329-
return WatcherChangeTypes.Deleted;
330-
}
331-
332-
Debug.Fail($"Unexpected value for the file changed event: \'{flag}\'");
333-
return WatcherChangeTypes.Changed;
317+
this.ItemID = id;
318+
this.FileChangeFlag = flag;
334319
}
335320
}
336321
}

Nodejs/Product/TestAdapterImpl/TestAdapterImpl.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@
4141
<Reference Include="envdte90, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
4242
<EmbedInteropTypes>False</EmbedInteropTypes>
4343
</Reference>
44-
<Reference Include="Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
45-
<EmbedInteropTypes>True</EmbedInteropTypes>
46-
</Reference>
47-
<Reference Include="Microsoft.VisualStudio.Shell.Interop.15.7.DesignTime, Version=15.7.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
48-
<EmbedInteropTypes>True</EmbedInteropTypes>
49-
</Reference>
5044
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel">
5145
<HintPath>$(DevEnvDir)CommonExtensions\Microsoft\TestWindow\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
5246
<Private>False</Private>

Nodejs/Product/TestAdapterImpl/TestContainerDiscoverer.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,13 @@ public IEnumerable<ITestContainer> GetTestContainers(IVsProject project)
360360
var ft = File.GetLastWriteTimeUtc(filePath);
361361
return (ft > latest) ? ft : latest;
362362
}
363-
catch (Exception exc) when (exc is UnauthorizedAccessException || exc is ArgumentException || exc is IOException)
363+
catch (UnauthorizedAccessException)
364+
{
365+
}
366+
catch (ArgumentException)
367+
{
368+
}
369+
catch (IOException)
364370
{
365371
}
366372
return latest;
@@ -524,13 +530,13 @@ private void OnProjectItemChanged(object sender, TestFileChangedEventArgs e)
524530
if (e != null && ShouldDiscover(e.File))
525531
{
526532
string root = null;
527-
var project = e.Project ?? GetTestProjectFromFile(e.File);
528533
switch (e.ChangedReason)
529534
{
530-
case WatcherChangeTypes.Created:
531-
if (project.IsTestProject(Guids.NodejsBaseProjectFactory))
535+
case TestFileChangedReason.Added:
536+
Debug.Assert(e.Project != null);
537+
if (e.Project.IsTestProject(Guids.NodejsBaseProjectFactory))
532538
{
533-
root = project.GetProjectHome();
539+
root = e.Project.GetProjectHome();
534540

535541
if (!string.IsNullOrEmpty(root) && CommonUtils.IsSubpathOf(root, e.File))
536542
{
@@ -542,10 +548,12 @@ private void OnProjectItemChanged(object sender, TestFileChangedEventArgs e)
542548
this.testFilesUpdateWatcher.AddFileWatch(e.File);
543549
}
544550

545-
OnTestContainersChanged(project);
551+
OnTestContainersChanged(e.Project);
546552
}
547553
break;
548-
case WatcherChangeTypes.Deleted:
554+
case TestFileChangedReason.Removed:
555+
Debug.Assert(e.Project != null);
556+
549557
if (this.fileRootMap.TryGetValue(e.File, out root))
550558
{
551559
this.fileRootMap.Remove(e.File);
@@ -563,15 +571,15 @@ private void OnProjectItemChanged(object sender, TestFileChangedEventArgs e)
563571
// track the last delete as an update as our file system scan won't see it
564572
this.lastWrite = DateTime.Now.ToUniversalTime();
565573

566-
OnTestContainersChanged(project);
574+
OnTestContainersChanged(e.Project);
567575
break;
568576

569577
// Dev12 renames files instead of overwriting them when
570578
// saving, so we need to listen for renames where the new
571579
// path is part of the project.
572-
case WatcherChangeTypes.Renamed:
573-
case WatcherChangeTypes.Changed:
574-
OnTestContainersChanged(project);
580+
case TestFileChangedReason.Renamed:
581+
case TestFileChangedReason.Changed:
582+
OnTestContainersChanged(GetTestProjectFromFile(e.File));
575583
break;
576584
}
577585
}
@@ -592,7 +600,7 @@ private IVsProject GetTestProjectFromFile(string filename)
592600
CommonUtils.IsSamePath(projectPath, filename) ||
593601
(hierarchy != null &&
594602
project.IsTestProject(Guids.NodejsBaseProjectFactory) &&
595-
ErrorHandler.Succeeded(hierarchy.ParseCanonicalName(filename, out _))))
603+
ErrorHandler.Succeeded(hierarchy.ParseCanonicalName(filename, out var itemid))))
596604
{
597605
return project;
598606
}

0 commit comments

Comments
 (0)