|  | // 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(); | 
|  | } | 
|  | } |