@@ -133,8 +133,8 @@ class _MacOSDirectoryWatcher
133
133
: [canonicalEvent];
134
134
135
135
for (var event in events) {
136
- if (event.isCreate ) {
137
- if ( ! event.isDirectory ! ) {
136
+ switch (event.type ) {
137
+ case EventType .createFile :
138
138
// If we already know about the file, treat it like a modification.
139
139
// This can happen if a file is copied on top of an existing one.
140
140
// We'll see an ADD event for the latter file when from the user's
@@ -144,47 +144,55 @@ class _MacOSDirectoryWatcher
144
144
145
145
_emitEvent (type, path);
146
146
_files.add (path);
147
- continue ;
148
- }
149
-
150
- if (_files.containsDir (path)) continue ;
151
-
152
- var stream = Directory (path)
153
- .list (recursive: true )
154
- .ignoring <PathNotFoundException >();
155
- var subscription = stream.listen ((entity) {
156
- if (entity is Directory ) return ;
157
- if (_files.contains (path)) return ;
158
-
159
- _emitEvent (ChangeType .ADD , entity.path);
160
- _files.add (entity.path);
161
- }, cancelOnError: true );
162
- subscription.onDone (() {
163
- _listSubscriptions.remove (subscription);
164
- });
165
- subscription.onError (_emitError);
166
- _listSubscriptions.add (subscription);
167
- } else if (event.isModify) {
168
- assert (! event.isDirectory! );
169
- _emitEvent (ChangeType .MODIFY , path);
170
- } else {
171
- assert (event.isDelete);
172
- for (var removedPath in _files.remove (path)) {
173
- _emitEvent (ChangeType .REMOVE , removedPath);
174
- }
147
+
148
+ case EventType .createDirectory:
149
+ if (_files.containsDir (path)) continue ;
150
+
151
+ var stream = Directory (path)
152
+ .list (recursive: true )
153
+ .ignoring <PathNotFoundException >();
154
+ var subscription = stream.listen ((entity) {
155
+ if (entity is Directory ) return ;
156
+ if (_files.contains (path)) return ;
157
+
158
+ _emitEvent (ChangeType .ADD , entity.path);
159
+ _files.add (entity.path);
160
+ }, cancelOnError: true );
161
+ subscription.onDone (() {
162
+ _listSubscriptions.remove (subscription);
163
+ });
164
+ subscription.onError (_emitError);
165
+ _listSubscriptions.add (subscription);
166
+
167
+ case EventType .modifyFile:
168
+ _emitEvent (ChangeType .MODIFY , path);
169
+
170
+ case EventType .delete:
171
+ for (var removedPath in _files.remove (path)) {
172
+ _emitEvent (ChangeType .REMOVE , removedPath);
173
+ }
174
+
175
+ // Guaranteed not present by `_sortEvents`.
176
+ case EventType .moveFile:
177
+ case EventType .moveDirectory:
178
+ case EventType .modifyDirectory:
179
+ throw StateError (event.type.name);
175
180
}
176
181
}
177
182
});
178
183
}
179
184
180
185
/// Sort all the events in a batch into sets based on their path.
181
186
///
182
- /// A single input event may result in multiple events in the returned map;
183
- /// for example, a MOVE event becomes a DELETE event for the source and a
184
- /// CREATE event for the destination .
187
+ /// Events for `path` are discarded.
188
+ ///
189
+ /// Events under directories that are created or modified are discarded .
185
190
///
186
- /// The returned events won't contain any [FileSystemMoveEvent] s, nor will it
187
- /// contain any events relating to [path] .
191
+ /// Three event types are not expected on MacOS, if encountered they will be
192
+ /// dropped with an assert fail to signal in tests. The types are:
193
+ /// [EventType.moveFile] , [EventType.moveDirectory] and
194
+ /// [EventType.modifyDirectory] . See
195
+ /// https://github.com/dart-lang/sdk/issues/14806.
188
196
Map <String , Set <Event >> _sortEvents (List <Event > batch) {
189
197
var eventsForPaths = < String , Set <Event >> {};
190
198
@@ -193,25 +201,29 @@ class _MacOSDirectoryWatcher
193
201
// really deleted, that's handled by [_onDone].
194
202
batch = batch.where ((event) => event.path != path).toList ();
195
203
196
- // Events within directories that already have events are superfluous; the
197
- // directory's full contents will be examined anyway, so we ignore such
198
- // events. Emitting them could cause useless or out-of-order events.
199
- var directories = unionAll (batch. map (( event) {
200
- if (event.isDelete || ! event.isDirectory ! ) return < String > {};
201
- return event.paths ;
204
+ // Events within directories that already have create events are not needed
205
+ // as the directory's full content will be listed.
206
+ var createdDirectories = unionAll (batch. map ((event) {
207
+ return event.type == EventType .createDirectory
208
+ ? event.paths
209
+ : const < String > {} ;
202
210
}));
203
211
204
- bool isInModifiedDirectory (String path) =>
205
- directories .any ((dir) => path != dir && p.isWithin (dir, path));
212
+ bool isInCreatedDirectory (String path) =>
213
+ createdDirectories .any ((dir) => path != dir && p.isWithin (dir, path));
206
214
207
215
void addEvent (String path, Event event) {
208
- if (isInModifiedDirectory (path)) return ;
216
+ if (isInCreatedDirectory (path)) return ;
209
217
eventsForPaths.putIfAbsent (path, () => < Event > {}).add (event);
210
218
}
211
219
212
220
for (var event in batch) {
213
- // The Mac OS watcher doesn't emit move events. See issue 14806.
214
- assert (! event.isMove);
221
+ if (event.type == EventType .moveFile ||
222
+ event.type == EventType .moveDirectory ||
223
+ event.type == EventType .modifyDirectory) {
224
+ assert (false );
225
+ continue ;
226
+ }
215
227
addEvent (event.path, event);
216
228
}
217
229
@@ -233,6 +245,7 @@ class _MacOSDirectoryWatcher
233
245
// contradictory (e.g. because of a move).
234
246
if (batch.isEmpty) return null ;
235
247
248
+ var path = batch.first.path;
236
249
var type = batch.first.type;
237
250
var isDirectory = batch.first.isDirectory;
238
251
var hadModifyEvent = false ;
@@ -246,50 +259,54 @@ class _MacOSDirectoryWatcher
246
259
// safely assume the file was modified after a CREATE or before the
247
260
// REMOVE; otherwise there will also be a REMOVE or CREATE event
248
261
// (respectively) that will be contradictory.
249
- if (event.isModify ) {
262
+ if (event.type == EventType .modifyFile ) {
250
263
hadModifyEvent = true ;
251
264
continue ;
252
265
}
253
266
assert (event.isCreate || event.isDelete);
254
267
255
268
// If we previously thought this was a MODIFY, we now consider it to be a
256
269
// CREATE or REMOVE event. This is safe for the same reason as above.
257
- if (type == EventType .modify ) {
270
+ if (type == EventType .modifyFile ) {
258
271
type = event.type;
259
272
continue ;
260
273
}
261
274
262
275
// A CREATE event contradicts a REMOVE event and vice versa.
263
- assert (type == EventType .create || type == EventType .delete);
276
+ assert (type == EventType .createFile ||
277
+ type == EventType .createDirectory ||
278
+ type == EventType .delete);
264
279
if (type != event.type) return null ;
265
280
}
266
281
267
282
// If we got a CREATE event for a file we already knew about, that comes
268
283
// from FSEvents reporting an add that happened prior to the watch
269
284
// beginning. If we also received a MODIFY event, we want to report that,
270
285
// but not the CREATE.
271
- if (type == EventType .create &&
286
+ if (type == EventType .createFile &&
272
287
hadModifyEvent &&
273
- _files.contains (batch.first. path)) {
274
- type = EventType .modify ;
288
+ _files.contains (path)) {
289
+ type = EventType .modifyFile ;
275
290
}
276
291
277
292
switch (type) {
278
- case EventType .create :
293
+ case EventType .createDirectory :
279
294
// Issue 16003 means that a CREATE event for a directory can indicate
280
295
// that the directory was moved and then re-created.
281
296
// [_eventsBasedOnFileSystem] will handle this correctly by producing a
282
297
// DELETE event followed by a CREATE event if the directory exists.
283
- if (isDirectory! ) return null ;
284
- return Event .createFile (batch.first.path);
298
+ return null ;
299
+
300
+ case EventType .createFile:
285
301
case EventType .delete:
286
- return Event .delete (batch.first.path);
287
- case EventType .modify:
288
- return isDirectory!
289
- ? Event .modifyDirectory (batch.first.path)
290
- : Event .modifyFile (batch.first.path);
291
- default :
292
- throw StateError ('unreachable' );
302
+ case EventType .modifyFile:
303
+ return batch.firstWhere ((e) => e.type == type);
304
+
305
+ // Guaranteed not present by `_sortEvents`.
306
+ case EventType .moveFile:
307
+ case EventType .moveDirectory:
308
+ case EventType .modifyDirectory:
309
+ throw StateError (type.name);
293
310
}
294
311
}
295
312
0 commit comments