4f3a672b00
Following on from a previous change to use the term `CodeAction` (an item returned from a `textDocument/codeAction` request) to mean either a `CodeActionLiteral` (which contains inline edits and/or a command) or a base `Command`, this adds a typedef describing the same and updates all code that used `Either2<CodeActionLiteral, Command>` to instead use the typedef. It also renames a few additional methods/functions to be more consistent with these terms and adds a `CodeActionExtension` to simplify extracting fields like `command` and `title` from either kind of code action. Change-Id: Iad5f77ef21229fc5c9738527f98ccf7202f83bd8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425863 Reviewed-by: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
230 lines
5.8 KiB
Dart
230 lines
5.8 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 '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 {
|
|
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> &&
|
|
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> &&
|
|
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> &&
|
|
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;
|
|
}
|
|
}
|