| // Copyright (c) 2020, 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'; | 
 | import 'dart:math'; | 
 |  | 
 | import 'package:analysis_server/lsp_protocol/protocol_generated.dart'; | 
 | import 'package:analysis_server/lsp_protocol/protocol_special.dart'; | 
 | import 'package:analysis_server/src/lsp/lsp_analysis_server.dart'; | 
 |  | 
 | /// Reports progress of long-running operations to the LSP client. | 
 | abstract class ProgressReporter { | 
 |   /// A no-op reporter that does nothing. | 
 |   static final noop = _NoopProgressReporter(); | 
 |  | 
 |   /// Creates a reporter for a token that was supplied by the client and does | 
 |   /// not need creating prior to use. | 
 |   factory ProgressReporter.clientProvided( | 
 |           LspAnalysisServer server, Either2<int, String> token) => | 
 |       _TokenProgressReporter(server, token); | 
 |  | 
 |   /// Creates a reporter for a new token that must be created prior to being | 
 |   /// used. | 
 |   /// | 
 |   /// If [token] is not supplied, a random identifier will be used. | 
 |   factory ProgressReporter.serverCreated(LspAnalysisServer server, | 
 |           [Either2<int, String>? token]) => | 
 |       _ServerCreatedProgressReporter(server, token); | 
 |  | 
 |   ProgressReporter._(); | 
 |  | 
 |   // TODO(dantup): Add support for cancellable progress notifications. | 
 |   FutureOr<void> begin(String title, {String? message}); | 
 |  | 
 |   FutureOr<void> end([String message]); | 
 | } | 
 |  | 
 | class _NoopProgressReporter extends ProgressReporter { | 
 |   _NoopProgressReporter() : super._(); | 
 |   @override | 
 |   void begin(String title, {String? message}) {} | 
 |   @override | 
 |   void end([String? message]) {} | 
 | } | 
 |  | 
 | class _ServerCreatedProgressReporter extends _TokenProgressReporter { | 
 |   static final _random = Random(); | 
 |   Future<bool>? _tokenBeginRequest; | 
 |  | 
 |   _ServerCreatedProgressReporter( | 
 |     LspAnalysisServer server, | 
 |     Either2<int, String>? token, | 
 |   ) : super( | 
 |           server, | 
 |           token ?? Either2<int, String>.t2(_randomTokenIdentifier()), | 
 |         ); | 
 |  | 
 |   @override | 
 |   Future<void> begin(String? title, {String? message}) async { | 
 |     assert(_tokenBeginRequest == null, | 
 |         'Begin should not be called more than once'); | 
 |  | 
 |     // Put the create/begin into a future so if end() is called before the | 
 |     // begin is sent (which could happen because create is async), end will | 
 |     // not be sent/return too early. | 
 |     _tokenBeginRequest = _server | 
 |         .sendRequest(Method.window_workDoneProgress_create, | 
 |             WorkDoneProgressCreateParams(token: _token)) | 
 |         .then((response) { | 
 |       // If the client did not create a token, do not send begin (and signal | 
 |       // that we should also not send end). | 
 |       if (response.error != null) return false; | 
 |       super.begin(title, message: message); | 
 |       return true; | 
 |     }); | 
 |  | 
 |     await _tokenBeginRequest; | 
 |   } | 
 |  | 
 |   @override | 
 |   Future<void> end([String? message]) async { | 
 |     // Only end the token after both create/begin have completed, and return | 
 |     // a Future to indicate that has happened to callers know when it's safe | 
 |     // to re-use the token identifier. | 
 |     final beginRequest = _tokenBeginRequest; | 
 |     if (beginRequest != null) { | 
 |       final didBegin = await beginRequest; | 
 |       if (didBegin) { | 
 |         super.end(message); | 
 |       } | 
 |       _tokenBeginRequest = null; | 
 |     } | 
 |   } | 
 |  | 
 |   static String _randomTokenIdentifier() { | 
 |     final millisecondsSinceEpoch = DateTime.now().millisecondsSinceEpoch; | 
 |     final random = _random.nextInt(0x3fffffff); | 
 |     return '$millisecondsSinceEpoch$random'; | 
 |   } | 
 | } | 
 |  | 
 | class _TokenProgressReporter extends ProgressReporter { | 
 |   final LspAnalysisServer _server; | 
 |   final Either2<int, String> _token; | 
 |   bool _needsEnd = false; | 
 |  | 
 |   _TokenProgressReporter(this._server, this._token) : super._(); | 
 |  | 
 |   @override | 
 |   void begin(String? title, {String? message}) { | 
 |     _needsEnd = true; | 
 |     _sendNotification( | 
 |         WorkDoneProgressBegin(title: title ?? 'Working…', message: message)); | 
 |   } | 
 |  | 
 |   @override | 
 |   void end([String? message]) { | 
 |     if (!_needsEnd) return; | 
 |     _needsEnd = false; | 
 |     _sendNotification(WorkDoneProgressEnd(message: message)); | 
 |   } | 
 |  | 
 |   void _sendNotification(ToJsonable value) async { | 
 |     _server.sendNotification(NotificationMessage( | 
 |         method: Method.progress, | 
 |         params: ProgressParams( | 
 |           token: _token, | 
 |           value: value, | 
 |         ), | 
 |         jsonrpc: jsonRpcVersion)); | 
 |   } | 
 | } |