| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| /// The status of analysis. |
| class AnalysisStatus { |
| static const IDLE = AnalysisStatus._(false); |
| static const ANALYZING = AnalysisStatus._(true); |
| |
| final bool _analyzing; |
| |
| const AnalysisStatus._(this._analyzing); |
| |
| /// Return `true` if the scheduler is analyzing. |
| bool get isAnalyzing => _analyzing; |
| |
| /// Return `true` if the scheduler is idle. |
| bool get isIdle => !_analyzing; |
| |
| @override |
| String toString() => _analyzing ? 'analyzing' : 'idle'; |
| } |
| |
| /// [Monitor] can be used to wait for a signal. |
| /// |
| /// Signals are not queued, the client will receive exactly one signal |
| /// regardless of the number of [notify] invocations. The [signal] is reset |
| /// after completion and will not complete until [notify] is called next time. |
| class Monitor { |
| Completer<void> _completer = Completer<void>(); |
| |
| /// Return a [Future] that completes when [notify] is called at least once. |
| Future<void> get signal async { |
| await _completer.future; |
| _completer = Completer<void>(); |
| } |
| |
| /// Complete the [signal] future if it is not completed yet. It is safe to |
| /// call this method multiple times, but the [signal] will complete only once. |
| void notify() { |
| if (!_completer.isCompleted) { |
| _completer.complete(null); |
| } |
| } |
| } |
| |
| /// Helper for managing transitioning [AnalysisStatus]. |
| class StatusSupport { |
| /// The controller for the [stream]. |
| final _statusController = StreamController<AnalysisStatus>(); |
| |
| /// The last status sent to the [stream]. |
| AnalysisStatus _currentStatus = AnalysisStatus.IDLE; |
| |
| /// If non-null, a completer which should be completed on the next transition |
| /// to idle. |
| Completer<void> _idleCompleter; |
| |
| /// Return the last status sent to the [stream]. |
| AnalysisStatus get currentStatus => _currentStatus; |
| |
| /// Return the stream that produces [AnalysisStatus] events. |
| Stream<AnalysisStatus> get stream => _statusController.stream; |
| |
| /// Prepare for the scheduler to start analyzing, but do not notify the |
| /// [stream] yet. |
| /// |
| /// A call to [preTransitionToAnalyzing] has the same effect on [waitForIdle] |
| /// as a call to [transitionToAnalyzing], but it has no effect on the |
| /// [stream]. |
| void preTransitionToAnalyzing() { |
| _idleCompleter ??= Completer<void>(); |
| } |
| |
| /// Send a notification to the [stream] that the scheduler started analyzing. |
| void transitionToAnalyzing() { |
| if (_currentStatus != AnalysisStatus.ANALYZING) { |
| preTransitionToAnalyzing(); |
| _currentStatus = AnalysisStatus.ANALYZING; |
| _statusController.add(AnalysisStatus.ANALYZING); |
| } |
| } |
| |
| /// Send a notification to the [stream] stream that the scheduler is idle. |
| void transitionToIdle() { |
| if (_currentStatus != AnalysisStatus.IDLE) { |
| _currentStatus = AnalysisStatus.IDLE; |
| _statusController.add(AnalysisStatus.IDLE); |
| } |
| _idleCompleter?.complete(); |
| _idleCompleter = null; |
| } |
| |
| /// Return a future that will be completed the next time the status is idle. |
| /// |
| /// If the status is currently idle, the returned future will be signaled |
| /// immediately. |
| Future<void> waitForIdle() { |
| return _idleCompleter?.future ?? Future<void>.value(); |
| } |
| } |