mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:29:48 +00:00
cc67085501
This is used to support "expand selection" functionality in editors. Fixes https://github.com/Dart-Code/Dart-Code/issues/3332. Change-Id: I27929b152ef618fb8b57c43e6c6f6e21fe0966dd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200427 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
293 lines
7.9 KiB
Dart
293 lines
7.9 KiB
Dart
// 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 'dart:async';
|
|
|
|
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
|
|
import 'package:analysis_server/src/lsp/json_parsing.dart';
|
|
import 'package:analysis_server/src/protocol/protocol_internal.dart';
|
|
import 'package:analyzer/src/generated/utilities_general.dart';
|
|
|
|
const jsonRpcVersion = '2.0';
|
|
|
|
const NullJsonHandler = LspJsonHandler<Null>(_alwaysTrue, _alwaysNull);
|
|
|
|
ErrorOr<R> cancelled<R>() =>
|
|
error(ErrorCodes.RequestCancelled, 'Request was cancelled', null);
|
|
|
|
ErrorOr<R> error<R>(ErrorCodes code, String message, [String? data]) =>
|
|
ErrorOr<R>.error(ResponseError(code: code, message: message, data: data));
|
|
|
|
ErrorOr<R> failure<R>(ErrorOr<dynamic> error) => ErrorOr<R>.error(error.error);
|
|
|
|
/// Returns if two objects are equal, recursively checking items in
|
|
/// Maps/Lists.
|
|
bool lspEquals(dynamic obj1, dynamic obj2) {
|
|
if (obj1 is List && obj2 is List) {
|
|
return listEqual(obj1, obj2, lspEquals);
|
|
} else if (obj1 is Map && obj2 is Map) {
|
|
return mapEqual(obj1, obj2, lspEquals);
|
|
} else {
|
|
return obj1.runtimeType == obj2.runtimeType && obj1 == obj2;
|
|
}
|
|
}
|
|
|
|
/// Returns an objects hash code, recursively combining hashes for items in
|
|
/// Maps/Lists.
|
|
int lspHashCode(dynamic obj) {
|
|
var hash = 0;
|
|
if (obj is List) {
|
|
for (var element in obj) {
|
|
hash = JenkinsSmiHash.combine(hash, lspHashCode(element));
|
|
}
|
|
} else if (obj is Map) {
|
|
for (var key in obj.keys) {
|
|
hash = JenkinsSmiHash.combine(hash, lspHashCode(key));
|
|
hash = JenkinsSmiHash.combine(hash, lspHashCode(obj[key]));
|
|
}
|
|
} else {
|
|
hash = obj.hashCode;
|
|
}
|
|
return JenkinsSmiHash.finish(hash);
|
|
}
|
|
|
|
Object? specToJson(Object? obj) {
|
|
if (obj is ToJsonable) {
|
|
return obj.toJson();
|
|
} else {
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
ErrorOr<R> success<R>(R t) => ErrorOr<R>.success(t);
|
|
|
|
Null _alwaysNull(_, [__]) => null;
|
|
|
|
bool _alwaysTrue(_, [__]) => true;
|
|
|
|
class Either2<T1, T2> {
|
|
final int _which;
|
|
final T1? _t1;
|
|
final T2? _t2;
|
|
|
|
Either2.t1(T1 this._t1)
|
|
: _t2 = null,
|
|
_which = 1;
|
|
Either2.t2(T2 this._t2)
|
|
: _t1 = null,
|
|
_which = 2;
|
|
|
|
@override
|
|
int get hashCode => map(lspHashCode, lspHashCode);
|
|
|
|
@override
|
|
bool operator ==(o) =>
|
|
o is Either2<T1, T2> && lspEquals(o._t1, _t1) && lspEquals(o._t2, _t2);
|
|
|
|
T map<T>(T Function(T1) f1, T Function(T2) f2) {
|
|
return _which == 1 ? f1(_t1 as T1) : f2(_t2 as T2);
|
|
}
|
|
|
|
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(o) => map((t) => t == o, (t) => t == o);
|
|
}
|
|
|
|
class Either3<T1, T2, T3> {
|
|
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 ==(o) =>
|
|
o is Either3<T1, T2, T3> &&
|
|
lspEquals(o._t1, _t1) &&
|
|
lspEquals(o._t2, _t2) &&
|
|
lspEquals(o._t3, _t3);
|
|
|
|
T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3) {
|
|
switch (_which) {
|
|
case 1:
|
|
return f1(_t1 as T1);
|
|
case 2:
|
|
return f2(_t2 as T2);
|
|
case 3:
|
|
return f3(_t3 as T3);
|
|
default:
|
|
throw 'Invalid state.';
|
|
}
|
|
}
|
|
|
|
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(o) => map((t) => t == o, (t) => t == o, (t) => t == o);
|
|
}
|
|
|
|
class Either4<T1, T2, T3, T4> {
|
|
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 ==(o) =>
|
|
o is Either4<T1, T2, T3, T4> &&
|
|
lspEquals(o._t1, _t1) &&
|
|
lspEquals(o._t2, _t2) &&
|
|
lspEquals(o._t3, _t3) &&
|
|
lspEquals(o._t4, _t4);
|
|
|
|
T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3,
|
|
T Function(T4) f4) {
|
|
switch (_which) {
|
|
case 1:
|
|
return f1(_t1 as T1);
|
|
case 2:
|
|
return f2(_t2 as T2);
|
|
case 3:
|
|
return f3(_t3 as T3);
|
|
case 4:
|
|
return f4(_t4 as T4);
|
|
default:
|
|
throw 'Invalid state.';
|
|
}
|
|
}
|
|
|
|
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(o) =>
|
|
map((t) => t == o, (t) => t == o, (t) => t == o, (t) => t == o);
|
|
}
|
|
|
|
class ErrorOr<T> extends Either2<ResponseError, T> {
|
|
ErrorOr.error(ResponseError error) : super.t1(error);
|
|
ErrorOr.success(T result) : super.t2(result);
|
|
|
|
/// Returns the error or throws if object is not an error. Check [isError]
|
|
/// before accessing [error].
|
|
ResponseError get error {
|
|
return _which == 1 ? _t1 as ResponseError : (throw 'Value is not an error');
|
|
}
|
|
|
|
/// Returns true if this object is an error, false if it is a result. Prefer
|
|
/// [mapResult] instead of checking this flag if [errors] will simply be
|
|
/// propagated as-is.
|
|
bool get isError => _which == 1;
|
|
|
|
/// Returns the result or throws if this object is an error. Check [isError]
|
|
/// before accessing [result]. It is valid for this to return null is the
|
|
/// object does not represent an error but the resulting value was null.
|
|
T get result {
|
|
return _which == 2 ? _t2 as T : (throw 'Value is not a result');
|
|
}
|
|
|
|
/// If this object is a result, maps [result] through [f], otherwise returns
|
|
/// a new error object representing [error].
|
|
FutureOr<ErrorOr<N>> mapResult<N>(FutureOr<ErrorOr<N>> Function(T) f) {
|
|
return isError
|
|
// Re-wrap the error using our new type arg
|
|
? ErrorOr<N>.error(error)
|
|
// Otherwise call the map function
|
|
: f(result);
|
|
}
|
|
|
|
/// Converts a [List<ErrorOr<T>>] into an [ErrorOr<List<T>>]. If any of the
|
|
/// items represents an error, that error will be returned. Otherwise, the
|
|
/// list of results will be returned in a success response.
|
|
static ErrorOr<List<T>> all<T>(Iterable<ErrorOr<T>> items) {
|
|
final results = <T>[];
|
|
for (final item in items) {
|
|
if (item.isError) {
|
|
return failure(item);
|
|
}
|
|
results.add(item.result);
|
|
}
|
|
return success(results);
|
|
}
|
|
}
|
|
|
|
/// A base class containing the fields common to RequestMessage and
|
|
/// NotificationMessage to simplify handling.
|
|
abstract class IncomingMessage {
|
|
Method get method;
|
|
dynamic get params;
|
|
}
|
|
|
|
/// 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();
|
|
}
|