blob: 6d46f4505e543e650f37fa05e3c8f6181f893300 [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.
sealed class AnalysisStatus {
const AnalysisStatus._();
bool get isIdle => !isWorking;
bool get isWorking;
}
final class AnalysisStatusIdle extends AnalysisStatus {
const AnalysisStatusIdle._() : super._();
@override
bool get isWorking => false;
@override
String toString() => 'idle';
}
final class AnalysisStatusWorking extends AnalysisStatus {
/// Will complete when we switch to [AnalysisStatusIdle].
final Completer<void> _idleCompleter = Completer<void>();
AnalysisStatusWorking._() : super._();
@override
bool get isWorking => true;
@override
String toString() => 'working';
}
/// [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 {
final StreamController<Object> _eventsController;
/// The last status sent to [_eventsController].
AnalysisStatus _currentStatus = const AnalysisStatusIdle._();
StatusSupport({required StreamController<Object> eventsController})
: _eventsController = eventsController;
/// The last status sent to [_eventsController].
AnalysisStatus get currentStatus => _currentStatus;
/// If the current status is not [AnalysisStatusIdle] yet, set the
/// current status to it, and send it to the stream.
void transitionToIdle() {
if (_currentStatus case AnalysisStatusWorking status) {
var newStatus = const AnalysisStatusIdle._();
_currentStatus = newStatus;
_eventsController.add(newStatus);
status._idleCompleter.complete();
}
}
/// If the current status is not [AnalysisStatusWorking] yet, set the
/// current status to it, and send it to the stream.
void transitionToWorking() {
if (_currentStatus is AnalysisStatusIdle) {
var newStatus = AnalysisStatusWorking._();
_currentStatus = newStatus;
_eventsController.add(newStatus);
}
}
/// If the current status is [AnalysisStatusIdle], returns the future that
/// will complete immediately.
///
/// If the current status is [AnalysisStatusWorking], returns the future
/// that will complete when the status changes to [AnalysisStatusIdle].
Future<void> waitForIdle() {
switch (_currentStatus) {
case AnalysisStatusIdle():
return Future<void>.value();
case AnalysisStatusWorking status:
return status._idleCompleter.future;
}
}
}