| // Copyright (c) 2025, 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.dart'; | |
| import 'package:analysis_server/src/lsp/constants.dart'; | |
| import 'package:analyzer/src/test_utilities/platform.dart'; | |
| import 'package:analyzer/src/test_utilities/test_code_format.dart'; | |
| import 'package:test/test.dart'; | |
| import '../lsp/request_helpers_mixin.dart'; | |
| import '../lsp/server_abstract.dart'; | |
| import '../tool/lsp_spec/matchers.dart'; | |
| import '../utils/test_code_extensions.dart'; | |
| import 'shared_test_interface.dart'; | |
| /// Shared edit argument tests that are used by both LSP + Legacy server | |
| /// tests. | |
| mixin SharedEditArgumentTests | |
| on | |
| SharedTestInterface, | |
| LspRequestHelpersMixin, | |
| LspVerifyEditHelpersMixin, | |
| ClientCapabilitiesHelperMixin { | |
| late TestCode code; | |
| @override | |
| Future<void> setUp() async { | |
| await super.setUp(); | |
| setApplyEditSupport(); | |
| setDocumentChangesSupport(); | |
| } | |
| Future<void> test_comma_addArg_addsIfExists() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '(x: 1,)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2,)', | |
| ); | |
| } | |
| Future<void> test_comma_addArg_doesNotAddIfNotExists() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_comma_editArg_doesNotAddIfNotExists() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '(x: 1, y: 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_comma_editArg_retainsIfExists() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '(x: 1, y: 1,)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2,)', | |
| ); | |
| } | |
| Future<void> test_documentChanges_supported() async { | |
| // Ensure documentChanges are supported. The verification in | |
| // LspChangeVerifier will verify the resulting edits match the capabilities. | |
| setDocumentChangesSupport(); | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_documentChanges_unsupported() async { | |
| // documentChanges are NOT supported. The verification in | |
| // LspChangeVerifier will verify the resulting edits match the capabilities. | |
| setDocumentChangesSupport(false); | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_format_multiline_insert_between() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y, int? children })', | |
| originalArgs: '''\n | |
| ( | |
| x: 1, | |
| children: 3, | |
| )''', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '''\n | |
| ( | |
| x: 1, | |
| y: 2, | |
| children: 3, | |
| )''', | |
| ); | |
| } | |
| Future<void> test_format_multiline_insert_last() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '''\n | |
| ( | |
| x: 1, | |
| )''', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '''\n | |
| ( | |
| x: 1, | |
| y: 2, | |
| )''', | |
| ); | |
| } | |
| Future<void> test_format_multiline_insert_solo() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '''\n | |
| ( | |
| )''', | |
| edit: ArgumentEdit(name: 'x', newValue: 1), | |
| expectedArgs: '''\n | |
| ( | |
| x: 1, | |
| )''', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_afterChildNotAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y, Widget? child })', | |
| originalArgs: '(child: null, x: 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(child: null, x: 1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_beforeChild_noOthers() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? y, Widget? child })', | |
| originalArgs: '(child: null)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(y: 2, child: null)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_beforeChild_noOthers_multiline() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? y, Widget? child })', | |
| originalArgs: '''\n | |
| ( | |
| child: null | |
| )''', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '''\n | |
| ( | |
| y: 2, | |
| child: null | |
| )''', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_beforeChildAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y, Widget? child })', | |
| originalArgs: '(x: 1, child: null)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2, child: null)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_beforeChildren_noOthers() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? y, List<Widget>? children })', | |
| originalArgs: '(children: [])', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(y: 2, children: [])', | |
| ); | |
| } | |
| Future<void> | |
| test_named_addAfterNamed_beforeChildren_noOthers_multiline() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? y, List<Widget>? children })', | |
| originalArgs: '''\n | |
| ( | |
| children: [] | |
| )''', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '''\n | |
| ( | |
| y: 2, | |
| children: [] | |
| )''', | |
| ); | |
| } | |
| Future<void> test_named_addAfterNamed_beforeChildrenAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x, int? y, List<Widget>? children })', | |
| originalArgs: '(x: 1, children: [])', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(x: 1, y: 2, children: [])', | |
| ); | |
| } | |
| Future<void> test_named_addAfterPositional() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, { int? y })', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterPositional_afterChildNotAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, { int? y, Widget? child })', | |
| originalArgs: '(child: null, 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(child: null, 1, y: 2)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterPositional_beforeChildAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, { int? y, Widget? child })', | |
| originalArgs: '(1, child: null)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(1, y: 2, child: null)', | |
| ); | |
| } | |
| Future<void> test_named_addAfterPositional_beforeChildrenAtEnd() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, { int? y, List<Widget>? children })', | |
| originalArgs: '(1, children: [])', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(1, y: 2, children: [])', | |
| ); | |
| } | |
| Future<void> test_named_privateInitializingFormal() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({this._x})', | |
| additionalWidgetCode: 'final int? _x;', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_optionalPositional_addAfterPositional() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '([int? x, int? y])', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(1, 2)', | |
| ); | |
| } | |
| Future<void> test_optionalPositional_notNext_afterPositional() async { | |
| await _expectFailedEdit( | |
| params: '([int? x, int y = 10, int? z])', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'z', newValue: 2), | |
| errorCode: ServerErrorCodes.editArgumentInvalidParameter, | |
| message: | |
| "The parameter 'z' is not editable because " | |
| "a value for the 3rd parameter can't be added until a value for all preceding positional parameters have been added.", | |
| ); | |
| } | |
| Future<void> test_optionalPositional_notNext_solo() async { | |
| await _expectFailedEdit( | |
| params: '([int? x = 10, int? y])', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| errorCode: ServerErrorCodes.editArgumentInvalidParameter, | |
| message: | |
| "The parameter 'y' is not editable because " | |
| "a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.", | |
| ); | |
| } | |
| Future<void> test_primaryConstructor_addArg() async { | |
| var content = ''' | |
| import 'package:flutter/widgets.dart'; | |
| class MyWidget(int x, {int? y}) extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) => MyW^idget(1); | |
| } | |
| '''; | |
| var expectedContent = ''' | |
| >>>>>>>>>> lib/test.dart | |
| import 'package:flutter/widgets.dart'; | |
| class MyWidget(int x, {int? y}) extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) => MyWidget(1, y: 2); | |
| } | |
| '''; | |
| await _expectArgumentEdit( | |
| content, | |
| ArgumentEdit(name: 'y', newValue: 2), | |
| expectedContent, | |
| ); | |
| } | |
| Future<void> test_primaryConstructor_editArg() async { | |
| var content = ''' | |
| import 'package:flutter/widgets.dart'; | |
| class MyWidget(int x, {int? y}) extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) => MyW^idget(1, y: 1); | |
| } | |
| '''; | |
| var expectedContent = ''' | |
| >>>>>>>>>> lib/test.dart | |
| import 'package:flutter/widgets.dart'; | |
| class MyWidget(int x, {int? y}) extends StatelessWidget { | |
| @override | |
| Widget build(BuildContext context) => MyWidget(1, y: 2); | |
| } | |
| '''; | |
| await _expectArgumentEdit( | |
| content, | |
| ArgumentEdit(name: 'y', newValue: 2), | |
| expectedContent, | |
| ); | |
| } | |
| Future<void> test_requiredPositional_addAfterNamed() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, { int? y })', | |
| originalArgs: '(y: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(y: 1, 2)', | |
| ); | |
| } | |
| Future<void> test_requiredPositional_addAfterPositional() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x, int? y)', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| expectedArgs: '(1, 2)', | |
| ); | |
| } | |
| Future<void> test_requiredPositional_notNext_afterPositional() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectFailedEdit( | |
| params: '(int? x, int? y, int? z)', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'z', newValue: 2), | |
| errorCode: ServerErrorCodes.editArgumentInvalidParameter, | |
| message: | |
| "The parameter 'z' is not editable because " | |
| "a value for the 3rd parameter can't be added until a value for all preceding positional parameters have been added.", | |
| ); | |
| } | |
| Future<void> test_requiredPositional_notNext_noExisting() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectFailedEdit( | |
| params: '(int? x, int? y)', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| errorCode: ServerErrorCodes.editArgumentInvalidParameter, | |
| message: | |
| "The parameter 'y' is not editable because " | |
| "a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.", | |
| ); | |
| } | |
| Future<void> test_requiredPositional_notNext_onlyNamed() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectFailedEdit( | |
| params: '(int? x, int? y, { int? z })', | |
| originalArgs: '(z: 1)', | |
| edit: ArgumentEdit(name: 'y', newValue: 2), | |
| errorCode: ServerErrorCodes.editArgumentInvalidParameter, | |
| message: | |
| "The parameter 'y' is not editable because " | |
| "a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.", | |
| ); | |
| } | |
| Future<void> test_soloArgument_addNamed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({int? x })', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_soloArgument_addOptionalPositional() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '([int? x])', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(2)', | |
| ); | |
| } | |
| Future<void> test_soloArgument_addRequiredPositional() async { | |
| failTestOnErrorDiagnostic = false; // Tests with missing positional. | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x)', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(2)', | |
| ); | |
| } | |
| Future<void> test_soloArgument_editNamed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_soloArgument_editOptionalPositional() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '([int? x])', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(2)', | |
| ); | |
| } | |
| Future<void> test_soloArgument_editRequiredPositional() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '(int? x)', | |
| originalArgs: '(1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(2)', | |
| ); | |
| } | |
| Future<void> test_type_bool_invalidType() async { | |
| await _expectFailedEdit( | |
| params: '({ bool? x })', | |
| originalArgs: '(x: true)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'invalid'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' should be bool? but was String", | |
| ); | |
| } | |
| Future<void> test_type_bool_null_allowed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ bool? x })', | |
| originalArgs: '(x: true)', | |
| edit: ArgumentEdit(name: 'x'), | |
| expectedArgs: '(x: null)', | |
| ); | |
| } | |
| Future<void> test_type_bool_null_notAllowed() async { | |
| await _expectFailedEdit( | |
| params: '({ required bool x })', | |
| originalArgs: '(x: true)', | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' can't be null", | |
| ); | |
| } | |
| Future<void> test_type_bool_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ bool? x })', | |
| originalArgs: '(x: true)', | |
| edit: ArgumentEdit(name: 'x', newValue: false), | |
| expectedArgs: '(x: false)', | |
| ); | |
| } | |
| Future<void> test_type_bool_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ bool? x })', | |
| originalArgs: '(x: 1 == 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: false), | |
| expectedArgs: '(x: false)', | |
| ); | |
| } | |
| Future<void> test_type_double_invalidType() async { | |
| await _expectFailedEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1.1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'invalid'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: | |
| "The value for the parameter 'x' should be double? but was String", | |
| ); | |
| } | |
| Future<void> test_type_double_null_allowed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1.0)', | |
| edit: ArgumentEdit(name: 'x'), | |
| expectedArgs: '(x: null)', | |
| ); | |
| } | |
| Future<void> test_type_double_null_notAllowed() async { | |
| await _expectFailedEdit( | |
| params: '({ required double x })', | |
| originalArgs: '(x: 1.0)', | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' can't be null", | |
| ); | |
| } | |
| Future<void> test_type_double_replaceInt() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2.2), | |
| expectedArgs: '(x: 2.2)', | |
| ); | |
| } | |
| Future<void> test_type_double_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1.1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2.2), | |
| expectedArgs: '(x: 2.2)', | |
| ); | |
| } | |
| Future<void> test_type_double_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1.1 + 0.1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2.2), | |
| expectedArgs: '(x: 2.2)', | |
| ); | |
| } | |
| Future<void> test_type_double_replaceWithInt() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ double? x })', | |
| originalArgs: '(x: 1.1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_addNew() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: .two)', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_disabled_addNew() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '()', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: E.two)', | |
| fileComment: '// @dart = 3.8', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_disabled_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '(x: E.one)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: E.two)', | |
| fileComment: '// @dart = 3.8', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_disabled_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: ''' | |
| enum E { one, two } | |
| const E myConst = E.one; | |
| ''', | |
| params: '({ E? x })', | |
| originalArgs: '(x: myConst)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: E.two)', | |
| fileComment: '// @dart = 3.8', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '(x: .one)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: .two)', | |
| ); | |
| } | |
| Future<void> test_type_enum_dotshorthand_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: ''' | |
| enum E { one, two } | |
| const E myConst = .one; | |
| ''', | |
| params: '({ E? x })', | |
| originalArgs: '(x: myConst)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: .two)', | |
| ); | |
| } | |
| Future<void> test_type_enum_invalidType() async { | |
| await _expectFailedEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '(x: E.one)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'invalid'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' should be one of 'E.one', 'E.two' but was 'invalid'", | |
| ); | |
| } | |
| Future<void> test_type_enum_null_allowed() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '(x: E.one)', | |
| edit: ArgumentEdit(name: 'x'), | |
| expectedArgs: '(x: null)', | |
| ); | |
| } | |
| Future<void> test_type_enum_null_notAllowed() async { | |
| await _expectFailedEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ required E x })', | |
| originalArgs: '(x: E.one)', | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' can't be null", | |
| ); | |
| } | |
| Future<void> test_type_enum_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: 'enum E { one, two }', | |
| params: '({ E? x })', | |
| originalArgs: '(x: E.one)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: E.two)', | |
| ); | |
| } | |
| Future<void> test_type_enum_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| additionalCode: ''' | |
| enum E { one, two } | |
| const myConst = E.one; | |
| ''', | |
| params: '({ E? x })', | |
| originalArgs: '(x: myConst)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'E.two'), | |
| expectedArgs: '(x: .two)', | |
| ); | |
| } | |
| Future<void> test_type_int_invalidType() async { | |
| await _expectFailedEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 'invalid'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' should be int? but was String", | |
| ); | |
| } | |
| Future<void> test_type_int_null_allowed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x'), | |
| expectedArgs: '(x: null)', | |
| ); | |
| } | |
| Future<void> test_type_int_null_notAllowed() async { | |
| await _expectFailedEdit( | |
| params: '({ required int x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' can't be null", | |
| ); | |
| } | |
| Future<void> test_type_int_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_type_int_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ int? x })', | |
| originalArgs: '(x: 1 + 0)', | |
| edit: ArgumentEdit(name: 'x', newValue: 2), | |
| expectedArgs: '(x: 2)', | |
| ); | |
| } | |
| Future<void> test_type_string_containsBackslashes() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: r'a\b'), | |
| expectedArgs: r"(x: 'a\\b')", | |
| ); | |
| } | |
| Future<void> test_type_string_containsBothQuotes() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: '''a'b"c'''), | |
| expectedArgs: r'''(x: 'a\'b"c')''', | |
| ); | |
| } | |
| Future<void> test_type_string_containsSingleQuotes() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: "a'b"), | |
| expectedArgs: r'''(x: 'a\'b')''', | |
| ); | |
| } | |
| Future<void> test_type_string_invalidType() async { | |
| await _expectFailedEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 123), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' should be String? but was int", | |
| ); | |
| } | |
| Future<void> test_type_string_multiline() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'a\nb'), | |
| expectedArgs: r'''(x: 'a\nb')''', | |
| ); | |
| } | |
| Future<void> test_type_string_null_allowed() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x'), | |
| expectedArgs: '(x: null)', | |
| ); | |
| } | |
| Future<void> test_type_string_null_notAllowed() async { | |
| await _expectFailedEdit( | |
| params: '({ required String x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editArgumentInvalidValue, | |
| message: "The value for the parameter 'x' can't be null", | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_dollar_escapedNonRaw() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: '')", | |
| edit: ArgumentEdit(name: 'x', newValue: r'$'), | |
| expectedArgs: r"(x: '\$')", | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_dollar_notEscapedRaw() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: r'')", | |
| edit: ArgumentEdit(name: 'x', newValue: r'$'), | |
| expectedArgs: r"(x: r'$')", | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_usesExistingDouble() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: '(x: "a")', | |
| edit: ArgumentEdit(name: 'x', newValue: 'a'), | |
| expectedArgs: '(x: "a")', | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_usesExistingSingle() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'a'), | |
| expectedArgs: "(x: 'a')", | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_usesExistingTripleDouble() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: '(x: """a""")', | |
| edit: ArgumentEdit(name: 'x', newValue: 'a'), | |
| expectedArgs: '(x: """a""")', | |
| ); | |
| } | |
| Future<void> test_type_string_quotes_usesExistingTripleSingle() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: '''a''')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'a'), | |
| expectedArgs: "(x: '''a''')", | |
| ); | |
| } | |
| Future<void> test_type_string_replaceLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'b'), | |
| expectedArgs: "(x: 'b')", | |
| ); | |
| } | |
| Future<void> test_type_string_replaceLiteral_raw() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: r'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'b'), | |
| expectedArgs: "(x: r'b')", | |
| ); | |
| } | |
| Future<void> test_type_string_replaceLiteral_tripleQuoted() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: '''a''')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'b'), | |
| expectedArgs: "(x: '''b''')", | |
| ); | |
| } | |
| Future<void> test_type_string_replaceNonLiteral() async { | |
| await _expectSimpleArgumentEdit( | |
| params: '({ String? x })', | |
| originalArgs: "(x: 'a' + 'a')", | |
| edit: ArgumentEdit(name: 'x', newValue: 'b'), | |
| expectedArgs: "(x: 'b')", | |
| ); | |
| } | |
| Future<void> test_unsupported_noApplyEditSupport() async { | |
| setApplyEditSupport(false); | |
| await _expectFailedEdit( | |
| params: '({ required String x })', | |
| originalArgs: "(x: 'a')", | |
| edit: ArgumentEdit(name: 'x'), | |
| errorCode: ServerErrorCodes.editsUnsupportedByEditor, | |
| message: 'The connected editor does not support applying edits', | |
| ); | |
| } | |
| /// Initializes the server with [content] and tries to apply the argument | |
| /// [edit] at the marked location. Verifies the changes made match | |
| /// [expectedContent]. | |
| Future<void> _expectArgumentEdit( | |
| String content, | |
| ArgumentEdit edit, | |
| String expectedContent, { | |
| bool open = true, | |
| }) async { | |
| content = normalizeNewlinesForPlatform(content); | |
| expectedContent = normalizeNewlinesForPlatform(expectedContent); | |
| code = TestCode.parse(content); | |
| createFile(testFilePath, code.code); | |
| await initializeServer(); | |
| if (open) { | |
| await openFile(testFileUri, code.code); | |
| } | |
| await workspaceAnalysisComplete(); | |
| var verifier = await executeForEdits( | |
| () => editArgument(testFileUri, code.position.position, edit), | |
| ); | |
| verifier.verifyFiles(expectedContent); | |
| } | |
| /// Initializes the server and verifies a simple argument edit fails with | |
| /// a given message. | |
| Future<void> _expectFailedEdit({ | |
| required String params, | |
| required String originalArgs, | |
| required ArgumentEdit edit, | |
| required ErrorCodes errorCode, | |
| required String message, | |
| String? additionalCode, | |
| }) async { | |
| additionalCode ??= ''; | |
| var content = | |
| ''' | |
| import 'package:flutter/widgets.dart'; | |
| $additionalCode | |
| class MyWidget extends StatelessWidget { | |
| const MyWidget$params; | |
| @override | |
| Widget build(BuildContext context) => MyW^idget$originalArgs; | |
| } | |
| '''; | |
| code = TestCode.parse(content); | |
| createFile(testFilePath, code.code); | |
| await initializeServer(); | |
| await workspaceAnalysisComplete(); | |
| await expectLater( | |
| editArgument(testFileUri, code.position.position, edit), | |
| throwsA(isResponseError(errorCode, message: message)), | |
| ); | |
| } | |
| /// Initializes the server and verifies a simple argument edit. | |
| Future<void> _expectSimpleArgumentEdit({ | |
| required String params, | |
| required String originalArgs, | |
| required ArgumentEdit edit, | |
| required String expectedArgs, | |
| String? additionalCode = '', | |
| String? additionalWidgetCode = '', | |
| String? fileComment = '', | |
| }) async { | |
| var content = | |
| ''' | |
| $fileComment | |
| import 'package:flutter/widgets.dart'; | |
| $additionalCode | |
| class MyWidget extends StatelessWidget { | |
| const MyWidget$params; | |
| @override | |
| Widget build(BuildContext context) => MyW^idget$originalArgs; | |
| $additionalWidgetCode | |
| } | |
| '''; | |
| var expectedContent = | |
| ''' | |
| >>>>>>>>>> lib/test.dart | |
| $fileComment | |
| import 'package:flutter/widgets.dart'; | |
| $additionalCode | |
| class MyWidget extends StatelessWidget { | |
| const MyWidget$params; | |
| @override | |
| Widget build(BuildContext context) => MyWidget$expectedArgs; | |
| $additionalWidgetCode | |
| } | |
| '''; | |
| await _expectArgumentEdit(content, edit, expectedContent); | |
| } | |
| } |