| // Copyright (c) 2020, 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/src/services/correction/assist.dart'; |
| import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart'; |
| import 'package:analysis_server/src/services/correction/statement_analyzer.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/source/source_range.dart'; |
| import 'package:analyzer_plugin/utilities/assist/assist.dart'; |
| import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; |
| |
| class SurroundWith extends MultiCorrectionProducer { |
| @override |
| Iterable<CorrectionProducer> get producers sync* { |
| // If the node is the CompilationUnit, the selected statements must span multiple |
| // top level items and cannot be surrounded with anything. |
| if (node is CompilationUnit) { |
| return; |
| } |
| |
| // prepare selected statements |
| var selectionAnalyzer = StatementAnalyzer( |
| resolvedResult, SourceRange(selectionOffset, selectionLength)); |
| selectionAnalyzer.analyze(); |
| var selectedNodes = selectionAnalyzer.selectedNodes; |
| // convert nodes to statements |
| var selectedStatements = <Statement>[]; |
| for (var selectedNode in selectedNodes) { |
| if (selectedNode is Statement) { |
| selectedStatements.add(selectedNode); |
| } |
| } |
| // we want only statements in blocks |
| for (var statement in selectedStatements) { |
| if (statement.parent is! Block) { |
| return; |
| } |
| } |
| // we want only statements |
| if (selectedStatements.isEmpty || |
| selectedStatements.length != selectedNodes.length) { |
| return; |
| } |
| // prepare statement information |
| var firstStatement = selectedStatements[0]; |
| var statementsRange = utils.getLinesRangeStatements(selectedStatements); |
| // prepare environment |
| var indentOld = utils.getNodePrefix(firstStatement); |
| var indentNew = '$indentOld${utils.getIndent(1)}'; |
| var indentedCode = |
| utils.replaceSourceRangeIndent(statementsRange, indentOld, indentNew); |
| |
| yield _SurroundWithBlock( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithDoWhile( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithFor(statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithForIn( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithIf(statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithSetState( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithTryCatch( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithTryFinally( |
| statementsRange, indentOld, indentNew, indentedCode); |
| yield _SurroundWithWhile( |
| statementsRange, indentOld, indentNew, indentedCode); |
| } |
| |
| /// Return an instance of this class. Used as a tear-off in `AssistProcessor`. |
| static SurroundWith newInstance() => SurroundWith(); |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| abstract class _SurroundWith extends CorrectionProducer { |
| final SourceRange statementsRange; |
| |
| final String indentOld; |
| |
| final String indentNew; |
| |
| final String indentedCode; |
| |
| _SurroundWith( |
| this.statementsRange, this.indentOld, this.indentNew, this.indentedCode); |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithBlock extends _SurroundWith { |
| _SurroundWithBlock(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_BLOCK; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addSimpleInsertion(statementsRange.offset, '$indentOld{$eol'); |
| builder.addSimpleReplacement( |
| statementsRange, |
| utils.replaceSourceRangeIndent( |
| statementsRange, indentOld, indentNew)); |
| builder.addSimpleInsertion(statementsRange.end, '$indentOld}$eol'); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithDoWhile extends _SurroundWith { |
| _SurroundWithDoWhile(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_DO_WHILE; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('do {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('} while ('); |
| builder.addSimpleLinkedEdit('CONDITION', 'condition'); |
| builder.write(');'); |
| builder.selectHere(); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithFor extends _SurroundWith { |
| _SurroundWithFor(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_FOR; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('for (var '); |
| builder.addSimpleLinkedEdit('VAR', 'v'); |
| builder.write(' = '); |
| builder.addSimpleLinkedEdit('INIT', 'init'); |
| builder.write('; '); |
| builder.addSimpleLinkedEdit('CONDITION', 'condition'); |
| builder.write('; '); |
| builder.addSimpleLinkedEdit('INCREMENT', 'increment'); |
| builder.write(') {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.selectHere(); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithForIn extends _SurroundWith { |
| _SurroundWithForIn(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_FOR_IN; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('for (var '); |
| builder.addSimpleLinkedEdit('NAME', 'item'); |
| builder.write(' in '); |
| builder.addSimpleLinkedEdit('ITERABLE', 'iterable'); |
| builder.write(') {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.selectHere(); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithIf extends _SurroundWith { |
| _SurroundWithIf(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_IF; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('if ('); |
| builder.addSimpleLinkedEdit('CONDITION', 'condition'); |
| builder.write(') {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.selectHere(); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithSetState extends _SurroundWith { |
| _SurroundWithSetState(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_SET_STATE; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| var classDeclaration = node.parent.thisOrAncestorOfType<ClassDeclaration>(); |
| if (classDeclaration != null && |
| flutter.isState(classDeclaration.declaredElement)) { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.writeln('setState(() {'); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.selectHere(); |
| builder.writeln('});'); |
| }); |
| }); |
| } |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithTryCatch extends _SurroundWith { |
| _SurroundWithTryCatch(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_TRY_CATCH; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('try {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('} on '); |
| builder.addSimpleLinkedEdit('EXCEPTION_TYPE', 'Exception'); |
| builder.write(' catch ('); |
| builder.addSimpleLinkedEdit('EXCEPTION_VAR', 'e'); |
| builder.write(') {'); |
| builder.write(eol); |
| // |
| builder.write(indentNew); |
| builder.addSimpleLinkedEdit('CATCH', '// TODO'); |
| builder.selectHere(); |
| builder.write(eol); |
| // |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithTryFinally extends _SurroundWith { |
| _SurroundWithTryFinally(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_TRY_FINALLY; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('try {'); |
| builder.write(eol); |
| // |
| builder.write(indentedCode); |
| // |
| builder.write(indentOld); |
| builder.write('} finally {'); |
| builder.write(eol); |
| // |
| builder.write(indentNew); |
| builder.addSimpleLinkedEdit('FINALLY', '// TODO'); |
| builder.selectHere(); |
| builder.write(eol); |
| // |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |
| |
| /// A correction processor that can make one of the possible change computed by |
| /// the [SurroundWith] producer. |
| class _SurroundWithWhile extends _SurroundWith { |
| _SurroundWithWhile(SourceRange statementsRange, String indentOld, |
| String indentNew, String indentedCode) |
| : super(statementsRange, indentOld, indentNew, indentedCode); |
| |
| @override |
| AssistKind get assistKind => DartAssistKind.SURROUND_WITH_WHILE; |
| |
| @override |
| Future<void> compute(ChangeBuilder builder) async { |
| await builder.addDartFileEdit(file, (builder) { |
| builder.addReplacement(statementsRange, (builder) { |
| builder.write(indentOld); |
| builder.write('while ('); |
| builder.addSimpleLinkedEdit('CONDITION', 'condition'); |
| builder.write(') {'); |
| builder.write(eol); |
| builder.write(indentedCode); |
| builder.write(indentOld); |
| builder.write('}'); |
| builder.selectHere(); |
| builder.write(eol); |
| }); |
| }); |
| } |
| } |