blob: 5c15c9e08854f4f88ccfa5791eb4c769b82e35e1 [file] [log] [blame]
// 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();
}
}