blob: 50179b7cd90df6b82ac0b46977845001d64369f2 [file] [log] [blame]
// Copyright (c) 2014, 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 'package:analysis_server/plugin/edit/assist/assist_core.dart';
import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
import 'package:analysis_server/src/services/correction/dart/add_return_type.dart';
import 'package:analysis_server/src/services/correction/dart/add_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/assign_to_local_variable.dart';
import 'package:analysis_server/src/services/correction/dart/convert_add_all_to_spread.dart';
import 'package:analysis_server/src/services/correction/dart/convert_class_to_enum.dart';
import 'package:analysis_server/src/services/correction/dart/convert_class_to_mixin.dart';
import 'package:analysis_server/src/services/correction/dart/convert_conditional_expression_to_if_element.dart';
import 'package:analysis_server/src/services/correction/dart/convert_documentation_into_block.dart';
import 'package:analysis_server/src/services/correction/dart/convert_documentation_into_line.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_async_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_block_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_final_field.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_for_index.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_getter.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_is_not.dart';
import 'package:analysis_server/src/services/correction/dart/convert_into_is_not_empty.dart';
import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_part_of_to_uri.dart';
import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_field_parameter.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_generic_function_syntax.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_if_case_statement.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_if_case_statement_chain.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_int_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_map_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_multiline_string.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_normal_parameter.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_package_import.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_relative_import.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_super_parameters.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_switch_expression.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_switch_statement.dart';
import 'package:analysis_server/src/services/correction/dart/destructure_local_variable_assignment.dart';
import 'package:analysis_server/src/services/correction/dart/encapsulate_field.dart';
import 'package:analysis_server/src/services/correction/dart/exchange_operands.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_children.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_stateful_widget.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_convert_to_stateless_widget.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_move_down.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_move_up.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_remove_widget.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_child.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_swap_with_parent.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_wrap.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_wrap_builder.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_wrap_generic.dart';
import 'package:analysis_server/src/services/correction/dart/flutter_wrap_stream_builder.dart';
import 'package:analysis_server/src/services/correction/dart/import_add_show.dart';
import 'package:analysis_server/src/services/correction/dart/inline_invocation.dart';
import 'package:analysis_server/src/services/correction/dart/invert_if_statement.dart';
import 'package:analysis_server/src/services/correction/dart/join_if_with_inner.dart';
import 'package:analysis_server/src/services/correction/dart/join_if_with_outer.dart';
import 'package:analysis_server/src/services/correction/dart/join_variable_declaration.dart';
import 'package:analysis_server/src/services/correction/dart/remove_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/replace_conditional_with_if_else.dart';
import 'package:analysis_server/src/services/correction/dart/replace_if_else_with_conditional.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_var.dart';
import 'package:analysis_server/src/services/correction/dart/shadow_field.dart';
import 'package:analysis_server/src/services/correction/dart/sort_child_property_last.dart';
import 'package:analysis_server/src/services/correction/dart/split_and_condition.dart';
import 'package:analysis_server/src/services/correction/dart/split_variable_declaration.dart';
import 'package:analysis_server/src/services/correction/dart/surround_with.dart';
import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
import 'package:analysis_server/src/services/correction/fix_processor.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/util/file_paths.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart'
hide AssistContributor;
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
/// The computer for Dart assists.
class AssistProcessor {
/// A list of the generators used to produce assists.
static const List<ProducerGenerator> _generators = [
AddDiagnosticPropertyReference.new,
AddReturnType.new,
AddTypeAnnotation.bulkFixable,
AssignToLocalVariable.new,
ConvertAddAllToSpread.new,
ConvertClassToEnum.new,
ConvertClassToMixin.new,
ConvertConditionalExpressionToIfElement.new,
ConvertDocumentationIntoBlock.new,
ConvertDocumentationIntoLine.new,
ConvertIfStatementToSwitchStatement.new,
ConvertIntoAsyncBody.new,
ConvertIntoBlockBody.missingBody,
ConvertIntoFinalField.new,
ConvertIntoForIndex.new,
ConvertIntoGetter.new,
ConvertIntoIsNot.new,
ConvertIntoIsNotEmpty.new,
ConvertMapFromIterableToForLiteral.new,
ConvertPartOfToUri.new,
ConvertSwitchExpressionToSwitchStatement.new,
ConvertToDoubleQuotes.new,
ConvertToExpressionFunctionBody.new,
ConvertToFieldParameter.new,
ConvertToGenericFunctionSyntax.new,
ConvertToIfCaseStatement.new,
ConvertToIfCaseStatementChain.new,
ConvertToIntLiteral.new,
ConvertToMapLiteral.new,
ConvertToMultilineString.new,
ConvertToNormalParameter.new,
ConvertToNullAware.new,
ConvertToPackageImport.new,
ConvertToRelativeImport.new,
ConvertToSetLiteral.new,
ConvertToSingleQuotes.new,
ConvertToSuperParameters.new,
ConvertToSwitchExpression.new,
DestructureLocalVariableAssignment.new,
EncapsulateField.new,
ExchangeOperands.new,
FlutterConvertToChildren.new,
FlutterConvertToStatefulWidget.new,
FlutterConvertToStatelessWidget.new,
FlutterMoveDown.new,
FlutterMoveUp.new,
FlutterRemoveWidget.new,
FlutterSwapWithChild.new,
FlutterSwapWithParent.new,
FlutterWrapBuilder.new,
FlutterWrapGeneric.new,
FlutterWrapStreamBuilder.new,
ImportAddShow.new,
InlineInvocation.new,
InvertIfStatement.new,
JoinIfWithInner.new,
JoinIfWithOuter.new,
JoinVariableDeclaration.new,
RemoveTypeAnnotation.other,
ReplaceConditionalWithIfElse.new,
ReplaceIfElseWithConditional.new,
ReplaceWithVar.new,
ShadowField.new,
SortChildPropertyLast.new,
SplitAndCondition.new,
SplitVariableDeclaration.new,
UseCurlyBraces.new,
];
/// A list of the multi-generators used to produce assists.
static const List<MultiProducerGenerator> _multiGenerators = [
FlutterWrap.new,
SurroundWith.new,
];
final DartAssistContext _assistContext;
final List<Assist> _assists = [];
AssistProcessor(this._assistContext);
Future<List<Assist>> compute() async {
if (isMacroGenerated(_assistContext.resolveResult.file.path)) {
return _assists;
}
await _addFromProducers();
return _assists;
}
void _addAssistFromBuilder(ChangeBuilder builder, AssistKind kind,
{List<Object>? args}) {
var change = builder.sourceChange;
if (change.edits.isEmpty) {
return;
}
change.id = kind.id;
change.message = formatList(kind.message, args);
_assists.add(Assist(kind, change));
}
Future<void> _addFromProducers() async {
var context = CorrectionProducerContext.createResolved(
selectionOffset: _assistContext.selectionOffset,
selectionLength: _assistContext.selectionLength,
resolvedResult: _assistContext.resolveResult,
);
Future<void> compute(CorrectionProducer producer) async {
var builder =
ChangeBuilder(workspace: _assistContext.workspace, eol: producer.eol);
try {
await producer.compute(builder);
var assistKind = producer.assistKind;
if (assistKind != null) {
_addAssistFromBuilder(builder, assistKind,
args: producer.assistArguments);
}
} on ConflictingEditException catch (exception, stackTrace) {
// Handle the exception by (a) not adding an assist based on the
// producer and (b) logging the exception.
_assistContext.instrumentationService
.logException(exception, stackTrace);
}
}
for (var generator in _generators) {
if (!_generatorAppliesToAnyLintRule(
generator,
_assistContext.producerGeneratorsForLintRules[generator] ??
<LintCode>{},
)) {
var producer = generator(context: context);
await compute(producer);
}
}
for (var multiGenerator in _multiGenerators) {
var multiProducer = multiGenerator(context: context);
for (var producer in await multiProducer.producers) {
await compute(producer);
}
}
}
/// Returns whether [generator] applies to any enabled lint rule, among
/// [errorCodes].
bool _generatorAppliesToAnyLintRule(
ProducerGenerator generator,
Set<LintCode> errorCodes,
) {
var selectionEnd =
_assistContext.selectionOffset + _assistContext.selectionLength;
var locator = NodeLocator(_assistContext.selectionOffset, selectionEnd);
var node = locator.searchWithin(_assistContext.resolveResult.unit);
if (node == null) {
return false;
}
var fileOffset = node.offset;
for (var error in _assistContext.resolveResult.errors) {
var errorSource = error.source;
if (_assistContext.resolveResult.path == errorSource.fullName) {
if (fileOffset >= error.offset &&
fileOffset <= error.offset + error.length) {
if (errorCodes.contains(error.errorCode)) {
return true;
}
}
}
}
return false;
}
static Map<ProducerGenerator, Set<LintCode>> computeLintRuleMap() => {
for (var generator in _generators)
generator: {
for (var MapEntry(key: lintName, value: generators)
in FixProcessor.lintProducerMap.entries)
if (generators.contains(generator)) lintName,
},
};
}