New Quick Asists for Flutter: 'Replace with child' and 'Replace with children'.

R=brianwilkerson@google.com, devoncarew@google.com

Bug: https://github.com/flutter/flutter-intellij/issues/1283
Change-Id: Ic50de9739cdab5cd82649dcbd8370c5e728d2ee3
     https://github.com/flutter/flutter-intellij/issues/1756
Reviewed-on: https://dart-review.googlesource.com/40240
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Devon Carew <devoncarew@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-02-09 17:09:18 +00:00
parent 87a395bd09
commit 59716fede1
5 changed files with 257 additions and 3 deletions

View file

@ -77,6 +77,8 @@ class DartAssistKind {
const AssistKind('EXTRACT_CLASS', 30, "Extract class into file '{0}'");
static const FLUTTER_CONVERT_TO_STATEFUL_WIDGET = const AssistKind(
"FLUTTER_CONVERT_TO_STATEFUL_WIDGET", 30, "Convert to StatefulWidget");
static const FLUTTER_REPLACE_WITH_CHILDREN = const AssistKind(
"FLUTTER_REPLACE_WITH_CHILDREN", 30, "Replace with children");
static const IMPORT_ADD_SHOW =
const AssistKind('IMPORT_ADD_SHOW', 30, "Add explicit 'show' combinator");
static const INTRODUCE_LOCAL_CAST_TYPE = const AssistKind(

View file

@ -143,6 +143,8 @@ class AssistProcessor {
await _addProposal_convertToStatefulWidget();
await _addProposal_encapsulateField();
await _addProposal_exchangeOperands();
await _addProposal_flutterReplaceWithChild();
await _addProposal_flutterReplaceWithChildren();
await _addProposal_importAddShow();
await _addProposal_introduceLocalTestedType();
await _addProposal_invertIf();
@ -1406,6 +1408,71 @@ class AssistProcessor {
_addAssistFromBuilder(changeBuilder, DartAssistKind.EXCHANGE_OPERANDS);
}
Future<Null> _addProposal_flutterReplaceWithChild() async {
var widgetCreation = flutter.identifyNewExpression(node);
if (widgetCreation == null) {
return;
}
var childArgument = flutter.findChildArgument(widgetCreation);
if (childArgument == null) {
return;
}
// child: new ThisWidget(child: ourChild)
// children: [foo, new ThisWidget(child: ourChild), bar]
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
var childExpression = childArgument.expression;
var childText = utils.getNodeText(childExpression);
var indentOld = utils.getLinePrefix(childExpression.offset);
var indentNew = utils.getLinePrefix(widgetCreation.offset);
childText = _replaceSourceIndent(childText, indentOld, indentNew);
builder.addSimpleReplacement(range.node(widgetCreation), childText);
});
_addAssistFromBuilder(
changeBuilder, DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN);
}
Future<Null> _addProposal_flutterReplaceWithChildren() async {
var widgetCreation = flutter.identifyNewExpression(node);
if (widgetCreation == null) {
return;
}
// We can inline the list of our children only into another list.
var widgetParentNode = widgetCreation.parent;
if (widgetParentNode is! ListLiteral) {
return;
}
// Prepare the list of our children.
List<Expression> childrenExpressions;
{
var childrenArgument = flutter.findChildrenArgument(widgetCreation);
var childrenExpression = childrenArgument?.expression;
if (childrenExpression is ListLiteral &&
childrenExpression.elements.isNotEmpty) {
childrenExpressions = childrenExpression.elements;
} else {
return;
}
}
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
var firstChild = childrenExpressions.first;
var lastChild = childrenExpressions.last;
var childText = utils.getRangeText(range.startEnd(firstChild, lastChild));
var indentOld = utils.getLinePrefix(firstChild.offset);
var indentNew = utils.getLinePrefix(widgetCreation.offset);
childText = _replaceSourceIndent(childText, indentOld, indentNew);
builder.addSimpleReplacement(range.node(widgetCreation), childText);
});
_addAssistFromBuilder(
changeBuilder, DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN);
}
Future<Null> _addProposal_importAddShow() async {
// prepare ImportDirective
ImportDirective importDirective =

View file

@ -101,14 +101,23 @@ void convertChildToChildren2(
}
/**
* Return the named expression representing the 'child' argument of the given
* [newExpr], or null if none.
* Return the named expression representing the `child` argument of the given
* [newExpr], or `null` if none.
*/
NamedExpression findChildArgument(InstanceCreationExpression newExpr) =>
newExpr.argumentList.arguments.firstWhere(
(arg) => arg is NamedExpression && arg.name.label.name == 'child',
orElse: () => null);
/**
* Return the named expression representing the `children` argument of the
* given [newExpr], or `null` if none.
*/
NamedExpression findChildrenArgument(InstanceCreationExpression newExpr) =>
newExpr.argumentList.arguments.firstWhere(
(arg) => arg is NamedExpression && arg.name.label.name == 'children',
orElse: () => null);
/**
* Return the Flutter instance creation expression that is the value of the
* 'child' argument of the given [newExpr], or null if none.

View file

@ -2628,6 +2628,182 @@ main() {
''');
}
test_flutterReplaceWithChild_OK_childIntoChild_multiLine() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Center(
child: new /*caret*/Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
heightFactor: 0.5,
child: new Text('foo'),
),
),
),
],
);
}
''');
_setCaretLocation();
await assertHasAssist(DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN, '''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Center(
child: new Center(
heightFactor: 0.5,
child: new Text('foo'),
),
),
],
);
}
''');
}
test_flutterReplaceWithChild_OK_childIntoChild_singleLine() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/material.dart';
main() {
new Padding(
padding: const EdgeInsets.all(8.0),
child: new /*caret*/Center(
heightFactor: 0.5,
child: new Text('foo'),
),
);
}
''');
_setCaretLocation();
await assertHasAssist(DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN, '''
import 'package:flutter/material.dart';
main() {
new Padding(
padding: const EdgeInsets.all(8.0),
child: new Text('foo'),
);
}
''');
}
test_flutterReplaceWithChild_OK_childIntoChildren() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Text('foo'),
new /*caret*/Center(
heightFactor: 0.5,
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new Text('bar'),
),
),
new Text('baz'),
],
);
}
''');
_setCaretLocation();
await assertHasAssist(DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN, '''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Text('foo'),
new Padding(
padding: const EdgeInsets.all(8.0),
child: new Text('bar'),
),
new Text('baz'),
],
);
}
''');
}
test_flutterReplaceWithChildren_BAD_parentChild() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/material.dart';
main() {
new Center(
child: new /*caret*/Row(
children: [
new Text('aaa'),
new Text('bbb'),
],
),
);
}
''');
_setCaretLocation();
await assertNoAssist(DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN);
}
test_flutterReplaceWithChildren_OK_intoChildren() async {
addFlutterPackage();
await resolveTestUnit('''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Text('aaa'),
new /*caret*/Column(
children: [
new Row(
children: [
new Text('bbb'),
new Text('ccc'),
],
),
new Row(
children: [
new Text('ddd'),
new Text('eee'),
],
),
],
),
new Text('fff'),
],
);
}
''');
_setCaretLocation();
await assertHasAssist(DartAssistKind.FLUTTER_REPLACE_WITH_CHILDREN, '''
import 'package:flutter/material.dart';
main() {
new Column(
children: <Widget>[
new Text('aaa'),
new Row(
children: [
new Text('bbb'),
new Text('ccc'),
],
),
new Row(
children: [
new Text('ddd'),
new Text('eee'),
],
),
new Text('fff'),
],
);
}
''');
}
test_importAddShow_BAD_hasShow() async {
await resolveTestUnit('''
import 'dart:math' show PI;

View file

@ -138,7 +138,7 @@ export 'package:flutter/painting.dart';
export 'package:flutter/rendering.dart';
class Center extends StatelessWidget {
const Center({Widget child, Key key});
const Center({Key key, double heightFactor, Widget child});
}
class Column extends Flex {