| // 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 = const AnalysisStatus._(false); |
| static const ANALYZING = const 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<Null> _completer = new Completer<Null>(); |
| |
| /** |
| * Return a [Future] that completes when [notify] is called at least once. |
| */ |
| Future<Null> get signal async { |
| await _completer.future; |
| _completer = new Completer<Null>(); |
| } |
| |
| /** |
| * 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 = new 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<Null> _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 ??= new Completer<Null>(); |
| } |
| |
| /** |
| * 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<Null> waitForIdle() { |
| return _idleCompleter?.future ?? new Future.value(); |
| } |
| } |