Skip to content

Commit 0f50f06

Browse files
authored
Add package-private extension type Event on FileSystemEvent. (#2200)
1 parent d67ab97 commit 0f50f06

File tree

5 files changed

+104
-33
lines changed

5 files changed

+104
-33
lines changed

.github/workflows/watcher.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
fail-fast: false
5555
matrix:
5656
os: [ubuntu-latest, macos-latest, windows-latest]
57-
sdk: [3.1, dev]
57+
sdk: [3.3, dev]
5858
steps:
5959
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
6060
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c

pkgs/watcher/lib/src/directory_watcher/linux.dart

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:io';
88
import 'package:async/async.dart';
99

1010
import '../directory_watcher.dart';
11+
import '../event.dart';
1112
import '../path_set.dart';
1213
import '../resubscribable.dart';
1314
import '../utils.dart';
@@ -81,7 +82,7 @@ class _LinuxDirectoryWatcher
8182
})));
8283

8384
// Batch the inotify changes together so that we can dedup events.
84-
var innerStream = _nativeEvents.stream.batchEvents();
85+
var innerStream = _nativeEvents.stream.map(Event.new).batchEvents();
8586
_listen(innerStream, _onBatch,
8687
onError: (Object error, StackTrace stackTrace) {
8788
// Guarantee that ready always completes.
@@ -145,7 +146,7 @@ class _LinuxDirectoryWatcher
145146
}
146147

147148
/// The callback that's run when a batch of changes comes in.
148-
void _onBatch(List<FileSystemEvent> batch) {
149+
void _onBatch(List<Event> batch) {
149150
var files = <String>{};
150151
var dirs = <String>{};
151152
var changed = <String>{};
@@ -162,30 +163,40 @@ class _LinuxDirectoryWatcher
162163

163164
changed.add(event.path);
164165

165-
if (event is FileSystemMoveEvent) {
166-
files.remove(event.path);
167-
dirs.remove(event.path);
168-
169-
var destination = event.destination;
170-
if (destination == null) continue;
171-
172-
changed.add(destination);
173-
if (event.isDirectory) {
174-
files.remove(destination);
175-
dirs.add(destination);
176-
} else {
177-
files.add(destination);
178-
dirs.remove(destination);
179-
}
180-
} else if (event is FileSystemDeleteEvent) {
181-
files.remove(event.path);
182-
dirs.remove(event.path);
183-
} else if (event.isDirectory) {
184-
files.remove(event.path);
185-
dirs.add(event.path);
186-
} else {
187-
files.add(event.path);
188-
dirs.remove(event.path);
166+
switch (event.type) {
167+
case EventType.moveFile:
168+
files.remove(event.path);
169+
dirs.remove(event.path);
170+
var destination = event.destination;
171+
if (destination != null) {
172+
changed.add(destination);
173+
files.add(destination);
174+
dirs.remove(destination);
175+
}
176+
177+
case EventType.moveDirectory:
178+
files.remove(event.path);
179+
dirs.remove(event.path);
180+
var destination = event.destination;
181+
if (destination != null) {
182+
changed.add(destination);
183+
files.remove(destination);
184+
dirs.add(destination);
185+
}
186+
187+
case EventType.delete:
188+
files.remove(event.path);
189+
dirs.remove(event.path);
190+
191+
case EventType.createDirectory:
192+
case EventType.modifyDirectory:
193+
files.remove(event.path);
194+
dirs.add(event.path);
195+
196+
case EventType.createFile:
197+
case EventType.modifyFile:
198+
files.add(event.path);
199+
dirs.remove(event.path);
189200
}
190201
}
191202

pkgs/watcher/lib/src/event.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
/// Extension type replacing [FileSystemEvent] for `package:watcher` internal
8+
/// use.
9+
///
10+
/// The [FileSystemDeleteEvent] subclass of [FileSystemEvent] does something
11+
/// surprising for `isDirectory`: it always returns `false`. The constructor
12+
/// accepts a boolean called `isDirectory` but discards it.
13+
///
14+
/// So, this extension type hides `isDirectory` and instead provides an
15+
/// [EventType] enum with the seven types of event actually used.
16+
extension type Event(FileSystemEvent event) {
17+
/// See [FileSystemEvent.path].
18+
String get path => event.path;
19+
20+
EventType get type {
21+
switch (event.type) {
22+
case FileSystemEvent.create:
23+
return event.isDirectory
24+
? EventType.createDirectory
25+
: EventType.createFile;
26+
case FileSystemEvent.delete:
27+
return EventType.delete;
28+
case FileSystemEvent.modify:
29+
return event.isDirectory
30+
? EventType.modifyDirectory
31+
: EventType.modifyFile;
32+
case FileSystemEvent.move:
33+
return event.isDirectory ? EventType.moveDirectory : EventType.moveFile;
34+
default:
35+
throw StateError('Invalid event type ${event.type}.');
36+
}
37+
}
38+
39+
/// See [FileSystemMoveEvent.destination].
40+
///
41+
/// For other types of event, always `null`.
42+
String? get destination => event.type == FileSystemEvent.move
43+
? (event as FileSystemMoveEvent).destination
44+
: null;
45+
}
46+
47+
/// See [FileSystemEvent.type].
48+
///
49+
/// This additionally encodes [FileSystemEvent.isDirectory], which is specified
50+
/// for all event types except deletes.
51+
enum EventType {
52+
delete,
53+
createFile,
54+
createDirectory,
55+
modifyFile,
56+
modifyDirectory,
57+
moveFile,
58+
moveDirectory;
59+
}

pkgs/watcher/lib/src/file_watcher/native.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:async';
66
import 'dart:io';
77

8+
import '../event.dart';
89
import '../file_watcher.dart';
910
import '../resubscribable.dart';
1011
import '../utils.dart';
@@ -33,7 +34,7 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
3334
Future<void> get ready => _readyCompleter.future;
3435
final _readyCompleter = Completer<void>();
3536

36-
StreamSubscription<List<FileSystemEvent>>? _subscription;
37+
StreamSubscription<List<Event>>? _subscription;
3738

3839
/// On MacOS only, whether the file existed on startup.
3940
bool? _existedAtStartup;
@@ -50,7 +51,7 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
5051
var file = File(path);
5152

5253
// Batch the events together so that we can dedupe them.
53-
var stream = file.watch().batchEvents();
54+
var stream = file.watch().map(Event.new).batchEvents();
5455

5556
if (Platform.isMacOS) {
5657
var existedAtStartupFuture = file.exists();
@@ -65,8 +66,8 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
6566
onError: _eventsController.addError, onDone: _onDone);
6667
}
6768

68-
void _onBatch(List<FileSystemEvent> batch) {
69-
if (batch.any((event) => event.type == FileSystemEvent.delete)) {
69+
void _onBatch(List<Event> batch) {
70+
if (batch.any((event) => event.type == EventType.delete)) {
7071
// If the file is deleted, the underlying stream will close. We handle
7172
// emitting our own REMOVE event in [_onDone].
7273
return;
@@ -77,7 +78,7 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
7778
// created just before the `watch`. If the file existed at startup then it
7879
// should be ignored.
7980
if (_existedAtStartup! &&
80-
batch.every((event) => event.type == FileSystemEvent.create)) {
81+
batch.every((event) => event.type == EventType.createFile)) {
8182
return;
8283
}
8384
}

pkgs/watcher/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repository: https://github.com/dart-lang/tools/tree/main/pkgs/watcher
77
issue_tracker: https://github.com/dart-lang/tools/labels/package%3Awatcher
88

99
environment:
10-
sdk: ^3.1.0
10+
sdk: ^3.3.0
1111

1212
dependencies:
1313
async: ^2.5.0

0 commit comments

Comments
 (0)