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