[analysis_server] Update Interactive Forms definitions based on the latest spec
I had previously been using a slightly outdated version of the protocol. This updates things to match what's in Hongxiang's definition at https://github.com/Dart-Code/Dart-Code/pull/6059. Change-Id: I98578383fb7a68bb0f2f1a813753192a17c2df40 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505221 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Keerti Parthasarathy <keertip@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
9eafcc7cee
commit
adf9500581
@@ -17,19 +17,26 @@ final interactiveFormClasses = <LspEntity>[
|
||||
type: 'FormField',
|
||||
canBeUndefined: true,
|
||||
comment:
|
||||
'Defines the questions and validation errors. This is a '
|
||||
'server-to-client field.',
|
||||
'FormFields defines the questions and validation errors in previous '
|
||||
'answers to the same questions.\n\n'
|
||||
'This is a server-to-client field. The language server defines '
|
||||
'these, and the client uses them to render the form.\n\n'
|
||||
'The interactive phase is considered complete when the server '
|
||||
'returns a response where this slice is omitted.',
|
||||
),
|
||||
field(
|
||||
'formAnswers',
|
||||
array: true,
|
||||
type: 'LSPAny',
|
||||
canBeUndefined: true,
|
||||
comment:
|
||||
'The values for the form questions.\n\n'
|
||||
'When sent by the language server, this acts as preserved/previous '
|
||||
'input.\n\n'
|
||||
'When sent by the client (in a resolve request), this is required '
|
||||
'when formFields are defined.',
|
||||
'FormAnswers contains the values for the form questions.\n\n'
|
||||
'When sent by the language server, this field is optional but '
|
||||
'recommended to support editing previous values.\n\n'
|
||||
'When sent by the language client as part of the ResolveXXX request, '
|
||||
'this field is required. The slice must have the same length as '
|
||||
'FormFields (one answer per question), where the answer at index i '
|
||||
'corresponds to the field at index i.',
|
||||
),
|
||||
field(
|
||||
'data',
|
||||
@@ -38,6 +45,28 @@ final interactiveFormClasses = <LspEntity>[
|
||||
comment: 'Context preserved for the server.',
|
||||
),
|
||||
]),
|
||||
interface(
|
||||
'InteractiveExecuteCommandParams',
|
||||
baseTypes: ['InteractiveParams', 'ExecuteCommandParams'],
|
||||
[
|
||||
field(
|
||||
'command',
|
||||
type: 'String',
|
||||
comment: 'The identifier of the actual command handler.',
|
||||
),
|
||||
field(
|
||||
'arguments',
|
||||
type: 'LSPAny',
|
||||
array: true,
|
||||
canBeUndefined: true,
|
||||
comment: 'Arguments that the command should be invoked with.',
|
||||
),
|
||||
],
|
||||
comment:
|
||||
'InteractiveExecuteCommandParams extends the standard LSP '
|
||||
'ExecuteCommandParams with the experimental fields for interactive '
|
||||
'forms.',
|
||||
),
|
||||
|
||||
interface('FormField', [
|
||||
field(
|
||||
@@ -72,19 +101,31 @@ final interactiveFormClasses = <LspEntity>[
|
||||
], comment: 'A single question in a form and its validation state.'),
|
||||
|
||||
// Field kinds
|
||||
interface('FormFieldType', abstract: true, [
|
||||
field('kind', type: 'String'),
|
||||
]), // Base
|
||||
interface('FormFieldType', abstract: true, [field('kind', type: 'String')]),
|
||||
interface('FormFieldTypeString', baseType: 'FormFieldType', [
|
||||
field('kind', type: 'string', literal: true),
|
||||
], comment: 'A text input.'),
|
||||
interface(
|
||||
'FormFieldTypeDocumentURI',
|
||||
'FormFieldTypeFile',
|
||||
baseType: 'FormFieldType',
|
||||
[field('kind', type: 'documentURI', literal: true)],
|
||||
[
|
||||
field('kind', type: 'file', literal: true),
|
||||
field(
|
||||
'existence',
|
||||
type: 'FileExistence',
|
||||
comment: 'Existence constraint.',
|
||||
),
|
||||
field(
|
||||
'type',
|
||||
type: 'FileType',
|
||||
comment:
|
||||
'Type specifies the set of allowed file types (regular file, '
|
||||
'directory, etc).\n\n'
|
||||
'Only applicable against existing file.',
|
||||
),
|
||||
],
|
||||
comment:
|
||||
'FormFieldTypeDocumentURI defines an input for a file or directory '
|
||||
'URI.\n\n'
|
||||
'FormFieldTypeFile defines an input for a file or directory URI.\n\n'
|
||||
'The client determines the best mechanism to collect this information '
|
||||
'from the user (e.g., a graphical file picker, a text input with '
|
||||
'autocomplete, etc).\n\n'
|
||||
@@ -92,27 +133,82 @@ final interactiveFormClasses = <LspEntity>[
|
||||
'defined in the LSP specification: '
|
||||
'https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentUri',
|
||||
),
|
||||
LspEnum(
|
||||
name: 'FileExistence',
|
||||
typeOfValues: TypeReference.int,
|
||||
members: [
|
||||
// Values should be powers of 2 to allow New|Existing.
|
||||
Constant(
|
||||
name: 'New',
|
||||
type: TypeReference.int,
|
||||
value: '1',
|
||||
comment: 'The file has not yet been created.',
|
||||
),
|
||||
Constant(
|
||||
name: 'Existing',
|
||||
type: TypeReference.int,
|
||||
value: '2',
|
||||
comment: 'The file exists already.',
|
||||
),
|
||||
],
|
||||
comment:
|
||||
'FileExistence whether the file denoted by a DocumentURI exists.\n\n'
|
||||
'It is a bit set allowing combinations of existence states. For '
|
||||
'example, New|Existing allows either state.',
|
||||
),
|
||||
LspEnum(
|
||||
name: 'FileType',
|
||||
typeOfValues: TypeReference.int,
|
||||
members: [
|
||||
// Values should be powers of 2 to allow Regular|Directory.
|
||||
Constant(
|
||||
name: 'Regular',
|
||||
type: TypeReference.int,
|
||||
value: '1',
|
||||
comment: 'The resource could be a regular file.',
|
||||
),
|
||||
Constant(
|
||||
name: 'Directory',
|
||||
type: TypeReference.int,
|
||||
value: '2',
|
||||
comment: 'The resource could be a directory.',
|
||||
),
|
||||
],
|
||||
comment:
|
||||
'FileType represents the expected filesystem resource type.\n\n'
|
||||
'It is a bit set allowing combinations of file types. For example, '
|
||||
'Regular|Directory allows either types.',
|
||||
),
|
||||
interface('FormFieldTypeBool', baseType: 'FormFieldType', [
|
||||
field('kind', type: 'bool', literal: true),
|
||||
], comment: 'A boolean input.'),
|
||||
interface('FormFieldTypeNumber', baseType: 'FormFieldType', [
|
||||
field('kind', type: 'number', literal: true),
|
||||
], comment: 'A numeric input.'),
|
||||
interface('FormFieldTypeEnum', baseType: 'FormFieldType', [
|
||||
field('kind', type: 'enum', literal: true),
|
||||
field(
|
||||
'name',
|
||||
type: 'string',
|
||||
canBeUndefined: true,
|
||||
comment: 'An optional identifier for the enum type.',
|
||||
),
|
||||
field(
|
||||
'entries',
|
||||
array: true,
|
||||
type: 'FormEnumEntry',
|
||||
comment: 'The list of allowable options.',
|
||||
),
|
||||
], comment: 'A numeric input.'),
|
||||
interface(
|
||||
'FormFieldTypeEnum',
|
||||
baseType: 'FormFieldType',
|
||||
[
|
||||
field('kind', type: 'enum', literal: true),
|
||||
field(
|
||||
'name',
|
||||
type: 'string',
|
||||
canBeUndefined: true,
|
||||
comment: 'An optional identifier for the enum type.',
|
||||
),
|
||||
field(
|
||||
'entries',
|
||||
array: true,
|
||||
type: 'FormEnumEntry',
|
||||
comment: 'The list of allowable options.',
|
||||
),
|
||||
],
|
||||
comment:
|
||||
'FormFieldTypeEnum defines a selection from a set of values.\n\n'
|
||||
'Use this type when:\n'
|
||||
'- The number of options is small (e.g., < 20).\n'
|
||||
'- All options are known at the time the form is created.\n',
|
||||
),
|
||||
interface('FormEnumEntry', [
|
||||
field(
|
||||
'value',
|
||||
@@ -127,5 +223,15 @@ final interactiveFormClasses = <LspEntity>[
|
||||
type: 'string',
|
||||
comment: 'The human-readable label presented to the user.',
|
||||
),
|
||||
], comment: 'A numeric input.'),
|
||||
], comment: 'A single option in an enumeration.'),
|
||||
interface('FormFieldTypeList', baseType: 'FormFieldType', [
|
||||
field('kind', type: 'list', literal: true),
|
||||
field(
|
||||
'elementType',
|
||||
type: 'FormFieldType',
|
||||
comment:
|
||||
'ElementType specifies the type of the items in the list. '
|
||||
'Recursive reference to the union type.',
|
||||
),
|
||||
], comment: 'A homogenous list of items.'),
|
||||
];
|
||||
|
||||
@@ -34,6 +34,7 @@ Interface interface(
|
||||
String name,
|
||||
List<Member> fields, {
|
||||
String? baseType,
|
||||
List<String>? baseTypes,
|
||||
String? comment,
|
||||
bool abstract = false,
|
||||
}) {
|
||||
@@ -41,7 +42,10 @@ Interface interface(
|
||||
name: name,
|
||||
abstract: abstract,
|
||||
comment: comment,
|
||||
baseTypes: [if (baseType != null) TypeReference(baseType)],
|
||||
baseTypes: [
|
||||
if (baseType != null) TypeReference(baseType),
|
||||
...?baseTypes?.map(TypeReference.new),
|
||||
],
|
||||
members: fields,
|
||||
);
|
||||
}
|
||||
|
||||
+473
-80
@@ -134,6 +134,55 @@ bool _canParseErrorCodes(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _canParseFileExistence(
|
||||
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) &&
|
||||
!FileExistence.canParse(value, reporter)) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
reporter.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _canParseFileType(
|
||||
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) && !FileType.canParse(value, reporter)) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
reporter.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _canParseFlutterOutline(
|
||||
Map<String, Object?> map, LspJsonReporter reporter, String fieldName,
|
||||
{required bool allowsUndefined, required bool allowsNull}) {
|
||||
@@ -2173,6 +2222,67 @@ class Element implements ToJsonable {
|
||||
}
|
||||
}
|
||||
|
||||
/// FileExistence whether the file denoted by a DocumentURI exists.
|
||||
///
|
||||
/// It is a bit set allowing combinations of existence states. For example,
|
||||
/// New|Existing allows either state.
|
||||
class FileExistence implements ToJsonable {
|
||||
/// The file exists already.
|
||||
static const Existing = FileExistence(2);
|
||||
|
||||
/// The file has not yet been created.
|
||||
static const New = FileExistence(1);
|
||||
|
||||
final int _value;
|
||||
|
||||
const FileExistence(this._value);
|
||||
const FileExistence.fromJson(this._value);
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is FileExistence && other._value == _value;
|
||||
|
||||
@override
|
||||
int toJson() => _value;
|
||||
|
||||
@override
|
||||
String toString() => _value.toString();
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) => obj is int;
|
||||
}
|
||||
|
||||
/// FileType represents the expected filesystem resource type.
|
||||
///
|
||||
/// It is a bit set allowing combinations of file types. For example,
|
||||
/// Regular|Directory allows either types.
|
||||
class FileType implements ToJsonable {
|
||||
/// The resource could be a directory.
|
||||
static const Directory = FileType(2);
|
||||
|
||||
/// The resource could be a regular file.
|
||||
static const Regular = FileType(1);
|
||||
|
||||
final int _value;
|
||||
|
||||
const FileType(this._value);
|
||||
const FileType.fromJson(this._value);
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is FileType && other._value == _value;
|
||||
|
||||
@override
|
||||
int toJson() => _value;
|
||||
|
||||
@override
|
||||
String toString() => _value.toString();
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) => obj is int;
|
||||
}
|
||||
|
||||
class FlutterOutline implements ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FlutterOutline.canParse,
|
||||
@@ -2705,7 +2815,7 @@ class FlutterWidgetPreviews implements ToJsonable {
|
||||
}
|
||||
}
|
||||
|
||||
/// A numeric input.
|
||||
/// A single option in an enumeration.
|
||||
class FormEnumEntry implements ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FormEnumEntry.canParse,
|
||||
@@ -2916,15 +3026,18 @@ abstract class FormFieldType implements ToJsonable {
|
||||
}
|
||||
|
||||
static FormFieldType fromJson(Map<String, Object?> json) {
|
||||
if (FormFieldTypeFile.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeFile.fromJson(json);
|
||||
}
|
||||
if (FormFieldTypeEnum.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeEnum.fromJson(json);
|
||||
}
|
||||
if (FormFieldTypeList.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeList.fromJson(json);
|
||||
}
|
||||
if (FormFieldTypeBool.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeBool.fromJson(json);
|
||||
}
|
||||
if (FormFieldTypeDocumentURI.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeDocumentURI.fromJson(json);
|
||||
}
|
||||
if (FormFieldTypeNumber.canParse(json, nullLspJsonReporter)) {
|
||||
return FormFieldTypeNumber.fromJson(json);
|
||||
}
|
||||
@@ -2993,72 +3106,11 @@ class FormFieldTypeBool implements FormFieldType, ToJsonable {
|
||||
}
|
||||
}
|
||||
|
||||
/// FormFieldTypeDocumentURI defines an input for a file or directory URI.
|
||||
/// FormFieldTypeEnum defines a selection from a set of values.
|
||||
///
|
||||
/// The client determines the best mechanism to collect this information from
|
||||
/// the user (e.g., a graphical file picker, a text input with autocomplete,
|
||||
/// etc).
|
||||
///
|
||||
/// The value returned by the client must be a valid "DocumentUri" as defined in
|
||||
/// the LSP specification:
|
||||
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentUri
|
||||
class FormFieldTypeDocumentURI implements FormFieldType, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FormFieldTypeDocumentURI.canParse,
|
||||
FormFieldTypeDocumentURI.fromJson,
|
||||
);
|
||||
|
||||
@override
|
||||
final String kind;
|
||||
|
||||
FormFieldTypeDocumentURI({
|
||||
this.kind = 'documentURI',
|
||||
}) {
|
||||
if (kind != 'documentURI') {
|
||||
throw 'kind may only be the literal \'documentURI\'';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => kind.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FormFieldTypeDocumentURI &&
|
||||
other.runtimeType == FormFieldTypeDocumentURI &&
|
||||
kind == other.kind;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson() {
|
||||
var result = <String, Object?>{};
|
||||
result['kind'] = kind;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => jsonEncoder.convert(toJson());
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) {
|
||||
if (obj is Map<String, Object?>) {
|
||||
return _canParseLiteral(obj, reporter, 'kind',
|
||||
allowsUndefined: false, allowsNull: false, literal: 'documentURI');
|
||||
} else {
|
||||
reporter.reportError('must be of type FormFieldTypeDocumentURI');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FormFieldTypeDocumentURI fromJson(Map<String, Object?> json) {
|
||||
final kindJson = json['kind'];
|
||||
final kind = kindJson as String;
|
||||
return FormFieldTypeDocumentURI(
|
||||
kind: kind,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A numeric input.
|
||||
/// Use this type when:
|
||||
/// - The number of options is small (e.g., < 20).
|
||||
/// - All options are known at the time the form is created.
|
||||
class FormFieldTypeEnum implements FormFieldType, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FormFieldTypeEnum.canParse,
|
||||
@@ -3147,6 +3199,176 @@ class FormFieldTypeEnum implements FormFieldType, ToJsonable {
|
||||
}
|
||||
}
|
||||
|
||||
/// FormFieldTypeFile defines an input for a file or directory URI.
|
||||
///
|
||||
/// The client determines the best mechanism to collect this information from
|
||||
/// the user (e.g., a graphical file picker, a text input with autocomplete,
|
||||
/// etc).
|
||||
///
|
||||
/// The value returned by the client must be a valid "DocumentUri" as defined in
|
||||
/// the LSP specification:
|
||||
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentUri
|
||||
class FormFieldTypeFile implements FormFieldType, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FormFieldTypeFile.canParse,
|
||||
FormFieldTypeFile.fromJson,
|
||||
);
|
||||
|
||||
/// Existence constraint.
|
||||
final FileExistence existence;
|
||||
|
||||
@override
|
||||
final String kind;
|
||||
|
||||
/// Type specifies the set of allowed file types (regular file, directory,
|
||||
/// etc).
|
||||
///
|
||||
/// Only applicable against existing file.
|
||||
final FileType type;
|
||||
FormFieldTypeFile({
|
||||
required this.existence,
|
||||
this.kind = 'file',
|
||||
required this.type,
|
||||
}) {
|
||||
if (kind != 'file') {
|
||||
throw 'kind may only be the literal \'file\'';
|
||||
}
|
||||
}
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
existence,
|
||||
kind,
|
||||
type,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FormFieldTypeFile &&
|
||||
other.runtimeType == FormFieldTypeFile &&
|
||||
existence == other.existence &&
|
||||
kind == other.kind &&
|
||||
type == other.type;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson() {
|
||||
var result = <String, Object?>{};
|
||||
result['existence'] = existence.toJson();
|
||||
result['kind'] = kind;
|
||||
result['type'] = type.toJson();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => jsonEncoder.convert(toJson());
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) {
|
||||
if (obj is Map<String, Object?>) {
|
||||
if (!_canParseFileExistence(obj, reporter, 'existence',
|
||||
allowsUndefined: false, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
if (!_canParseLiteral(obj, reporter, 'kind',
|
||||
allowsUndefined: false, allowsNull: false, literal: 'file')) {
|
||||
return false;
|
||||
}
|
||||
return _canParseFileType(obj, reporter, 'type',
|
||||
allowsUndefined: false, allowsNull: false);
|
||||
} else {
|
||||
reporter.reportError('must be of type FormFieldTypeFile');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FormFieldTypeFile fromJson(Map<String, Object?> json) {
|
||||
final existenceJson = json['existence'];
|
||||
final existence = FileExistence.fromJson(existenceJson as int);
|
||||
final kindJson = json['kind'];
|
||||
final kind = kindJson as String;
|
||||
final typeJson = json['type'];
|
||||
final type = FileType.fromJson(typeJson as int);
|
||||
return FormFieldTypeFile(
|
||||
existence: existence,
|
||||
kind: kind,
|
||||
type: type,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A homogenous list of items.
|
||||
class FormFieldTypeList implements FormFieldType, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
FormFieldTypeList.canParse,
|
||||
FormFieldTypeList.fromJson,
|
||||
);
|
||||
|
||||
/// ElementType specifies the type of the items in the list. Recursive
|
||||
/// reference to the union type.
|
||||
final FormFieldType elementType;
|
||||
|
||||
@override
|
||||
final String kind;
|
||||
|
||||
FormFieldTypeList({
|
||||
required this.elementType,
|
||||
this.kind = 'list',
|
||||
}) {
|
||||
if (kind != 'list') {
|
||||
throw 'kind may only be the literal \'list\'';
|
||||
}
|
||||
}
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
elementType,
|
||||
kind,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FormFieldTypeList &&
|
||||
other.runtimeType == FormFieldTypeList &&
|
||||
elementType == other.elementType &&
|
||||
kind == other.kind;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson() {
|
||||
var result = <String, Object?>{};
|
||||
result['elementType'] = elementType.toJson();
|
||||
result['kind'] = kind;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => jsonEncoder.convert(toJson());
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) {
|
||||
if (obj is Map<String, Object?>) {
|
||||
if (!_canParseFormFieldType(obj, reporter, 'elementType',
|
||||
allowsUndefined: false, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
return _canParseLiteral(obj, reporter, 'kind',
|
||||
allowsUndefined: false, allowsNull: false, literal: 'list');
|
||||
} else {
|
||||
reporter.reportError('must be of type FormFieldTypeList');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FormFieldTypeList fromJson(Map<String, Object?> json) {
|
||||
final elementTypeJson = json['elementType'];
|
||||
final elementType =
|
||||
FormFieldType.fromJson(elementTypeJson as Map<String, Object?>);
|
||||
final kindJson = json['kind'];
|
||||
final kind = kindJson as String;
|
||||
return FormFieldTypeList(
|
||||
elementType: elementType,
|
||||
kind: kind,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A numeric input.
|
||||
class FormFieldTypeNumber implements FormFieldType, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
@@ -3358,6 +3580,163 @@ class IncomingMessage implements Message, ToJsonable {
|
||||
}
|
||||
}
|
||||
|
||||
/// InteractiveExecuteCommandParams extends the standard LSP
|
||||
/// ExecuteCommandParams with the experimental fields for interactive forms.
|
||||
class InteractiveExecuteCommandParams
|
||||
implements ExecuteCommandParams, InteractiveParams, ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
InteractiveExecuteCommandParams.canParse,
|
||||
InteractiveExecuteCommandParams.fromJson,
|
||||
);
|
||||
|
||||
/// Arguments that the command should be invoked with.
|
||||
@override
|
||||
final List<LSPAny>? arguments;
|
||||
|
||||
/// The identifier of the actual command handler.
|
||||
@override
|
||||
final String command;
|
||||
|
||||
/// Context preserved for the server.
|
||||
@override
|
||||
final LSPAny data;
|
||||
|
||||
/// FormAnswers contains the values for the form questions.
|
||||
///
|
||||
/// When sent by the language server, this field is optional but recommended
|
||||
/// to support editing previous values.
|
||||
///
|
||||
/// When sent by the language client as part of the ResolveXXX request, this
|
||||
/// field is required. The slice must have the same length as FormFields (one
|
||||
/// answer per question), where the answer at index i corresponds to the field
|
||||
/// at index i.
|
||||
@override
|
||||
final List<LSPAny>? formAnswers;
|
||||
|
||||
/// FormFields defines the questions and validation errors in previous answers
|
||||
/// to the same questions.
|
||||
///
|
||||
/// This is a server-to-client field. The language server defines these, and
|
||||
/// the client uses them to render the form.
|
||||
///
|
||||
/// The interactive phase is considered complete when the server returns a
|
||||
/// response where this slice is omitted.
|
||||
@override
|
||||
final List<FormField>? formFields;
|
||||
|
||||
/// An optional token that a server can use to report work done progress.
|
||||
@override
|
||||
final ProgressToken? workDoneToken;
|
||||
InteractiveExecuteCommandParams({
|
||||
this.arguments,
|
||||
required this.command,
|
||||
this.data,
|
||||
this.formAnswers,
|
||||
this.formFields,
|
||||
this.workDoneToken,
|
||||
});
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
lspHashCode(arguments),
|
||||
command,
|
||||
data,
|
||||
lspHashCode(formAnswers),
|
||||
lspHashCode(formFields),
|
||||
workDoneToken,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is InteractiveExecuteCommandParams &&
|
||||
other.runtimeType == InteractiveExecuteCommandParams &&
|
||||
const DeepCollectionEquality().equals(arguments, other.arguments) &&
|
||||
command == other.command &&
|
||||
data == other.data &&
|
||||
const DeepCollectionEquality().equals(formAnswers, other.formAnswers) &&
|
||||
const DeepCollectionEquality().equals(formFields, other.formFields) &&
|
||||
workDoneToken == other.workDoneToken;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object?> toJson() {
|
||||
var result = <String, Object?>{};
|
||||
if (arguments != null) {
|
||||
result['arguments'] = arguments;
|
||||
}
|
||||
result['command'] = command;
|
||||
if (data != null) {
|
||||
result['data'] = data;
|
||||
}
|
||||
if (formAnswers != null) {
|
||||
result['formAnswers'] = formAnswers;
|
||||
}
|
||||
if (formFields != null) {
|
||||
result['formFields'] = formFields?.map((item) => item.toJson()).toList();
|
||||
}
|
||||
if (workDoneToken != null) {
|
||||
result['workDoneToken'] = workDoneToken?.toJson();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => jsonEncoder.convert(toJson());
|
||||
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) {
|
||||
if (obj is Map<String, Object?>) {
|
||||
if (!_canParseListObjectNullable(obj, reporter, 'arguments',
|
||||
allowsUndefined: true, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
if (!_canParseString(obj, reporter, 'command',
|
||||
allowsUndefined: false, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
if (!_canParseListObjectNullable(obj, reporter, 'formAnswers',
|
||||
allowsUndefined: true, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
if (!_canParseListFormField(obj, reporter, 'formFields',
|
||||
allowsUndefined: true, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
return _canParseIntString(obj, reporter, 'workDoneToken',
|
||||
allowsUndefined: true, allowsNull: false);
|
||||
} else {
|
||||
reporter.reportError('must be of type InteractiveExecuteCommandParams');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static InteractiveExecuteCommandParams fromJson(Map<String, Object?> json) {
|
||||
final argumentsJson = json['arguments'];
|
||||
final arguments =
|
||||
(argumentsJson as List<Object?>?)?.map((item) => item).toList();
|
||||
final commandJson = json['command'];
|
||||
final command = commandJson as String;
|
||||
final dataJson = json['data'];
|
||||
final data = dataJson;
|
||||
final formAnswersJson = json['formAnswers'];
|
||||
final formAnswers =
|
||||
(formAnswersJson as List<Object?>?)?.map((item) => item).toList();
|
||||
final formFieldsJson = json['formFields'];
|
||||
final formFields = (formFieldsJson as List<Object?>?)
|
||||
?.map((item) => FormField.fromJson(item as Map<String, Object?>))
|
||||
.toList();
|
||||
final workDoneTokenJson = json['workDoneToken'];
|
||||
final workDoneToken =
|
||||
workDoneTokenJson == null ? null : _eitherIntString(workDoneTokenJson);
|
||||
return InteractiveExecuteCommandParams(
|
||||
arguments: arguments,
|
||||
command: command,
|
||||
data: data,
|
||||
formAnswers: formAnswers,
|
||||
formFields: formFields,
|
||||
workDoneToken: workDoneToken,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InteractiveParams implements ToJsonable {
|
||||
static const jsonHandler = LspJsonHandler(
|
||||
InteractiveParams.canParse,
|
||||
@@ -3367,20 +3746,29 @@ class InteractiveParams implements ToJsonable {
|
||||
/// Context preserved for the server.
|
||||
final LSPAny data;
|
||||
|
||||
/// The values for the form questions.
|
||||
/// FormAnswers contains the values for the form questions.
|
||||
///
|
||||
/// When sent by the language server, this acts as preserved/previous input.
|
||||
/// When sent by the language server, this field is optional but recommended
|
||||
/// to support editing previous values.
|
||||
///
|
||||
/// When sent by the client (in a resolve request), this is required when
|
||||
/// formFields are defined.
|
||||
final List<LSPAny> formAnswers;
|
||||
/// When sent by the language client as part of the ResolveXXX request, this
|
||||
/// field is required. The slice must have the same length as FormFields (one
|
||||
/// answer per question), where the answer at index i corresponds to the field
|
||||
/// at index i.
|
||||
final List<LSPAny>? formAnswers;
|
||||
|
||||
/// Defines the questions and validation errors. This is a server-to-client
|
||||
/// field.
|
||||
/// FormFields defines the questions and validation errors in previous answers
|
||||
/// to the same questions.
|
||||
///
|
||||
/// This is a server-to-client field. The language server defines these, and
|
||||
/// the client uses them to render the form.
|
||||
///
|
||||
/// The interactive phase is considered complete when the server returns a
|
||||
/// response where this slice is omitted.
|
||||
final List<FormField>? formFields;
|
||||
InteractiveParams({
|
||||
this.data,
|
||||
required this.formAnswers,
|
||||
this.formAnswers,
|
||||
this.formFields,
|
||||
});
|
||||
@override
|
||||
@@ -3405,7 +3793,9 @@ class InteractiveParams implements ToJsonable {
|
||||
if (data != null) {
|
||||
result['data'] = data;
|
||||
}
|
||||
result['formAnswers'] = formAnswers;
|
||||
if (formAnswers != null) {
|
||||
result['formAnswers'] = formAnswers;
|
||||
}
|
||||
if (formFields != null) {
|
||||
result['formFields'] = formFields?.map((item) => item.toJson()).toList();
|
||||
}
|
||||
@@ -3418,7 +3808,7 @@ class InteractiveParams implements ToJsonable {
|
||||
static bool canParse(Object? obj, LspJsonReporter reporter) {
|
||||
if (obj is Map<String, Object?>) {
|
||||
if (!_canParseListObjectNullable(obj, reporter, 'formAnswers',
|
||||
allowsUndefined: false, allowsNull: false)) {
|
||||
allowsUndefined: true, allowsNull: false)) {
|
||||
return false;
|
||||
}
|
||||
return _canParseListFormField(obj, reporter, 'formFields',
|
||||
@@ -3430,11 +3820,14 @@ class InteractiveParams implements ToJsonable {
|
||||
}
|
||||
|
||||
static InteractiveParams fromJson(Map<String, Object?> json) {
|
||||
if (InteractiveExecuteCommandParams.canParse(json, nullLspJsonReporter)) {
|
||||
return InteractiveExecuteCommandParams.fromJson(json);
|
||||
}
|
||||
final dataJson = json['data'];
|
||||
final data = dataJson;
|
||||
final formAnswersJson = json['formAnswers'];
|
||||
final formAnswers =
|
||||
(formAnswersJson as List<Object?>).map((item) => item).toList();
|
||||
(formAnswersJson as List<Object?>?)?.map((item) => item).toList();
|
||||
final formFieldsJson = json['formFields'];
|
||||
final formFields = (formFieldsJson as List<Object?>?)
|
||||
?.map((item) => FormField.fromJson(item as Map<String, Object?>))
|
||||
|
||||
@@ -19881,6 +19881,9 @@ class ExecuteCommandParams implements WorkDoneProgressParams, ToJsonable {
|
||||
}
|
||||
|
||||
static ExecuteCommandParams fromJson(Map<String, Object?> json) {
|
||||
if (InteractiveExecuteCommandParams.canParse(json, nullLspJsonReporter)) {
|
||||
return InteractiveExecuteCommandParams.fromJson(json);
|
||||
}
|
||||
final argumentsJson = json['arguments'];
|
||||
final arguments =
|
||||
(argumentsJson as List<Object?>?)?.map((item) => item).toList();
|
||||
|
||||
Reference in New Issue
Block a user