[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.
|
||||
|
||||
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/legacy/naming_conventions.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/utilities/extensions/selection.dart';
|
||||
import 'package:analyzer/dart/element/element.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:language_server_protocol/protocol_custom_generated.dart';
|
||||
import 'package:language_server_protocol/protocol_generated.dart';
|
||||
|
||||
/// 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 constTitle = 'Add a name to the constructor';
|
||||
@@ -26,14 +30,51 @@ class AddConstructorName extends RefactoringProducer {
|
||||
@override
|
||||
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
|
||||
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
|
||||
Future<ComputeStatus> compute(
|
||||
List<Object?> commandArguments,
|
||||
ChangeBuilder builder,
|
||||
) 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);
|
||||
if (element == null) {
|
||||
// This should never happen because `isAvailable` would have returned
|
||||
@@ -45,7 +86,8 @@ class AddConstructorName extends RefactoringProducer {
|
||||
if (refactoring == null) {
|
||||
return ComputeStatusFailure();
|
||||
}
|
||||
refactoring.newName = _computeName(element);
|
||||
constructorName ??= _computeName(element);
|
||||
refactoring.newName = constructorName;
|
||||
var status = await refactoring.checkAllConditions();
|
||||
if (status.hasError) {
|
||||
return ComputeStatusFailure();
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
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:test/test.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';
|
||||
|
||||
void main() {
|
||||
@@ -17,7 +21,70 @@ void main() {
|
||||
}
|
||||
|
||||
@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 {
|
||||
var originalSource = '''
|
||||
class C^() {}
|
||||
|
||||
Reference in New Issue
Block a user