| // Copyright (c) 2018, 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 'package:collection/collection.dart'; | |
| import 'package:language_server_protocol/json_parsing.dart'; | |
| import 'package:language_server_protocol/protocol_custom_generated.dart'; | |
| import 'package:language_server_protocol/protocol_generated.dart'; | |
| const jsonRpcVersion = '2.0'; | |
| const nullJsonHandler = LspJsonHandler<void>(_alwaysTrue, _alwaysNull); | |
| /// Returns if two objects are equal, recursively checking items in | |
| /// Maps/Lists. | |
| bool lspEquals(dynamic obj1, dynamic obj2) { | |
| return const DeepCollectionEquality().equals(obj1, obj2); | |
| } | |
| /// Returns an objects hash code, recursively combining hashes for items in | |
| /// Maps/Lists. | |
| int lspHashCode(dynamic obj) { | |
| return const DeepCollectionEquality().hash(obj); | |
| } | |
| Object? specToJson(Object? obj) { | |
| if (obj is ToJsonable) { | |
| return obj.toJson(); | |
| } else if (obj is List) { | |
| return List.generate( | |
| obj.length, | |
| (index) => specToJson(obj[index]), | |
| ); | |
| } else { | |
| return obj; | |
| } | |
| } | |
| void _alwaysNull(dynamic _, [dynamic __]) {} | |
| bool _alwaysTrue(dynamic _, [dynamic __]) => true; | |
| /// An LSP Code Action, which can be either a [CodeActionLiteral] (if supported | |
| /// by the client) that contains a [Command] and/or an inline edit, or just a | |
| /// [Command] itself. | |
| typedef CodeAction = Either2<CodeActionLiteral, Command>; | |
| typedef DocumentChanges | |
| = List<Either4<CreateFile, DeleteFile, RenameFile, TextDocumentEdit>>; | |
| class Either2<T1, T2> implements ToJsonable { | |
| final int _which; | |
| final T1? _t1; | |
| final T2? _t2; | |
| const Either2.t1(T1 this._t1) | |
| : _t2 = null, | |
| _which = 1; | |
| const Either2.t2(T2 this._t2) | |
| : _t1 = null, | |
| _which = 2; | |
| @override | |
| int get hashCode => map(lspHashCode, lspHashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is Either2<T1, T2> && | |
| other._which == _which && | |
| lspEquals(other._t1, _t1) && | |
| lspEquals(other._t2, _t2); | |
| T map<T>(T Function(T1) f1, T Function(T2) f2) { | |
| return _which == 1 ? f1(_t1 as T1) : f2(_t2 as T2); | |
| } | |
| @override | |
| Object? toJson() => map(specToJson, specToJson); | |
| @override | |
| String toString() => map((t) => t.toString(), (t) => t.toString()); | |
| /// Checks whether the value of the union equals the supplied value. | |
| bool valueEquals(Object o) => map((t) => t == o, (t) => t == o); | |
| } | |
| class Either3<T1, T2, T3> implements ToJsonable { | |
| final int _which; | |
| final T1? _t1; | |
| final T2? _t2; | |
| final T3? _t3; | |
| Either3.t1(this._t1) | |
| : _t2 = null, | |
| _t3 = null, | |
| _which = 1; | |
| Either3.t2(this._t2) | |
| : _t1 = null, | |
| _t3 = null, | |
| _which = 2; | |
| Either3.t3(this._t3) | |
| : _t1 = null, | |
| _t2 = null, | |
| _which = 3; | |
| @override | |
| int get hashCode => map(lspHashCode, lspHashCode, lspHashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is Either3<T1, T2, T3> && | |
| other._which == _which && | |
| lspEquals(other._t1, _t1) && | |
| lspEquals(other._t2, _t2) && | |
| lspEquals(other._t3, _t3); | |
| T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3) { | |
| return switch (_which) { | |
| 1 => f1(_t1 as T1), | |
| 2 => f2(_t2 as T2), | |
| 3 => f3(_t3 as T3), | |
| _ => throw 'Invalid state.' | |
| }; | |
| } | |
| @override | |
| Object? toJson() => map(specToJson, specToJson, specToJson); | |
| @override | |
| String toString() => map( | |
| (t) => t.toString(), | |
| (t) => t.toString(), | |
| (t) => t.toString(), | |
| ); | |
| /// Checks whether the value of the union equals the supplied value. | |
| bool valueEquals(Object o) => | |
| map((t) => t == o, (t) => t == o, (t) => t == o); | |
| } | |
| class Either4<T1, T2, T3, T4> implements ToJsonable { | |
| final int _which; | |
| final T1? _t1; | |
| final T2? _t2; | |
| final T3? _t3; | |
| final T4? _t4; | |
| Either4.t1(this._t1) | |
| : _t2 = null, | |
| _t3 = null, | |
| _t4 = null, | |
| _which = 1; | |
| Either4.t2(this._t2) | |
| : _t1 = null, | |
| _t3 = null, | |
| _t4 = null, | |
| _which = 2; | |
| Either4.t3(this._t3) | |
| : _t1 = null, | |
| _t2 = null, | |
| _t4 = null, | |
| _which = 3; | |
| Either4.t4(this._t4) | |
| : _t1 = null, | |
| _t2 = null, | |
| _t3 = null, | |
| _which = 4; | |
| @override | |
| int get hashCode => map(lspHashCode, lspHashCode, lspHashCode, lspHashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is Either4<T1, T2, T3, T4> && | |
| other._which == _which && | |
| lspEquals(other._t1, _t1) && | |
| lspEquals(other._t2, _t2) && | |
| lspEquals(other._t3, _t3) && | |
| lspEquals(other._t4, _t4); | |
| T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3, | |
| T Function(T4) f4) { | |
| return switch (_which) { | |
| 1 => f1(_t1 as T1), | |
| 2 => f2(_t2 as T2), | |
| 3 => f3(_t3 as T3), | |
| 4 => f4(_t4 as T4), | |
| _ => throw 'Invalid state.' | |
| }; | |
| } | |
| @override | |
| Object? toJson() => map(specToJson, specToJson, specToJson, specToJson); | |
| @override | |
| String toString() => map( | |
| (t) => t.toString(), | |
| (t) => t.toString(), | |
| (t) => t.toString(), | |
| (t) => t.toString(), | |
| ); | |
| /// Checks whether the value of the union equals the supplied value. | |
| bool valueEquals(Object o) => | |
| map((t) => t == o, (t) => t == o, (t) => t == o, (t) => t == o); | |
| } | |
| /// A helper to allow handlers to declare both a JSON validation function and | |
| /// parse function. | |
| class LspJsonHandler<T> { | |
| final bool Function(Map<String, Object?>?, LspJsonReporter reporter) | |
| validateParams; | |
| final T Function(Map<String, Object?>) convertParams; | |
| const LspJsonHandler(this.validateParams, this.convertParams); | |
| } | |
| abstract class ToJsonable { | |
| Object? toJson(); | |
| } | |
| extension IncomingMessageExtension on IncomingMessage { | |
| /// Returns the amount of time (in milliseconds) since the client sent this | |
| /// request or `null` if the client did not provide [clientRequestTime]. | |
| int? get timeSinceRequest { | |
| var clientRequestTime = this.clientRequestTime; | |
| return clientRequestTime != null | |
| ? DateTime.now().millisecondsSinceEpoch - clientRequestTime | |
| : null; | |
| } | |
| } |