| Index: pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
|
| diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
|
| index 6cfd951cdab52349cfbd65a31fd2007428b92682..c865aec72eec276336d43c3be9b09ae729e72a28 100644
|
| --- a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
|
| +++ b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
|
| @@ -40,6 +40,8 @@ class ExtractLocalRefactoringImpl extends RefactoringImpl
|
|
|
| String name;
|
| bool extractAll = true;
|
| + final List<int> coveringExpressionOffsets = <int>[];
|
| + final List<int> coveringExpressionLengths = <int>[];
|
| final List<String> names = <String>[];
|
| final List<int> offsets = <int>[];
|
| final List<int> lengths = <int>[];
|
| @@ -178,19 +180,58 @@ class ExtractLocalRefactoringImpl extends RefactoringImpl
|
|
|
| /**
|
| * Checks if [selectionRange] selects [Expression] which can be extracted, and
|
| - * location of this [DartExpression] in AST allows extracting.
|
| + * location of this [Expression] in AST allows extracting.
|
| */
|
| RefactoringStatus _checkSelection() {
|
| - _ExtractExpressionAnalyzer _selectionAnalyzer =
|
| - new _ExtractExpressionAnalyzer(selectionRange);
|
| - unit.accept(_selectionAnalyzer);
|
| - AstNode coveringNode = _selectionAnalyzer.coveringNode;
|
| - // may be fatal error
|
| + String selectionStr;
|
| + // exclude whitespaces
|
| {
|
| - RefactoringStatus status = _selectionAnalyzer.status;
|
| - if (status.hasFatalError) {
|
| - return status;
|
| + selectionStr = utils.getRangeText(selectionRange);
|
| + int numLeading = countLeadingWhitespaces(selectionStr);
|
| + int numTrailing = countTrailingWhitespaces(selectionStr);
|
| + int offset = selectionRange.offset + numLeading;
|
| + int end = selectionRange.end - numTrailing;
|
| + selectionRange = new SourceRange(offset, end - offset);
|
| + }
|
| + // get covering node
|
| + AstNode coveringNode = new NodeLocator(
|
| + selectionRange.offset, selectionRange.end).searchWithin(unit);
|
| + // compute covering expressions
|
| + for (AstNode node = coveringNode; node is Expression; node = node.parent) {
|
| + AstNode parent = node.parent;
|
| + // cannot extract the name part of a property access
|
| + if (parent is PrefixedIdentifier && parent.identifier == node ||
|
| + parent is PropertyAccess && parent.propertyName == node) {
|
| + continue;
|
| + }
|
| + // fatal selection problems
|
| + if (coveringExpressionOffsets.isEmpty) {
|
| + if (node is SimpleIdentifier) {
|
| + Element element = node.bestElement;
|
| + if (element is FunctionElement || element is MethodElement) {
|
| + return new RefactoringStatus.fatal(
|
| + 'Cannot extract a single method name.',
|
| + newLocation_fromNode(node));
|
| + }
|
| + if (node.inDeclarationContext()) {
|
| + return new RefactoringStatus.fatal(
|
| + 'Cannot extract the name part of a declaration.',
|
| + newLocation_fromNode(node));
|
| + }
|
| + }
|
| + if (parent is AssignmentExpression && parent.leftHandSide == node) {
|
| + return new RefactoringStatus.fatal(
|
| + 'Cannot extract the left-hand side of an assignment.',
|
| + newLocation_fromNode(node));
|
| + }
|
| }
|
| + // set selected expression
|
| + if (coveringExpressionOffsets.isEmpty) {
|
| + rootExpression = node;
|
| + }
|
| + // add the expression range
|
| + coveringExpressionOffsets.add(node.offset);
|
| + coveringExpressionLengths.add(node.length);
|
| }
|
| // we need enclosing block to add variable declaration statement
|
| if (coveringNode == null ||
|
| @@ -201,38 +242,18 @@ class ExtractLocalRefactoringImpl extends RefactoringImpl
|
| }
|
| // part of string literal
|
| if (coveringNode is StringLiteral) {
|
| - stringLiteralPart = utils.getRangeText(selectionRange);
|
| - if (stringLiteralPart.startsWith("'") ||
|
| - stringLiteralPart.startsWith('"') ||
|
| - stringLiteralPart.endsWith("'") ||
|
| - stringLiteralPart.endsWith('"')) {
|
| - return new RefactoringStatus.fatal(
|
| - 'Cannot extract only leading or trailing quote of string literal.');
|
| - }
|
| - return new RefactoringStatus();
|
| - }
|
| - // single node selected
|
| - if (_selectionAnalyzer.selectedNodes.length == 1 &&
|
| - !utils.selectionIncludesNonWhitespaceOutsideNode(
|
| - selectionRange, _selectionAnalyzer.firstSelectedNode)) {
|
| - AstNode selectedNode = _selectionAnalyzer.firstSelectedNode;
|
| - if (selectedNode is Expression) {
|
| - rootExpression = selectedNode;
|
| - singleExpression = rootExpression;
|
| - wholeStatementExpression =
|
| - singleExpression.parent is ExpressionStatement;
|
| + if (selectionRange.offset > coveringNode.offset &&
|
| + selectionRange.end < coveringNode.end) {
|
| + stringLiteralPart = selectionStr;
|
| return new RefactoringStatus();
|
| }
|
| }
|
| - // fragment of binary expression selected
|
| - if (coveringNode is BinaryExpression) {
|
| - BinaryExpression binaryExpression = coveringNode;
|
| - if (utils.validateBinaryExpressionRange(
|
| - binaryExpression, selectionRange)) {
|
| - rootExpression = binaryExpression;
|
| - singleExpression = null;
|
| - return new RefactoringStatus();
|
| - }
|
| + // single node selected
|
| + if (rootExpression != null) {
|
| + singleExpression = rootExpression;
|
| + selectionRange = rangeNode(singleExpression);
|
| + wholeStatementExpression = singleExpression.parent is ExpressionStatement;
|
| + return new RefactoringStatus();
|
| }
|
| // invalid selection
|
| return new RefactoringStatus.fatal(
|
| @@ -259,7 +280,7 @@ class ExtractLocalRefactoringImpl extends RefactoringImpl
|
| * Returns an [Element]-sensitive encoding of [tokens].
|
| * Each [Token] with a [LocalVariableElement] has a suffix of the element id.
|
| *
|
| - * So, we can distingush different local variables with the same name, if
|
| + * So, we can distinguish different local variables with the same name, if
|
| * there are multiple variables with the same name are declared in the
|
| * function we are searching occurrences in.
|
| */
|
| @@ -475,7 +496,7 @@ class _ExtractExpressionAnalyzer extends SelectionAnalyzer {
|
| }
|
|
|
| /**
|
| - * Records fatal error with given message and [Locatiom].
|
| + * Records fatal error with given [message] and [location].
|
| */
|
| void _invalidSelection(String message, Location location) {
|
| status.addFatalError(message, location);
|
| @@ -523,7 +544,7 @@ class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> {
|
| @override
|
| Object visitStringLiteral(StringLiteral node) {
|
| if (ref.stringLiteralPart != null) {
|
| - int occuLength = ref.stringLiteralPart.length;
|
| + int length = ref.stringLiteralPart.length;
|
| String value = ref.utils.getNodeText(node);
|
| int lastIndex = 0;
|
| while (true) {
|
| @@ -531,10 +552,10 @@ class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> {
|
| if (index == -1) {
|
| break;
|
| }
|
| - lastIndex = index + occuLength;
|
| - int occuStart = node.offset + index;
|
| - SourceRange occuRange = rangeStartLength(occuStart, occuLength);
|
| - occurrences.add(occuRange);
|
| + lastIndex = index + length;
|
| + int start = node.offset + index;
|
| + SourceRange range = rangeStartLength(start, length);
|
| + occurrences.add(range);
|
| }
|
| return null;
|
| }
|
| @@ -560,8 +581,8 @@ class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> {
|
| List<Token> nodeTokens = TokenUtils.getTokens(nodeSource);
|
| nodeSource = ref._encodeExpressionTokens(node, nodeTokens);
|
| if (nodeSource == selectionSource) {
|
| - SourceRange occuRange = rangeNode(node);
|
| - _addOccurrence(occuRange);
|
| + SourceRange range = rangeNode(node);
|
| + _addOccurrence(range);
|
| }
|
| }
|
|
|
| @@ -587,10 +608,10 @@ class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> {
|
| Token startToken = nodeTokens[startTokenIndex];
|
| Token endToken = nodeTokens[endTokenIndex];
|
| // add occurrence range
|
| - int occuStart = nodeOffset + startToken.offset;
|
| - int occuEnd = nodeOffset + endToken.end;
|
| - SourceRange occuRange = rangeStartEnd(occuStart, occuEnd);
|
| - _addOccurrence(occuRange);
|
| + int start = nodeOffset + startToken.offset;
|
| + int end = nodeOffset + endToken.end;
|
| + SourceRange range = rangeStartEnd(start, end);
|
| + _addOccurrence(range);
|
| }
|
| }
|
| }
|
|
|