Initial draft of a custom LSP protocol for refactoring

Change-Id: Ib7a6b6bd0b2e0dccd8e7b6c117dc284b0e5a95cd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/253361
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Danny Tuppeny <danny@tuppeny.com>
This commit is contained in:
Brian Wilkerson 2022-08-02 15:48:04 +00:00 committed by Commit Bot
parent 106c4a5e1c
commit 023c6df3b4
3 changed files with 217 additions and 4 deletions

View file

@ -136,6 +136,130 @@ class ClosingLabel implements ToJsonable {
String toString() => jsonEncoder.convert(toJson());
}
/// Information about one of the arguments needed by the command.
///
/// A list of parameters is sent in the `data` field of the `CodeAction`
/// returned by the server. The values of the parameters should appear in the
/// `args` field of the `Command` sent to the server in the same order as the
/// corresponding parameters.
class CommandParameter implements ToJsonable {
static const jsonHandler = LspJsonHandler(
CommandParameter.canParse,
CommandParameter.fromJson,
);
CommandParameter({
required this.defaultValue,
required this.label,
required this.type,
});
static CommandParameter fromJson(Map<String, Object?> json) {
final defaultValueJson = json['defaultValue'];
final defaultValue = defaultValueJson as String;
final labelJson = json['label'];
final label = labelJson as String;
final typeJson = json['type'];
final type = CommandParameterType.fromJson(typeJson as String);
return CommandParameter(
defaultValue: defaultValue,
label: label,
type: type,
);
}
/// The default value for the parameter.
final String defaultValue;
/// A human-readable label to be displayed in the UI affordance used to prompt
/// the user for the value of the parameter.
final String label;
/// The type of the value of the parameter.
final CommandParameterType type;
@override
Map<String, Object?> toJson() {
var result = <String, Object?>{};
result['defaultValue'] = defaultValue;
result['label'] = label;
result['type'] = type.toJson();
return result;
}
static bool canParse(Object? obj, LspJsonReporter reporter) {
if (obj is Map<String, Object?>) {
if (!_canParseString(obj, reporter, 'defaultValue',
allowsUndefined: false, allowsNull: false)) {
return false;
}
if (!_canParseString(obj, reporter, 'label',
allowsUndefined: false, allowsNull: false)) {
return false;
}
return _canParseCommandParameterType(obj, reporter, 'type',
allowsUndefined: false, allowsNull: false);
} else {
reporter.reportError('must be of type CommandParameter');
return false;
}
}
@override
bool operator ==(Object other) {
return other is CommandParameter &&
other.runtimeType == CommandParameter &&
defaultValue == other.defaultValue &&
label == other.label &&
type == other.type;
}
@override
int get hashCode => Object.hash(
defaultValue,
label,
type,
);
@override
String toString() => jsonEncoder.convert(toJson());
}
/// The type of the value associated with a CommandParameter. All values are
/// encoded as strings, but the type indicates how the string will be decoded by
/// the server.
class CommandParameterType implements ToJsonable {
const CommandParameterType(this._value);
const CommandParameterType.fromJson(this._value);
final String _value;
static bool canParse(Object? obj, LspJsonReporter reporter) => obj is String;
/// The type associated with a bool value.
///
/// The value must either be `'true'` or `'false'`.
static const boolean = CommandParameterType('boolean');
/// The type associated with a value representing a path to a file.
static const filePath = CommandParameterType('filePath');
/// The type associated with a string value.
static const string = CommandParameterType('string');
@override
Object toJson() => _value;
@override
String toString() => _value.toString();
@override
int get hashCode => _value.hashCode;
@override
bool operator ==(Object other) =>
other is CommandParameterType && other._value == _value;
}
class CompletionItemResolutionInfo implements ToJsonable {
static const jsonHandler = LspJsonHandler(
CompletionItemResolutionInfo.canParse,
@ -1811,6 +1935,32 @@ bool _canParseBool(
return true;
}
bool _canParseCommandParameterType(
Map<String, Object?> map, LspJsonReporter reporter, String fieldName,
{required bool allowsUndefined, required bool allowsNull}) {
reporter.push(fieldName);
try {
if (!allowsUndefined && !map.containsKey(fieldName)) {
reporter.reportError('must not be undefined');
return false;
}
final value = map[fieldName];
final nullCheck = allowsNull || allowsUndefined;
if (!nullCheck && value == null) {
reporter.reportError('must not be null');
return false;
}
if ((!nullCheck || value != null) &&
!CommandParameterType.canParse(value, reporter)) {
reporter.reportError('must be of type CommandParameterType');
return false;
}
} finally {
reporter.pop();
}
return true;
}
bool _canParseElement(
Map<String, Object?> map, LspJsonReporter reporter, String fieldName,
{required bool allowsUndefined, required bool allowsNull}) {

View file

@ -306,7 +306,7 @@ typedef TextDocumentSignatureHelpResult = SignatureHelp?;
/// Result for a request to resolve the type definition locations of a symbol at
/// a given text document position. The request's parameter is of type
/// TextDocumentPositionParams the response is of type Definition or a Thenable
/// TextDocumentPositioParams the response is of type Definition or a Thenable
/// that resolves to such.
typedef TextDocumentTypeDefinitionResult
= Either2<Definition, List<DefinitionLink>>?;
@ -21955,7 +21955,7 @@ class PreviousResultId implements ToJsonable {
);
}
/// The URI for which the client knows a result id.
/// The URI for which the client knowns a result id.
final DocumentUri uri;
/// The value of the previous result id.

View file

@ -111,9 +111,11 @@ const jsonEncoder = JsonEncoder.withIndent(' ');
List<LspEntity> getCustomClasses() {
/// Helper to create an interface type.
Interface interface(String name, List<Member> fields, {String? baseType}) {
Interface interface(String name, List<Member> fields,
{String? baseType, String? comment}) {
return Interface(
name: name,
comment: comment,
baseTypes: [if (baseType != null) TypeReference(baseType)],
members: fields,
);
@ -353,7 +355,68 @@ List<LspEntity> getCustomClasses() {
]),
),
isRename: false,
)
),
//
// Command parameter support
//
interface(
'CommandParameter',
[
field(
'label',
type: 'String',
comment:
'A human-readable label to be displayed in the UI affordance '
'used to prompt the user for the value of the parameter.',
),
field(
'type',
type: 'CommandParameterType',
comment: 'The type of the value of the parameter.',
),
field(
'defaultValue',
type: 'String',
comment: 'The default value for the parameter.',
),
],
comment: 'Information about one of the arguments needed by the command.'
'\n\n'
'A list of parameters is sent in the `data` field of the '
'`CodeAction` returned by the server. The values of the parameters '
'should appear in the `args` field of the `Command` sent to the '
'server in the same order as the corresponding parameters.',
),
LspEnum(
name: 'CommandParameterType',
comment: 'The type of the value associated with a CommandParameter. All '
'values are encoded as strings, but the type indicates how the '
'string will be decoded by the server.',
members: [
Constant(
name: 'boolean',
value: 'boolean',
type: TypeReference('String'),
comment: 'The type associated with a bool value.'
'\n\n'
"The value must either be `'true'` or `'false'`.",
),
Constant(
name: 'string',
value: 'string',
type: TypeReference('String'),
comment: 'The type associated with a string value.',
),
Constant(
name: 'filePath',
value: 'filePath',
type: TypeReference('String'),
comment:
'The type associated with a value representing a path to a file.',
),
],
typeOfValues: TypeReference('String'),
),
];
return customTypes;
}