| Index: pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
|
| diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
|
| index 0402955e4db4f14f726c2f6cc07e8a7770d66b9c..aa17f1ae497b417bdba4df56185aaef823512419 100644
|
| --- a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
|
| +++ b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
|
| @@ -323,7 +323,8 @@ class ExtractMethodRefactoringImpl extends RefactoringImpl
|
| String returnExpressionSource = _getMethodBodySource();
|
| // closure
|
| if (_selectionFunctionExpression != null) {
|
| - declarationSource = '$name$returnExpressionSource';
|
| + String returnTypeCode = _getExpectedClosureReturnTypeCode();
|
| + declarationSource = '$returnTypeCode$name$returnExpressionSource';
|
| if (_selectionFunctionExpression.body is ExpressionFunctionBody) {
|
| declarationSource += ';';
|
| }
|
| @@ -428,36 +429,48 @@ class ExtractMethodRefactoringImpl extends RefactoringImpl
|
| * location of this [DartExpression] in AST allows extracting.
|
| */
|
| RefactoringStatus _checkSelection() {
|
| + // Check for implicitly selected closure.
|
| + {
|
| + FunctionExpression function = _findFunctionExpression();
|
| + if (function != null) {
|
| + _selectionFunctionExpression = function;
|
| + selectionRange = range.node(function);
|
| + _parentMember = getEnclosingClassOrUnitMember(function);
|
| + return new RefactoringStatus();
|
| + }
|
| + }
|
| +
|
| _ExtractMethodAnalyzer selectionAnalyzer =
|
| new _ExtractMethodAnalyzer(unit, selectionRange);
|
| unit.accept(selectionAnalyzer);
|
| - // may be fatal error
|
| + // May be a fatal error.
|
| {
|
| RefactoringStatus status = selectionAnalyzer.status;
|
| if (status.hasFatalError) {
|
| return status;
|
| }
|
| }
|
| - // check selected nodes
|
| List<AstNode> selectedNodes = selectionAnalyzer.selectedNodes;
|
| +
|
| + // Check selected nodes.
|
| if (!selectedNodes.isEmpty) {
|
| - AstNode coveringNode = selectionAnalyzer.coveringNode;
|
| - _parentMember = getEnclosingClassOrUnitMember(coveringNode);
|
| + AstNode selectedNode = selectedNodes.first;
|
| + _parentMember = getEnclosingClassOrUnitMember(selectedNode);
|
| // single expression selected
|
| - if (selectedNodes.length == 1 &&
|
| - !utils.selectionIncludesNonWhitespaceOutsideNode(
|
| - selectionRange, selectionAnalyzer.firstSelectedNode)) {
|
| - AstNode selectedNode = selectionAnalyzer.firstSelectedNode;
|
| - if (selectedNode is Expression) {
|
| - _selectionExpression = selectedNode;
|
| - // additional check for closure
|
| - if (_selectionExpression is FunctionExpression) {
|
| - _selectionFunctionExpression =
|
| - _selectionExpression as FunctionExpression;
|
| - _selectionExpression = null;
|
| + if (selectedNodes.length == 1) {
|
| + if (!utils.selectionIncludesNonWhitespaceOutsideNode(
|
| + selectionRange, selectedNode)) {
|
| + if (selectedNode is Expression) {
|
| + _selectionExpression = selectedNode;
|
| + // additional check for closure
|
| + if (_selectionExpression is FunctionExpression) {
|
| + _selectionFunctionExpression =
|
| + _selectionExpression as FunctionExpression;
|
| + _selectionExpression = null;
|
| + }
|
| + // OK
|
| + return new RefactoringStatus();
|
| }
|
| - // OK
|
| - return new RefactoringStatus();
|
| }
|
| }
|
| // statements selected
|
| @@ -506,6 +519,66 @@ class ExtractMethodRefactoringImpl extends RefactoringImpl
|
| }
|
|
|
| /**
|
| + * If the [selectionRange] is associated with a [FunctionExpression], return
|
| + * this [FunctionExpression].
|
| + */
|
| + FunctionExpression _findFunctionExpression() {
|
| + if (selectionRange.length != 0) {
|
| + return null;
|
| + }
|
| + int offset = selectionRange.offset;
|
| + AstNode node = new NodeLocator2(offset, offset).searchWithin(unit);
|
| +
|
| + // Check for the parameter list of a FunctionExpression.
|
| + {
|
| + FunctionExpression function =
|
| + node?.getAncestor((n) => n is FunctionExpression);
|
| + if (function != null &&
|
| + function.parameters != null &&
|
| + range.node(function.parameters).contains(offset)) {
|
| + return function;
|
| + }
|
| + }
|
| +
|
| + // Check for the name of the named argument with the closure expression.
|
| + if (node is SimpleIdentifier &&
|
| + node.parent is Label &&
|
| + node.parent.parent is NamedExpression) {
|
| + NamedExpression namedExpression = node.parent.parent;
|
| + Expression expression = namedExpression.expression;
|
| + if (expression is FunctionExpression) {
|
| + return expression;
|
| + }
|
| + }
|
| +
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * If the selected closure (i.e. [_selectionFunctionExpression]) is an
|
| + * argument for a function typed parameter (as it should be), and the
|
| + * function type has the return type specified, return this return type's
|
| + * code. Otherwise return the empty string.
|
| + */
|
| + String _getExpectedClosureReturnTypeCode() {
|
| + Expression argument = _selectionFunctionExpression;
|
| + if (argument.parent is NamedExpression) {
|
| + argument = argument.parent as NamedExpression;
|
| + }
|
| + ParameterElement parameter = argument.bestParameterElement;
|
| + if (parameter != null) {
|
| + DartType parameterType = parameter.type;
|
| + if (parameterType is FunctionType) {
|
| + String typeCode = _getTypeCode(parameterType.returnType);
|
| + if (typeCode != 'dynamic') {
|
| + return typeCode + ' ';
|
| + }
|
| + }
|
| + }
|
| + return '';
|
| + }
|
| +
|
| + /**
|
| * Returns the selected [Expression] source, with applying new parameter
|
| * names.
|
| */
|
|
|