[analysis_server] Support prompting for name for "Add Constructor Name" refactor
Similar to the previous CL for Add Import Prefix, this adds support for prompting for a name for "Add Constructor Name" if Interactive Forms are available. Change-Id: I952695df03016a549345f329ac04961d7c279af1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509380 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
committed by
Brian Wilkerson
parent
8f30cb7e43
commit
dbe61d2ece
@@ -3,17 +3,21 @@
|
|||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:analysis_server/src/lsp/constants.dart';
|
import 'package:analysis_server/src/lsp/constants.dart';
|
||||||
|
import 'package:analysis_server/src/lsp/error_or.dart';
|
||||||
|
import 'package:analysis_server/src/services/interactive_forms/interactive_forms.dart';
|
||||||
import 'package:analysis_server/src/services/refactoring/framework/refactoring_producer.dart';
|
import 'package:analysis_server/src/services/refactoring/framework/refactoring_producer.dart';
|
||||||
|
import 'package:analysis_server/src/services/refactoring/legacy/naming_conventions.dart';
|
||||||
import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart';
|
import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart';
|
||||||
import 'package:analysis_server/src/services/search/search_engine_internal.dart';
|
import 'package:analysis_server/src/services/search/search_engine_internal.dart';
|
||||||
import 'package:analysis_server/src/utilities/extensions/selection.dart';
|
import 'package:analysis_server/src/utilities/extensions/selection.dart';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
|
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
|
||||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||||
|
import 'package:language_server_protocol/protocol_custom_generated.dart';
|
||||||
import 'package:language_server_protocol/protocol_generated.dart';
|
import 'package:language_server_protocol/protocol_generated.dart';
|
||||||
|
|
||||||
/// The refactoring that adds a name to an unnamed constructor.
|
/// The refactoring that adds a name to an unnamed constructor.
|
||||||
class AddConstructorName extends RefactoringProducer {
|
class AddConstructorName extends ParameterizedRefactoringProducer {
|
||||||
static const String commandName = 'dart.refactor.add_constructor_name';
|
static const String commandName = 'dart.refactor.add_constructor_name';
|
||||||
|
|
||||||
static const String constTitle = 'Add a name to the constructor';
|
static const String constTitle = 'Add a name to the constructor';
|
||||||
@@ -26,14 +30,51 @@ class AddConstructorName extends RefactoringProducer {
|
|||||||
@override
|
@override
|
||||||
CodeActionKind get kind => DartCodeActionKind.refactorAdd;
|
CodeActionKind get kind => DartCodeActionKind.refactorAdd;
|
||||||
|
|
||||||
|
@override
|
||||||
|
/// This refactor supports input using the new system (see
|
||||||
|
/// [buildInteractiveForm]) but not using the old one, so there are no
|
||||||
|
/// parameters.
|
||||||
|
List<CommandParameter> get parameters => [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => constTitle;
|
String get title => constTitle;
|
||||||
|
|
||||||
|
/// Builds the [InteractiveForm] to collect input for this refactor.
|
||||||
|
@override
|
||||||
|
ErrorOr<InteractiveForm> buildInteractiveForm() {
|
||||||
|
var element = selection?.constructor(mustNotHaveName: true);
|
||||||
|
if (element == null) {
|
||||||
|
// We shouldn't have gotten here if the selection was not valid for this
|
||||||
|
// refactor, but return a useful error to aid debugging if so.
|
||||||
|
return error(
|
||||||
|
ErrorCodes.InvalidParams,
|
||||||
|
'The selection is not valid for adding a constructor name',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameField = ValidatableFormField(
|
||||||
|
id: 'name',
|
||||||
|
description: 'Constructor Name',
|
||||||
|
required: true,
|
||||||
|
defaultValue: _computeName(element),
|
||||||
|
type: FormFieldTypeString(),
|
||||||
|
validate: wrapRefactorValidationFunction(validateConstructorName),
|
||||||
|
);
|
||||||
|
|
||||||
|
return success(createForm([nameField]));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ComputeStatus> compute(
|
Future<ComputeStatus> compute(
|
||||||
List<Object?> commandArguments,
|
List<Object?> commandArguments,
|
||||||
ChangeBuilder builder,
|
ChangeBuilder builder,
|
||||||
) async {
|
) async {
|
||||||
|
// Handle optional name in the arguments (if Interactive Forms were used).
|
||||||
|
var constructorName = switch (commandArguments) {
|
||||||
|
[String name] => name,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
var element = selection?.constructor(mustNotHaveName: true);
|
var element = selection?.constructor(mustNotHaveName: true);
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
// This should never happen because `isAvailable` would have returned
|
// This should never happen because `isAvailable` would have returned
|
||||||
@@ -45,7 +86,8 @@ class AddConstructorName extends RefactoringProducer {
|
|||||||
if (refactoring == null) {
|
if (refactoring == null) {
|
||||||
return ComputeStatusFailure();
|
return ComputeStatusFailure();
|
||||||
}
|
}
|
||||||
refactoring.newName = _computeName(element);
|
constructorName ??= _computeName(element);
|
||||||
|
refactoring.newName = constructorName;
|
||||||
var status = await refactoring.checkAllConditions();
|
var status = await refactoring.checkAllConditions();
|
||||||
if (status.hasError) {
|
if (status.hasError) {
|
||||||
return ComputeStatusFailure();
|
return ComputeStatusFailure();
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:analysis_server/lsp_protocol/protocol.dart';
|
import 'package:analysis_server/lsp_protocol/protocol.dart';
|
||||||
|
import 'package:analysis_server/src/lsp/extensions/code_action.dart';
|
||||||
import 'package:analysis_server/src/services/refactoring/add_constructor_name.dart';
|
import 'package:analysis_server/src/services/refactoring/add_constructor_name.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||||
|
|
||||||
|
import '../../../support/interactive_forms.dart';
|
||||||
|
import '../../../utils/lsp_protocol_extensions.dart';
|
||||||
import 'refactoring_test_support.dart';
|
import 'refactoring_test_support.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -17,7 +21,70 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@reflectiveTest
|
@reflectiveTest
|
||||||
class AddConstructorNameInClassTest extends _AddConstructorNameTest {
|
class AddConstructorNameInClassTest extends _AddConstructorNameTest
|
||||||
|
with InteractiveFormsTestMixin {
|
||||||
|
Future<void> test_interactiveForm_clientModifiedValues() async {
|
||||||
|
setSupportedInteractiveFormInputKinds({'string'});
|
||||||
|
|
||||||
|
var originalSource = '''
|
||||||
|
class C^() {}
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
C();
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
var expected = '''
|
||||||
|
>>>>>>>>>> lib/main.dart
|
||||||
|
class C.customName() {}
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
C.customName();
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
|
||||||
|
addTestSource(originalSource);
|
||||||
|
|
||||||
|
await initializeServer(experimentalInteractiveForms: true);
|
||||||
|
var action = await expectCodeActionWithTitle(refactoringTitle);
|
||||||
|
var completedCommand = await completeInteractiveForm(action.command!, {
|
||||||
|
'name': 'customName',
|
||||||
|
});
|
||||||
|
|
||||||
|
await verifyCommandEdits(completedCommand, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> test_interactiveForm_expectedFields() async {
|
||||||
|
setSupportedInteractiveFormInputKinds({'string'});
|
||||||
|
|
||||||
|
var originalSource = '''
|
||||||
|
class C^() {}
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
C();
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
|
||||||
|
addTestSource(originalSource);
|
||||||
|
|
||||||
|
await initializeServer(experimentalInteractiveForms: true);
|
||||||
|
var action = await expectCodeActionWithTitle(refactoringTitle);
|
||||||
|
var command = action.asCommand;
|
||||||
|
var interactiveCommand = await resolveCommand(
|
||||||
|
ExecuteCommandParams(
|
||||||
|
command: command.command,
|
||||||
|
arguments: command.arguments,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(interactiveCommand.formFields, hasLength(1));
|
||||||
|
var field = interactiveCommand.formFields!.single;
|
||||||
|
expect(field.id, 'name');
|
||||||
|
expect(field.description, 'Constructor Name');
|
||||||
|
expect(field.defaultValue, 'name');
|
||||||
|
expect(field.error, isNull);
|
||||||
|
expect(field.type, isA<FormFieldTypeString>());
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> test_primary() async {
|
Future<void> test_primary() async {
|
||||||
var originalSource = '''
|
var originalSource = '''
|
||||||
class C^() {}
|
class C^() {}
|
||||||
|
|||||||
Reference in New Issue
Block a user