forked from pauldotknopf/JavaScriptViewEngine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFileWatcher.cs
More file actions
169 lines (148 loc) · 5.13 KB
/
FileWatcher.cs
File metadata and controls
169 lines (148 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
namespace JavaScriptViewEngine
{
/// <summary>
/// Handles watching for changes to files.
/// </summary>
public class FileWatcher : IFileWatcher
{
#region Fields
FileSystemWatcher _watcher;
ISet<string> _watchedFiles;
Timer _timer;
#endregion
#region Ctor
/// <summary>
/// Initializes a new instance of the <see cref="FileWatcher"/> class.
/// </summary>
public FileWatcher()
{
DebounceTimeout = 25;
}
#endregion
#region IFileWatcher
/// <summary>
/// Occurs when any watched files have changed (including renames and deletions).
/// </summary>
public event EventHandler Changed;
/// <summary>
/// Gets or sets the path to watch.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets the files to watch in the path. If no files are provided, every file in the
/// path is watched.
/// </summary>
public IEnumerable<string> Files
{
get { return _watchedFiles; }
set
{
if (value == null || !value.Any())
{
_watchedFiles = null;
}
else
{
_watchedFiles = new HashSet<string>(value.Select(name => name.ToLowerInvariant()));
}
}
}
/// <summary>
/// The debounce timeout before a file changed event is fired.
/// Give this a large value when you are watching a large directory that has many changes during a build process.
/// </summary>
public int DebounceTimeout { get; set; }
/// <summary>
/// Starts watching for changes in the specified path.
/// </summary>
/// <returns>Whether creation of the watcher was successful</returns>
public virtual bool Start()
{
if (Path == null)
{
throw new InvalidOperationException("Path must be set first");
}
_timer = new Timer(OnTimer, null, Timeout.Infinite, Timeout.Infinite);
try
{
// Attempt to initialise a FileSystemWatcher so we can recycle the JavaScript
// engine pool when files are changed.
_watcher = new FileSystemWatcher
{
Path = Path,
IncludeSubdirectories = true,
EnableRaisingEvents = true,
};
_watcher.Changed += OnFileChanged;
_watcher.Created += OnFileChanged;
_watcher.Deleted += OnFileChanged;
_watcher.Renamed += OnFileChanged;
return true;
}
catch (Exception ex)
{
// Can't use FileSystemWatcher (eg. not running in Full Trust)
Debug.WriteLine("Unable to initialise FileSystemWatcher: " + ex.Message);
return false;
}
}
/// <summary>
/// Stops watching for changes in the specified path.
/// </summary>
public virtual void Stop()
{
if (_watcher != null)
{
_watcher.Dispose();
_watcher = null;
}
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Stop();
}
#endregion
#region Methods
/// <summary>
/// Handles events fired when any files are changed.
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="args">The <see cref="FileSystemEventArgs"/> instance containing the event data</param>
protected virtual void OnFileChanged(object sender, FileSystemEventArgs args)
{
// If we're watching specific files, we need to check if the file that changed is one that we
// care about.
if (_watchedFiles != null && !_watchedFiles.Contains(args.FullPath.ToLowerInvariant()))
{
return;
}
Debug.WriteLine($"[RenderPool] Watched file '{args.FullPath}' changed");
// Use a timer so multiple changes only result in a single reset.
_timer.Change(DebounceTimeout, Timeout.Infinite);
}
/// <summary>
/// Handles events fired when any files are changed, and the changes have been debounced.
/// </summary>
/// <param name="state">The state.</param>
protected virtual void OnTimer(object state)
{
var changed = Changed;
changed?.Invoke(this, null);
}
#endregion
}
}