| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright (c) 2015, 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 library watcher.file_watcher.native; | 
|  | 6 | 
|  | 7 import 'dart:async'; | 
|  | 8 import 'dart:io'; | 
|  | 9 | 
|  | 10 import '../file_watcher.dart'; | 
|  | 11 import '../resubscribable.dart'; | 
|  | 12 import '../utils.dart'; | 
|  | 13 import '../watch_event.dart'; | 
|  | 14 | 
|  | 15 /// Uses the native file system notifications to watch for filesystem events. | 
|  | 16 /// | 
|  | 17 /// Single-file notifications are much simpler than those for multiple files, so | 
|  | 18 /// this doesn't need to be split out into multiple OS-specific classes. | 
|  | 19 class NativeFileWatcher extends ResubscribableWatcher implements FileWatcher { | 
|  | 20   NativeFileWatcher(String path) | 
|  | 21       : super(path, () => new _NativeFileWatcher(path)); | 
|  | 22 } | 
|  | 23 | 
|  | 24 class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher { | 
|  | 25   final String path; | 
|  | 26 | 
|  | 27   Stream<WatchEvent> get events => _eventsController.stream; | 
|  | 28   final _eventsController = new StreamController<WatchEvent>.broadcast(); | 
|  | 29 | 
|  | 30   bool get isReady => _readyCompleter.isCompleted; | 
|  | 31 | 
|  | 32   Future get ready => _readyCompleter.future; | 
|  | 33   final _readyCompleter = new Completer(); | 
|  | 34 | 
|  | 35   StreamSubscription _subscription; | 
|  | 36 | 
|  | 37   _NativeFileWatcher(this.path) { | 
|  | 38     _listen(); | 
|  | 39 | 
|  | 40     // We don't need to do any initial set-up, so we're ready immediately after | 
|  | 41     // being listened to. | 
|  | 42     _readyCompleter.complete(); | 
|  | 43   } | 
|  | 44 | 
|  | 45   void _listen() { | 
|  | 46     // Batch the events together so that we can dedup them. | 
|  | 47     _subscription = new File(path).watch() | 
|  | 48         .transform(new BatchedStreamTransformer<FileSystemEvent>()) | 
|  | 49         .listen(_onBatch, onError: _eventsController.addError, onDone: _onDone); | 
|  | 50   } | 
|  | 51 | 
|  | 52   void _onBatch(List<FileSystemEvent> batch) { | 
|  | 53     if (batch.any((event) => event.type == FileSystemEvent.DELETE)) { | 
|  | 54       // If the file is deleted, the underlying stream will close. We handle | 
|  | 55       // emitting our own REMOVE event in [_onDone]. | 
|  | 56       return; | 
|  | 57     } | 
|  | 58 | 
|  | 59     _eventsController.add(new WatchEvent(ChangeType.MODIFY, path)); | 
|  | 60   } | 
|  | 61 | 
|  | 62   _onDone() async { | 
|  | 63     var fileExists = await new File(path).exists(); | 
|  | 64 | 
|  | 65     // Check for this after checking whether the file exists because it's | 
|  | 66     // possible that [close] was called between [File.exists] being called and | 
|  | 67     // it completing. | 
|  | 68     if (_eventsController.isClosed) return; | 
|  | 69 | 
|  | 70     if (fileExists) { | 
|  | 71       // If the file exists now, it was probably removed and quickly replaced; | 
|  | 72       // this can happen for example when another file is moved on top of it. | 
|  | 73       // Re-subscribe and report a modify event. | 
|  | 74       _eventsController.add(new WatchEvent(ChangeType.MODIFY, path)); | 
|  | 75       _listen(); | 
|  | 76     } else { | 
|  | 77       _eventsController.add(new WatchEvent(ChangeType.REMOVE, path)); | 
|  | 78       close(); | 
|  | 79     } | 
|  | 80   } | 
|  | 81 | 
|  | 82   void close() { | 
|  | 83     if (_subscription != null) _subscription.cancel(); | 
|  | 84     _subscription = null; | 
|  | 85     _eventsController.close(); | 
|  | 86   } | 
|  | 87 } | 
| OLD | NEW | 
|---|