[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:
Danny Tuppeny
2026-05-21 09:51:15 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 9eafcc7cee
commit adf9500581
4 changed files with 617 additions and 111 deletions
@@ -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.'),
];
+5 -1
View File
@@ -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,
);
}
@@ -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();