blob: be324d42cc868dbd2d44aa92ca1d12f3caad3b9f [file] [log] [blame]
// 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);
});
});
}
}