blob: fb21d070e007fa3383a3e9f7cb33b3d95b884097 [file] [log] [blame]
// Copyright (c) 2017, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/source.dart';
const _FLUTTER_WIDGET_NAME = "Widget";
const _FLUTTER_WIDGET_URI = "package:flutter/src/widgets/framework.dart";
void convertFlutterChildToChildren(
InstanceCreationExpression childArg,
NamedExpression namedExp,
String eol,
Function getNodeText,
Function getLinePrefix,
Function getIndent,
Function getText,
Function _addInsertEdit,
Function _addRemoveEdit,
Function _addReplaceEdit,
Function rangeNode) {
int childLoc = namedExp.offset + 'child'.length;
_addInsertEdit(childLoc, 'ren');
int listLoc = childArg.offset;
String childArgSrc = getNodeText(childArg);
if (!childArgSrc.contains(eol)) {
_addInsertEdit(listLoc, '<Widget>[');
_addInsertEdit(listLoc + childArg.length, ']');
} else {
int newlineLoc = childArgSrc.lastIndexOf(eol);
if (newlineLoc == childArgSrc.length) {
newlineLoc -= 1;
}
String indentOld = getLinePrefix(childArg.offset + 1 + newlineLoc);
String indentNew = '$indentOld${getIndent(1)}';
// The separator includes 'child:' but that has no newlines.
String separator =
getText(namedExp.offset, childArg.offset - namedExp.offset);
String prefix = separator.contains(eol) ? "" : "$eol$indentNew";
if (prefix.isEmpty) {
_addInsertEdit(namedExp.offset + 'child:'.length, ' <Widget>[');
_addRemoveEdit(new SourceRange(childArg.offset - 2, 2));
} else {
_addInsertEdit(listLoc, '<Widget>[');
}
String newChildArgSrc = childArgSrc.replaceAll(
new RegExp("^$indentOld", multiLine: true), "$indentNew");
newChildArgSrc = "$prefix$newChildArgSrc,$eol$indentOld]";
_addReplaceEdit(rangeNode(childArg), newChildArgSrc);
}
}
/**
* 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 Flutter instance creation expression that is the value of the
* 'child' argument of the given [newExpr], or null if none.
*/
InstanceCreationExpression findChildWidget(InstanceCreationExpression newExpr) {
NamedExpression child = findChildArgument(newExpr);
return getChildWidget(child);
}
/**
* If the given [node] is a simple identifier, find the named expression whose
* name is the given [name] that is an argument to a Flutter instance creation
* expression. Return null if any condition cannot be satisfied.
*/
NamedExpression findFlutterNamedExpression(AstNode node, String name) {
if (node is! SimpleIdentifier) {
return null;
}
SimpleIdentifier namedArg = node;
NamedExpression namedExp;
if (namedArg.parent is Label && namedArg.parent.parent is NamedExpression) {
namedExp = namedArg.parent.parent;
if (namedArg.name != name || namedExp.expression == null) {
return null;
}
} else {
return null;
}
if (namedExp.parent?.parent is! InstanceCreationExpression) {
return null;
}
InstanceCreationExpression newExpr = namedExp.parent.parent;
if (newExpr == null || !isFlutterInstanceCreationExpression(newExpr)) {
return null;
}
return namedExp;
}
ListLiteral getChildList(NamedExpression child) {
if (child.expression is ListLiteral) {
ListLiteral list = child.expression;
if (list.elements.isEmpty ||
list.elements.every((element) =>
element is InstanceCreationExpression &&
isFlutterInstanceCreationExpression(element))) {
return list;
}
}
return null;
}
/**
* Return the Flutter instance creation expression that is the value of the
* given [child], or null if none. If [strict] is true, require the value to
* also have a 'child' argument.
*/
InstanceCreationExpression getChildWidget(NamedExpression child,
[bool strict = false]) {
if (child?.expression is InstanceCreationExpression) {
InstanceCreationExpression childNewExpr = child.expression;
if (isFlutterInstanceCreationExpression(childNewExpr)) {
if (!strict || (findChildArgument(childNewExpr) != null)) {
return childNewExpr;
}
}
}
return null;
}
/**
* Return the instance creation expression that surrounds the given
* [node], if any, else null. The [node] may be the instance creation
* expression itself or the identifier that names the constructor.
*/
InstanceCreationExpression identifyNewExpression(AstNode node) {
InstanceCreationExpression newExpr;
if (node is SimpleIdentifier) {
if (node.parent is ConstructorName &&
node.parent.parent is InstanceCreationExpression) {
newExpr = node.parent.parent;
} else if (node.parent?.parent is ConstructorName &&
node.parent.parent?.parent is InstanceCreationExpression) {
newExpr = node.parent.parent.parent;
}
} else if (node is InstanceCreationExpression) {
newExpr = node;
}
return newExpr;
}
/**
* Return `true` if the given [newExpr] is a constructor invocation for a
* class that has the Flutter class Widget as a superclass.
*/
bool isFlutterInstanceCreationExpression(InstanceCreationExpression newExpr) {
ClassElement classElement = newExpr.staticElement?.enclosingElement;
return isFlutterWidget(classElement);
}
/**
* Return `true` if the given [classElement] has the Flutter class Widget as a
* superclass.
*/
bool isFlutterWidget(ClassElement classElement) {
InterfaceType superType = classElement?.allSupertypes?.firstWhere(
(InterfaceType type) => _FLUTTER_WIDGET_NAME == type.name,
orElse: () => null);
if (superType == null) {
return false;
}
Uri uri = superType.element?.source?.uri;
if (uri.toString() != _FLUTTER_WIDGET_URI) {
return false;
}
return true;
}