| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // This code was auto-generated, is not intended to be edited, and is subject to | 5 // This code was auto-generated, is not intended to be edited, and is subject to |
| 6 // significant change. Please see the README file for more information. | 6 // significant change. Please see the README file for more information. |
| 7 | 7 |
| 8 library services.util; | 8 library services.util; |
| 9 | 9 |
| 10 import 'dart:collection'; | 10 import 'dart:collection'; |
| 11 import 'package:analyzer/src/generated/java_core.dart' hide StringUtils; | 11 import 'package:analyzer/src/generated/java_core.dart' hide StringUtils; |
| 12 import 'package:analyzer/src/generated/ast.dart'; | 12 import 'package:analyzer/src/generated/ast.dart'; |
| 13 import 'package:analyzer/src/generated/element.dart'; | 13 import 'package:analyzer/src/generated/element.dart'; |
| 14 import 'package:analyzer/src/generated/engine.dart'; | 14 import 'package:analyzer/src/generated/engine.dart'; |
| 15 import 'package:analyzer/src/generated/error.dart'; | 15 import 'package:analyzer/src/generated/error.dart'; |
| 16 import 'package:analyzer/src/generated/resolver.dart'; | 16 import 'package:analyzer/src/generated/resolver.dart'; |
| 17 import 'package:analyzer/src/generated/source.dart'; | 17 import 'package:analyzer/src/generated/source.dart'; |
| 18 import 'package:analyzer/src/generated/scanner.dart'; | 18 import 'package:analyzer/src/generated/scanner.dart'; |
| 19 import 'change.dart'; | |
| 20 import 'proposal.dart'; | |
| 21 import 'status.dart'; | |
| 22 import 'stubs.dart'; | 19 import 'stubs.dart'; |
| 23 | 20 |
| 24 /** | 21 /** |
| 25 * Context for which assistance should be provided. | 22 * Context for which assistance should be provided. |
| 26 */ | 23 */ |
| 27 class AssistContext { | 24 class AssistContext { |
| 28 final SearchEngine searchEngine; | 25 final SearchEngine searchEngine; |
| 29 | 26 |
| 30 final AnalysisContext analysisContext; | 27 final AnalysisContext analysisContext; |
| 31 | 28 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 class CorrectionUtils { | 113 class CorrectionUtils { |
| 117 /** | 114 /** |
| 118 * If `true` then [addEdit] validates that | 115 * If `true` then [addEdit] validates that |
| 119 * [Edit] replaces correct part of the [Source]. | 116 * [Edit] replaces correct part of the [Source]. |
| 120 */ | 117 */ |
| 121 static bool _DEBUG_VALIDATE_EDITS = true; | 118 static bool _DEBUG_VALIDATE_EDITS = true; |
| 122 | 119 |
| 123 static List<String> _KNOWN_METHOD_NAME_PREFIXES = ["get", "is", "to"]; | 120 static List<String> _KNOWN_METHOD_NAME_PREFIXES = ["get", "is", "to"]; |
| 124 | 121 |
| 125 /** | 122 /** |
| 126 * Validates that the [Edit] replaces the expected part of the [Source] and ad
ds this | |
| 127 * [Edit] to the [SourceChange]. | |
| 128 */ | |
| 129 static void addEdit(AnalysisContext context, SourceChange change, String descr
iption, String expected, Edit edit) { | |
| 130 if (_DEBUG_VALIDATE_EDITS) { | |
| 131 Source source = change.source; | |
| 132 String sourceContent = getSourceContent(context, source); | |
| 133 // prepare range | |
| 134 int beginIndex = edit.offset; | |
| 135 int endIndex = beginIndex + edit.length; | |
| 136 int sourceLength = sourceContent.length; | |
| 137 if (beginIndex >= sourceLength || endIndex >= sourceLength) { | |
| 138 throw new IllegalStateException("${source} has ${sourceLength} character
s but ${beginIndex} to ${endIndex} requested.\n\nTry to use Tools | Reanalyze So
urces."); | |
| 139 } | |
| 140 // check that range has expected content | |
| 141 String rangeContent = sourceContent.substring(beginIndex, endIndex); | |
| 142 if (rangeContent != expected) { | |
| 143 throw new IllegalStateException("${source} expected |${expected}| at ${b
eginIndex} to ${endIndex} but |${rangeContent}| found.\n\nTry to use Tools | Rea
nalyze Sources."); | |
| 144 } | |
| 145 } | |
| 146 // do add the Edit | |
| 147 change.addEdit(edit, description); | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * @return <code>true</code> if given [List]s are equals at given position. | 123 * @return <code>true</code> if given [List]s are equals at given position. |
| 152 */ | 124 */ |
| 153 static bool allListsEqual(List<List> lists, int position) { | 125 static bool allListsEqual(List<List> lists, int position) { |
| 154 Object element = lists[0][position]; | 126 Object element = lists[0][position]; |
| 155 for (List list in lists) { | 127 for (List list in lists) { |
| 156 if (!identical(list[position], element)) { | 128 if (!identical(list[position], element)) { |
| 157 return false; | 129 return false; |
| 158 } | 130 } |
| 159 } | 131 } |
| 160 return true; | 132 return true; |
| 161 } | 133 } |
| 162 | 134 |
| 163 /** | 135 /** |
| 164 * @return the updated [String] with applied [Edit]s. | |
| 165 */ | |
| 166 static String applyReplaceEdits(String s, List<Edit> edits) { | |
| 167 // sort edits | |
| 168 edits = []; | |
| 169 edits.sort((Edit o1, Edit o2) => o1.offset - o2.offset); | |
| 170 // apply edits | |
| 171 int delta = 0; | |
| 172 for (Edit edit in edits) { | |
| 173 int editOffset = edit.offset + delta; | |
| 174 String beforeEdit = s.substring(0, editOffset); | |
| 175 String afterEdit = s.substring(editOffset + edit.length); | |
| 176 s = "${beforeEdit}${edit.replacement}${afterEdit}"; | |
| 177 delta += getDeltaOffset(edit); | |
| 178 } | |
| 179 // done | |
| 180 return s; | |
| 181 } | |
| 182 | |
| 183 /** | |
| 184 * @return <code>true</code> if given [SourceRange] covers given [AstNode]. | 136 * @return <code>true</code> if given [SourceRange] covers given [AstNode]. |
| 185 */ | 137 */ |
| 186 static bool covers(SourceRange r, AstNode node) { | 138 static bool covers(SourceRange r, AstNode node) { |
| 187 SourceRange nodeRange = SourceRangeFactory.rangeNode(node); | 139 SourceRange nodeRange = SourceRangeFactory.rangeNode(node); |
| 188 return r.covers(nodeRange); | 140 return r.covers(nodeRange); |
| 189 } | 141 } |
| 190 | 142 |
| 191 /** | 143 /** |
| 192 * @return all direct children of the given [Element]. | 144 * @return all direct children of the given [Element]. |
| 193 */ | 145 */ |
| (...skipping 24 matching lines...) Expand all Loading... |
| 218 } | 170 } |
| 219 if (typeName == "String") { | 171 if (typeName == "String") { |
| 220 return "''"; | 172 return "''"; |
| 221 } | 173 } |
| 222 } | 174 } |
| 223 // no better guess | 175 // no better guess |
| 224 return "null"; | 176 return "null"; |
| 225 } | 177 } |
| 226 | 178 |
| 227 /** | 179 /** |
| 228 * @return the number of characters this [Edit] will move offsets after its ra
nge. | |
| 229 */ | |
| 230 static int getDeltaOffset(Edit edit) => edit.replacement.length - edit.length; | |
| 231 | |
| 232 /** | |
| 233 * @return the name of the [Element] kind. | 180 * @return the name of the [Element] kind. |
| 234 */ | 181 */ |
| 235 static String getElementKindName(Element element) { | 182 static String getElementKindName(Element element) { |
| 236 ElementKind kind = element.kind; | 183 ElementKind kind = element.kind; |
| 237 return getElementKindName2(kind); | 184 return getElementKindName2(kind); |
| 238 } | 185 } |
| 239 | 186 |
| 240 /** | 187 /** |
| 241 * @return the display name of the [ElementKind]. | 188 * @return the display name of the [ElementKind]. |
| 242 */ | 189 */ |
| (...skipping 689 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 932 static List<String> _getVariableNameSuggestions(String name) { | 879 static List<String> _getVariableNameSuggestions(String name) { |
| 933 List<String> result = []; | 880 List<String> result = []; |
| 934 List<String> parts = name.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z]
)"); | 881 List<String> parts = name.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z]
)"); |
| 935 for (int i = 0; i < parts.length; i++) { | 882 for (int i = 0; i < parts.length; i++) { |
| 936 String suggestion = "${parts[i].toLowerCase()}${StringUtils.join(parts, ""
, i + 1, parts.length)}"; | 883 String suggestion = "${parts[i].toLowerCase()}${StringUtils.join(parts, ""
, i + 1, parts.length)}"; |
| 937 result.add(suggestion); | 884 result.add(suggestion); |
| 938 } | 885 } |
| 939 return result; | 886 return result; |
| 940 } | 887 } |
| 941 | 888 |
| 942 /** | |
| 943 * Adds enclosing parenthesis if the precedence of the [InvertedCondition] if
less than the | |
| 944 * precedence of the expression we are going it to use in. | |
| 945 */ | |
| 946 static String _parenthesizeIfRequired(CorrectionUtils_InvertedCondition expr,
int newOperatorPrecedence) { | |
| 947 if (expr._precedence < newOperatorPrecedence) { | |
| 948 return "(${expr._source})"; | |
| 949 } | |
| 950 return expr._source; | |
| 951 } | |
| 952 | |
| 953 final CompilationUnit unit; | 889 final CompilationUnit unit; |
| 954 | 890 |
| 955 LibraryElement _library; | 891 LibraryElement _library; |
| 956 | 892 |
| 957 String _buffer; | 893 String _buffer; |
| 958 | 894 |
| 959 String _endOfLine; | 895 String _endOfLine; |
| 960 | 896 |
| 961 CorrectionUtils(this.unit) { | 897 CorrectionUtils(this.unit) { |
| 962 CompilationUnitElement element = unit.element; | 898 CompilationUnitElement element = unit.element; |
| 963 this._library = element.library; | 899 this._library = element.library; |
| 964 this._buffer = getSourceContent(element.context, element.source); | 900 this._buffer = getSourceContent(element.context, element.source); |
| 965 } | 901 } |
| 966 | 902 |
| 967 /** | 903 /** |
| 968 * @return the source of the given [SourceRange] with indentation changed from
"oldIndent" | |
| 969 * to "newIndent", keeping indentation of the lines relative to each o
ther. | |
| 970 */ | |
| 971 Edit createIndentEdit(SourceRange range, String oldIndent, String newIndent) { | |
| 972 String newSource = getIndentSource(range, oldIndent, newIndent); | |
| 973 return new Edit(range.offset, range.length, newSource); | |
| 974 } | |
| 975 | |
| 976 /** | |
| 977 * @return the [AstNode] that encloses the given offset. | 904 * @return the [AstNode] that encloses the given offset. |
| 978 */ | 905 */ |
| 979 AstNode findNode(int offset) => new NodeLocator.con1(offset).searchWithin(unit
); | 906 AstNode findNode(int offset) => new NodeLocator.con1(offset).searchWithin(unit
); |
| 980 | 907 |
| 981 /** | 908 /** |
| 982 * TODO(scheglov) replace with nodes once there will be [CompilationUnit#getCo
mments]. | 909 * TODO(scheglov) replace with nodes once there will be [CompilationUnit#getCo
mments]. |
| 983 * | 910 * |
| 984 * @return the [SourceRange]s of all comments in [CompilationUnit]. | 911 * @return the [SourceRange]s of all comments in [CompilationUnit]. |
| 985 */ | 912 */ |
| 986 List<SourceRange> get commentRanges { | 913 List<SourceRange> get commentRanges { |
| (...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1499 sb.append(getTypeSource2(argument)); | 1426 sb.append(getTypeSource2(argument)); |
| 1500 } | 1427 } |
| 1501 sb.append(">"); | 1428 sb.append(">"); |
| 1502 } | 1429 } |
| 1503 } | 1430 } |
| 1504 // done | 1431 // done |
| 1505 return sb.toString(); | 1432 return sb.toString(); |
| 1506 } | 1433 } |
| 1507 | 1434 |
| 1508 /** | 1435 /** |
| 1509 * @return the source of the inverted condition for the given logical expressi
on. | |
| 1510 */ | |
| 1511 String invertCondition(Expression expression) => _invertCondition0(expression)
._source; | |
| 1512 | |
| 1513 /** | |
| 1514 * @return <code>true</code> if selection range contains only whitespace. | 1436 * @return <code>true</code> if selection range contains only whitespace. |
| 1515 */ | 1437 */ |
| 1516 bool isJustWhitespace(SourceRange range) => getText3(range).trim().length == 0
; | 1438 bool isJustWhitespace(SourceRange range) => getText3(range).trim().length == 0
; |
| 1517 | 1439 |
| 1518 /** | 1440 /** |
| 1519 * @return <code>true</code> if selection range contains only whitespace or co
mments | 1441 * @return <code>true</code> if selection range contains only whitespace or co
mments |
| 1520 */ | 1442 */ |
| 1521 bool isJustWhitespaceOrComment(SourceRange range) { | 1443 bool isJustWhitespaceOrComment(SourceRange range) { |
| 1522 String trimmedText = getText3(range).trim(); | 1444 String trimmedText = getText3(range).trim(); |
| 1523 // may be whitespace | 1445 // may be whitespace |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1564 ImportElement _getImportElement(Element element) { | 1486 ImportElement _getImportElement(Element element) { |
| 1565 for (ImportElement imp in _library.imports) { | 1487 for (ImportElement imp in _library.imports) { |
| 1566 Map<String, Element> definedNames = getImportNamespace(imp); | 1488 Map<String, Element> definedNames = getImportNamespace(imp); |
| 1567 if (definedNames.containsValue(element)) { | 1489 if (definedNames.containsValue(element)) { |
| 1568 return imp; | 1490 return imp; |
| 1569 } | 1491 } |
| 1570 } | 1492 } |
| 1571 return null; | 1493 return null; |
| 1572 } | 1494 } |
| 1573 | 1495 |
| 1574 /** | |
| 1575 * @return the [InvertedCondition] for the given logical expression. | |
| 1576 */ | |
| 1577 CorrectionUtils_InvertedCondition _invertCondition0(Expression expression) { | |
| 1578 if (expression is BooleanLiteral) { | |
| 1579 BooleanLiteral literal = expression; | |
| 1580 if (literal.value) { | |
| 1581 return CorrectionUtils_InvertedCondition._simple("false"); | |
| 1582 } else { | |
| 1583 return CorrectionUtils_InvertedCondition._simple("true"); | |
| 1584 } | |
| 1585 } | |
| 1586 if (expression is BinaryExpression) { | |
| 1587 BinaryExpression binary = expression; | |
| 1588 TokenType operator = binary.operator.type; | |
| 1589 Expression le = binary.leftOperand; | |
| 1590 Expression re = binary.rightOperand; | |
| 1591 CorrectionUtils_InvertedCondition ls = _invertCondition0(le); | |
| 1592 CorrectionUtils_InvertedCondition rs = _invertCondition0(re); | |
| 1593 if (operator == TokenType.LT) { | |
| 1594 return CorrectionUtils_InvertedCondition._binary2(ls, " >= ", rs); | |
| 1595 } | |
| 1596 if (operator == TokenType.GT) { | |
| 1597 return CorrectionUtils_InvertedCondition._binary2(ls, " <= ", rs); | |
| 1598 } | |
| 1599 if (operator == TokenType.LT_EQ) { | |
| 1600 return CorrectionUtils_InvertedCondition._binary2(ls, " > ", rs); | |
| 1601 } | |
| 1602 if (operator == TokenType.GT_EQ) { | |
| 1603 return CorrectionUtils_InvertedCondition._binary2(ls, " < ", rs); | |
| 1604 } | |
| 1605 if (operator == TokenType.EQ_EQ) { | |
| 1606 return CorrectionUtils_InvertedCondition._binary2(ls, " != ", rs); | |
| 1607 } | |
| 1608 if (operator == TokenType.BANG_EQ) { | |
| 1609 return CorrectionUtils_InvertedCondition._binary2(ls, " == ", rs); | |
| 1610 } | |
| 1611 if (operator == TokenType.AMPERSAND_AMPERSAND) { | |
| 1612 int newPrecedence = TokenType.BAR_BAR.precedence; | |
| 1613 return CorrectionUtils_InvertedCondition._binary(newPrecedence, ls, " ||
", rs); | |
| 1614 } | |
| 1615 if (operator == TokenType.BAR_BAR) { | |
| 1616 int newPrecedence = TokenType.AMPERSAND_AMPERSAND.precedence; | |
| 1617 return CorrectionUtils_InvertedCondition._binary(newPrecedence, ls, " &&
", rs); | |
| 1618 } | |
| 1619 } | |
| 1620 if (expression is IsExpression) { | |
| 1621 IsExpression isExpression = expression; | |
| 1622 String expressionSource = getText(isExpression.expression); | |
| 1623 String typeSource = getText(isExpression.type); | |
| 1624 if (isExpression.notOperator == null) { | |
| 1625 return CorrectionUtils_InvertedCondition._simple("${expressionSource} is
! ${typeSource}"); | |
| 1626 } else { | |
| 1627 return CorrectionUtils_InvertedCondition._simple("${expressionSource} is
${typeSource}"); | |
| 1628 } | |
| 1629 } | |
| 1630 if (expression is PrefixExpression) { | |
| 1631 PrefixExpression prefixExpression = expression; | |
| 1632 TokenType operator = prefixExpression.operator.type; | |
| 1633 if (operator == TokenType.BANG) { | |
| 1634 Expression operand = prefixExpression.operand; | |
| 1635 while (operand is ParenthesizedExpression) { | |
| 1636 ParenthesizedExpression pe = operand as ParenthesizedExpression; | |
| 1637 operand = pe.expression; | |
| 1638 } | |
| 1639 return CorrectionUtils_InvertedCondition._simple(getText(operand)); | |
| 1640 } | |
| 1641 } | |
| 1642 if (expression is ParenthesizedExpression) { | |
| 1643 ParenthesizedExpression pe = expression; | |
| 1644 Expression innerExpresion = pe.expression; | |
| 1645 while (innerExpresion is ParenthesizedExpression) { | |
| 1646 innerExpresion = (innerExpresion as ParenthesizedExpression).expression; | |
| 1647 } | |
| 1648 return _invertCondition0(innerExpresion); | |
| 1649 } | |
| 1650 DartType type = expression.bestType; | |
| 1651 if (type.displayName == "bool") { | |
| 1652 return CorrectionUtils_InvertedCondition._simple("!${getText(expression)}"
); | |
| 1653 } | |
| 1654 return CorrectionUtils_InvertedCondition._simple(getText(expression)); | |
| 1655 } | |
| 1656 | |
| 1657 bool _selectionIncludesNonWhitespaceOutsideOperands(SourceRange selection, Lis
t<Expression> operands) => _selectionIncludesNonWhitespaceOutsideRange(selection
, SourceRangeFactory.rangeNodes(operands)); | 1496 bool _selectionIncludesNonWhitespaceOutsideOperands(SourceRange selection, Lis
t<Expression> operands) => _selectionIncludesNonWhitespaceOutsideRange(selection
, SourceRangeFactory.rangeNodes(operands)); |
| 1658 | 1497 |
| 1659 /** | 1498 /** |
| 1660 * @return <code>true</code> if "selection" covers "range" and there are any n
on-whitespace tokens | 1499 * @return <code>true</code> if "selection" covers "range" and there are any n
on-whitespace tokens |
| 1661 * between "selection" and "range" start/end. | 1500 * between "selection" and "range" start/end. |
| 1662 */ | 1501 */ |
| 1663 bool _selectionIncludesNonWhitespaceOutsideRange(SourceRange selection, Source
Range range) { | 1502 bool _selectionIncludesNonWhitespaceOutsideRange(SourceRange selection, Source
Range range) { |
| 1664 // selection should cover range | 1503 // selection should cover range |
| 1665 if (!selection.covers(range)) { | 1504 if (!selection.covers(range)) { |
| 1666 return false; | 1505 return false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1682 * Describes where to insert new directive or top-level declaration. | 1521 * Describes where to insert new directive or top-level declaration. |
| 1683 */ | 1522 */ |
| 1684 class CorrectionUtils_InsertDesc { | 1523 class CorrectionUtils_InsertDesc { |
| 1685 int offset = 0; | 1524 int offset = 0; |
| 1686 | 1525 |
| 1687 String prefix = ""; | 1526 String prefix = ""; |
| 1688 | 1527 |
| 1689 String suffix = ""; | 1528 String suffix = ""; |
| 1690 } | 1529 } |
| 1691 | 1530 |
| 1692 /** | |
| 1693 * This class is used to hold the source and also its precedence during invertin
g logical | |
| 1694 * expressions. | |
| 1695 */ | |
| 1696 class CorrectionUtils_InvertedCondition { | |
| 1697 static CorrectionUtils_InvertedCondition _binary(int precedence, CorrectionUti
ls_InvertedCondition left, String operation, CorrectionUtils_InvertedCondition r
ight) => new CorrectionUtils_InvertedCondition(precedence, "${CorrectionUtils._p
arenthesizeIfRequired(left, precedence)}${operation}${CorrectionUtils._parenthes
izeIfRequired(right, precedence)}"); | |
| 1698 | |
| 1699 static CorrectionUtils_InvertedCondition _binary2(CorrectionUtils_InvertedCond
ition left, String operation, CorrectionUtils_InvertedCondition right) => new Co
rrectionUtils_InvertedCondition(2147483647, "${left._source}${operation}${right.
_source}"); | |
| 1700 | |
| 1701 static CorrectionUtils_InvertedCondition _simple(String source) => new Correct
ionUtils_InvertedCondition(2147483647, source); | |
| 1702 | |
| 1703 final int _precedence; | |
| 1704 | |
| 1705 final String _source; | |
| 1706 | |
| 1707 CorrectionUtils_InvertedCondition(this._precedence, this._source); | |
| 1708 } | |
| 1709 | |
| 1710 class GeneralizingAstVisitor_CorrectionUtils_getOperandsInOrderFor extends Gener
alizingAstVisitor<Object> { | 1531 class GeneralizingAstVisitor_CorrectionUtils_getOperandsInOrderFor extends Gener
alizingAstVisitor<Object> { |
| 1711 TokenType groupOperatorType; | 1532 TokenType groupOperatorType; |
| 1712 | 1533 |
| 1713 List<Expression> operands; | 1534 List<Expression> operands; |
| 1714 | 1535 |
| 1715 GeneralizingAstVisitor_CorrectionUtils_getOperandsInOrderFor(this.groupOperato
rType, this.operands) : super(); | 1536 GeneralizingAstVisitor_CorrectionUtils_getOperandsInOrderFor(this.groupOperato
rType, this.operands) : super(); |
| 1716 | 1537 |
| 1717 @override | 1538 @override |
| 1718 Object visitExpression(Expression node) { | 1539 Object visitExpression(Expression node) { |
| 1719 if (node is BinaryExpression && node.operator.type == groupOperatorType) { | 1540 if (node is BinaryExpression && node.operator.type == groupOperatorType) { |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1874 } | 1695 } |
| 1875 | 1696 |
| 1876 void _match(Element element, AstNode node) { | 1697 void _match(Element element, AstNode node) { |
| 1877 if (identical(_target, element) || identical(_target2, element) || identical
(_target3, element) || identical(_target4, element)) { | 1698 if (identical(_target, element) || identical(_target2, element) || identical
(_target3, element) || identical(_target4, element)) { |
| 1878 _matches.add(node); | 1699 _matches.add(node); |
| 1879 } | 1700 } |
| 1880 } | 1701 } |
| 1881 } | 1702 } |
| 1882 | 1703 |
| 1883 /** | 1704 /** |
| 1884 * Abstract visitor for visiting [AstNode]s covered by the selection [SourceRang
e]. | |
| 1885 */ | |
| 1886 class SelectionAnalyzer extends GeneralizingAstVisitor<Object> { | |
| 1887 SourceRange selection; | |
| 1888 | |
| 1889 AstNode _coveringNode; | |
| 1890 | |
| 1891 List<AstNode> _selectedNodes; | |
| 1892 | |
| 1893 SelectionAnalyzer(SourceRange selection) { | |
| 1894 assert(selection != null); | |
| 1895 this.selection = selection; | |
| 1896 } | |
| 1897 | |
| 1898 /** | |
| 1899 * @return the [AstNode] with the shortest length which completely covers the
specified | |
| 1900 * selection. | |
| 1901 */ | |
| 1902 AstNode get coveringNode => _coveringNode; | |
| 1903 | |
| 1904 /** | |
| 1905 * @return the first selected [AstNode], may be <code>null</code>. | |
| 1906 */ | |
| 1907 AstNode get firstSelectedNode { | |
| 1908 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
| 1909 return null; | |
| 1910 } | |
| 1911 return _selectedNodes[0]; | |
| 1912 } | |
| 1913 | |
| 1914 /** | |
| 1915 * @return the last selected [AstNode], may be <code>null</code>. | |
| 1916 */ | |
| 1917 AstNode get lastSelectedNode { | |
| 1918 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
| 1919 return null; | |
| 1920 } | |
| 1921 return _selectedNodes[_selectedNodes.length - 1]; | |
| 1922 } | |
| 1923 | |
| 1924 /** | |
| 1925 * @return the [SourceRange] which covers selected [AstNode]s, may be | |
| 1926 * <code>null</code> if no [AstNode]s under selection. | |
| 1927 */ | |
| 1928 SourceRange get selectedNodeRange { | |
| 1929 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
| 1930 return null; | |
| 1931 } | |
| 1932 AstNode firstNode = _selectedNodes[0]; | |
| 1933 AstNode lastNode = _selectedNodes[_selectedNodes.length - 1]; | |
| 1934 return SourceRangeFactory.rangeStartEnd(firstNode, lastNode); | |
| 1935 } | |
| 1936 | |
| 1937 /** | |
| 1938 * @return the [AstNode]s fully covered by the selection [SourceRange]. | |
| 1939 */ | |
| 1940 List<AstNode> get selectedNodes { | |
| 1941 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
| 1942 return []; | |
| 1943 } | |
| 1944 return _selectedNodes; | |
| 1945 } | |
| 1946 | |
| 1947 /** | |
| 1948 * @return <code>true</code> if there are [AstNode] fully covered by the selec
tion | |
| 1949 * [SourceRange]. | |
| 1950 */ | |
| 1951 bool get hasSelectedNodes => _selectedNodes != null && !_selectedNodes.isEmpty
; | |
| 1952 | |
| 1953 @override | |
| 1954 Object visitNode(AstNode node) { | |
| 1955 SourceRange nodeRange = SourceRangeFactory.rangeNode(node); | |
| 1956 if (selection.covers(nodeRange)) { | |
| 1957 if (isFirstNode) { | |
| 1958 handleFirstSelectedNode(node); | |
| 1959 } else { | |
| 1960 handleNextSelectedNode(node); | |
| 1961 } | |
| 1962 return null; | |
| 1963 } else if (selection.coveredBy(nodeRange)) { | |
| 1964 _coveringNode = node; | |
| 1965 node.visitChildren(this); | |
| 1966 return null; | |
| 1967 } else if (selection.startsIn(nodeRange)) { | |
| 1968 handleSelectionStartsIn(node); | |
| 1969 node.visitChildren(this); | |
| 1970 return null; | |
| 1971 } else if (selection.endsIn(nodeRange)) { | |
| 1972 handleSelectionEndsIn(node); | |
| 1973 node.visitChildren(this); | |
| 1974 return null; | |
| 1975 } | |
| 1976 // no intersection | |
| 1977 return null; | |
| 1978 } | |
| 1979 | |
| 1980 /** | |
| 1981 * Adds first selected [AstNode]. | |
| 1982 */ | |
| 1983 void handleFirstSelectedNode(AstNode node) { | |
| 1984 _selectedNodes = []; | |
| 1985 _selectedNodes.add(node); | |
| 1986 } | |
| 1987 | |
| 1988 /** | |
| 1989 * Adds second or more selected [AstNode]. | |
| 1990 */ | |
| 1991 void handleNextSelectedNode(AstNode node) { | |
| 1992 if (identical(firstSelectedNode.parent, node.parent)) { | |
| 1993 _selectedNodes.add(node); | |
| 1994 } | |
| 1995 } | |
| 1996 | |
| 1997 /** | |
| 1998 * Notifies that selection ends in given [AstNode]. | |
| 1999 */ | |
| 2000 void handleSelectionEndsIn(AstNode node) { | |
| 2001 } | |
| 2002 | |
| 2003 /** | |
| 2004 * Notifies that selection starts in given [AstNode]. | |
| 2005 */ | |
| 2006 void handleSelectionStartsIn(AstNode node) { | |
| 2007 } | |
| 2008 | |
| 2009 /** | |
| 2010 * Resets selected nodes. | |
| 2011 */ | |
| 2012 void reset() { | |
| 2013 _selectedNodes = null; | |
| 2014 } | |
| 2015 | |
| 2016 /** | |
| 2017 * @return <code>true</code> if there was no selected nodes yet. | |
| 2018 */ | |
| 2019 bool get isFirstNode => _selectedNodes == null; | |
| 2020 } | |
| 2021 | |
| 2022 /** | |
| 2023 * Helper for building Dart source with tracked positions. | |
| 2024 */ | |
| 2025 class SourceBuilder { | |
| 2026 final int offset; | |
| 2027 | |
| 2028 JavaStringBuilder _buffer = new JavaStringBuilder(); | |
| 2029 | |
| 2030 Map<String, List<SourceRange>> _linkedPositions = {}; | |
| 2031 | |
| 2032 final Map<String, List<LinkedPositionProposal>> linkedProposals = {}; | |
| 2033 | |
| 2034 String _currentPositionGroupId; | |
| 2035 | |
| 2036 int _currentPositionStart = 0; | |
| 2037 | |
| 2038 int _endPosition = -1; | |
| 2039 | |
| 2040 SourceBuilder.con1(this.offset); | |
| 2041 | |
| 2042 SourceBuilder.con2(SourceRange offsetRange) : this.con1(offsetRange.offset); | |
| 2043 | |
| 2044 /** | |
| 2045 * Adds proposal for the current position, may be called after [startPosition]
. | |
| 2046 */ | |
| 2047 void addProposal(CorrectionImage icon, String text) { | |
| 2048 List<LinkedPositionProposal> proposals = linkedProposals[_currentPositionGro
upId]; | |
| 2049 if (proposals == null) { | |
| 2050 proposals = []; | |
| 2051 linkedProposals[_currentPositionGroupId] = proposals; | |
| 2052 } | |
| 2053 proposals.add(new LinkedPositionProposal(icon, text)); | |
| 2054 } | |
| 2055 | |
| 2056 /** | |
| 2057 * Appends source to the buffer. | |
| 2058 */ | |
| 2059 SourceBuilder append(String s) { | |
| 2060 _buffer.append(s); | |
| 2061 return this; | |
| 2062 } | |
| 2063 | |
| 2064 /** | |
| 2065 * Ends position started using [startPosition]. | |
| 2066 */ | |
| 2067 void endPosition() { | |
| 2068 assert(_currentPositionGroupId != null); | |
| 2069 _addPosition(); | |
| 2070 _currentPositionGroupId = null; | |
| 2071 } | |
| 2072 | |
| 2073 /** | |
| 2074 * @return the "end position" for the [CorrectionProposal], may be <code>-1</c
ode> if not | |
| 2075 * set in this [SourceBuilder]. | |
| 2076 */ | |
| 2077 int get endPosition2 { | |
| 2078 if (_endPosition == -1) { | |
| 2079 return -1; | |
| 2080 } | |
| 2081 return offset + _endPosition; | |
| 2082 } | |
| 2083 | |
| 2084 /** | |
| 2085 * @return the [Map] or position IDs to their locations. | |
| 2086 */ | |
| 2087 Map<String, List<SourceRange>> get linkedPositions => _linkedPositions; | |
| 2088 | |
| 2089 /** | |
| 2090 * @return the length of the built source. | |
| 2091 */ | |
| 2092 int length() => _buffer.length; | |
| 2093 | |
| 2094 /** | |
| 2095 * Marks current position as "end position" of the [CorrectionProposal]. | |
| 2096 */ | |
| 2097 void setEndPosition() { | |
| 2098 _endPosition = _buffer.length; | |
| 2099 } | |
| 2100 | |
| 2101 /** | |
| 2102 * Sets text-only proposals for the current position. | |
| 2103 */ | |
| 2104 void set proposals(List<String> proposals) { | |
| 2105 List<LinkedPositionProposal> proposalList = []; | |
| 2106 for (String proposalText in proposals) { | |
| 2107 proposalList.add(new LinkedPositionProposal(null, proposalText)); | |
| 2108 } | |
| 2109 linkedProposals[_currentPositionGroupId] = proposalList; | |
| 2110 } | |
| 2111 | |
| 2112 /** | |
| 2113 * Starts linked position with given ID. | |
| 2114 */ | |
| 2115 void startPosition(String groupId) { | |
| 2116 assert(_currentPositionGroupId == null); | |
| 2117 _currentPositionGroupId = groupId; | |
| 2118 _currentPositionStart = _buffer.length; | |
| 2119 } | |
| 2120 | |
| 2121 @override | |
| 2122 String toString() => _buffer.toString(); | |
| 2123 | |
| 2124 /** | |
| 2125 * Adds position location [SourceRange] using current fields. | |
| 2126 */ | |
| 2127 void _addPosition() { | |
| 2128 List<SourceRange> locations = _linkedPositions[_currentPositionGroupId]; | |
| 2129 if (locations == null) { | |
| 2130 locations = []; | |
| 2131 _linkedPositions[_currentPositionGroupId] = locations; | |
| 2132 } | |
| 2133 int start = offset + _currentPositionStart; | |
| 2134 int end = offset + _buffer.length; | |
| 2135 locations.add(SourceRangeFactory.rangeStartEnd(start, end)); | |
| 2136 } | |
| 2137 } | |
| 2138 | |
| 2139 /** | |
| 2140 * Analyzer to check if a selection covers a valid set of statements of AST. | |
| 2141 */ | |
| 2142 class StatementAnalyzer extends SelectionAnalyzer { | |
| 2143 /** | |
| 2144 * @return <code>true</code> if "nodes" contains "node". | |
| 2145 */ | |
| 2146 static bool _contains(List<AstNode> nodes, AstNode node) => nodes.contains(nod
e); | |
| 2147 | |
| 2148 /** | |
| 2149 * @return <code>true</code> if "nodes" contains one of the "otherNodes". | |
| 2150 */ | |
| 2151 static bool _contains2(List<AstNode> nodes, List<AstNode> otherNodes) { | |
| 2152 for (AstNode otherNode in otherNodes) { | |
| 2153 if (nodes.contains(otherNode)) { | |
| 2154 return true; | |
| 2155 } | |
| 2156 } | |
| 2157 return false; | |
| 2158 } | |
| 2159 | |
| 2160 CorrectionUtils utils; | |
| 2161 | |
| 2162 RefactoringStatus _status = new RefactoringStatus(); | |
| 2163 | |
| 2164 StatementAnalyzer.con1(CompilationUnit cunit, SourceRange selection) : this.co
n2(new CorrectionUtils(cunit), selection); | |
| 2165 | |
| 2166 StatementAnalyzer.con2(CorrectionUtils utils, SourceRange selection) : super(s
election) { | |
| 2167 this.utils = utils; | |
| 2168 } | |
| 2169 | |
| 2170 /** | |
| 2171 * @return the [RefactoringStatus] result of checking selection. | |
| 2172 */ | |
| 2173 RefactoringStatus get status => _status; | |
| 2174 | |
| 2175 @override | |
| 2176 Object visitCompilationUnit(CompilationUnit node) { | |
| 2177 super.visitCompilationUnit(node); | |
| 2178 if (!hasSelectedNodes) { | |
| 2179 return null; | |
| 2180 } | |
| 2181 // check that selection does not begin/end in comment | |
| 2182 { | |
| 2183 int selectionStart = selection.offset; | |
| 2184 int selectionEnd = selection.end; | |
| 2185 List<SourceRange> commentRanges = utils.commentRanges; | |
| 2186 for (SourceRange commentRange in commentRanges) { | |
| 2187 if (commentRange.contains(selectionStart)) { | |
| 2188 invalidSelection("Selection begins inside a comment."); | |
| 2189 } | |
| 2190 if (commentRange.containsExclusive(selectionEnd)) { | |
| 2191 invalidSelection("Selection ends inside a comment."); | |
| 2192 } | |
| 2193 } | |
| 2194 } | |
| 2195 // more checks | |
| 2196 if (!_status.hasFatalError) { | |
| 2197 _checkSelectedNodes(node); | |
| 2198 } | |
| 2199 return null; | |
| 2200 } | |
| 2201 | |
| 2202 @override | |
| 2203 Object visitDoStatement(DoStatement node) { | |
| 2204 super.visitDoStatement(node); | |
| 2205 List<AstNode> selectedNodes = this.selectedNodes; | |
| 2206 if (_contains(selectedNodes, node.body)) { | |
| 2207 invalidSelection("Operation not applicable to a 'do' statement's body and
expression."); | |
| 2208 } | |
| 2209 return null; | |
| 2210 } | |
| 2211 | |
| 2212 @override | |
| 2213 Object visitForStatement(ForStatement node) { | |
| 2214 super.visitForStatement(node); | |
| 2215 List<AstNode> selectedNodes = this.selectedNodes; | |
| 2216 bool containsInit = _contains(selectedNodes, node.initialization) || _contai
ns(selectedNodes, node.variables); | |
| 2217 bool containsCondition = _contains(selectedNodes, node.condition); | |
| 2218 bool containsUpdaters = _contains2(selectedNodes, node.updaters); | |
| 2219 bool containsBody = _contains(selectedNodes, node.body); | |
| 2220 if (containsInit && containsCondition) { | |
| 2221 invalidSelection("Operation not applicable to a 'for' statement's initiali
zer and condition."); | |
| 2222 } else if (containsCondition && containsUpdaters) { | |
| 2223 invalidSelection("Operation not applicable to a 'for' statement's conditio
n and updaters."); | |
| 2224 } else if (containsUpdaters && containsBody) { | |
| 2225 invalidSelection("Operation not applicable to a 'for' statement's updaters
and body."); | |
| 2226 } | |
| 2227 return null; | |
| 2228 } | |
| 2229 | |
| 2230 @override | |
| 2231 Object visitSwitchStatement(SwitchStatement node) { | |
| 2232 super.visitSwitchStatement(node); | |
| 2233 List<AstNode> selectedNodes = this.selectedNodes; | |
| 2234 List<SwitchMember> switchMembers = node.members; | |
| 2235 for (AstNode selectedNode in selectedNodes) { | |
| 2236 if (switchMembers.contains(selectedNode)) { | |
| 2237 invalidSelection("Selection must either cover whole switch statement or
parts of a single case block."); | |
| 2238 break; | |
| 2239 } | |
| 2240 } | |
| 2241 return null; | |
| 2242 } | |
| 2243 | |
| 2244 @override | |
| 2245 Object visitTryStatement(TryStatement node) { | |
| 2246 super.visitTryStatement(node); | |
| 2247 AstNode firstSelectedNode = this.firstSelectedNode; | |
| 2248 if (firstSelectedNode != null) { | |
| 2249 if (identical(firstSelectedNode, node.body) || identical(firstSelectedNode
, node.finallyBlock)) { | |
| 2250 invalidSelection("Selection must either cover whole try statement or par
ts of try, catch, or finally block."); | |
| 2251 } else { | |
| 2252 List<CatchClause> catchClauses = node.catchClauses; | |
| 2253 for (CatchClause catchClause in catchClauses) { | |
| 2254 if (identical(firstSelectedNode, catchClause) || identical(firstSelect
edNode, catchClause.body) || identical(firstSelectedNode, catchClause.exceptionP
arameter)) { | |
| 2255 invalidSelection("Selection must either cover whole try statement or
parts of try, catch, or finally block."); | |
| 2256 } | |
| 2257 } | |
| 2258 } | |
| 2259 } | |
| 2260 return null; | |
| 2261 } | |
| 2262 | |
| 2263 @override | |
| 2264 Object visitWhileStatement(WhileStatement node) { | |
| 2265 super.visitWhileStatement(node); | |
| 2266 List<AstNode> selectedNodes = this.selectedNodes; | |
| 2267 if (_contains(selectedNodes, node.condition) && _contains(selectedNodes, nod
e.body)) { | |
| 2268 invalidSelection("Operation not applicable to a while statement's expressi
on and body."); | |
| 2269 } | |
| 2270 return null; | |
| 2271 } | |
| 2272 | |
| 2273 /** | |
| 2274 * Records fatal error with given message. | |
| 2275 */ | |
| 2276 void invalidSelection(String message) { | |
| 2277 invalidSelection2(message, null); | |
| 2278 } | |
| 2279 | |
| 2280 /** | |
| 2281 * Records fatal error with given message and [RefactoringStatusContext]. | |
| 2282 */ | |
| 2283 void invalidSelection2(String message, RefactoringStatusContext context) { | |
| 2284 _status.addFatalError(message, context); | |
| 2285 reset(); | |
| 2286 } | |
| 2287 | |
| 2288 /** | |
| 2289 * Checks final selected [AstNode]s after processing [CompilationUnit]. | |
| 2290 */ | |
| 2291 void _checkSelectedNodes(CompilationUnit unit) { | |
| 2292 List<AstNode> nodes = selectedNodes; | |
| 2293 // some tokens before first selected node | |
| 2294 { | |
| 2295 AstNode firstNode = nodes[0]; | |
| 2296 SourceRange rangeBeforeFirstNode = SourceRangeFactory.rangeStartStart(sele
ction, firstNode); | |
| 2297 if (_hasTokens(rangeBeforeFirstNode)) { | |
| 2298 invalidSelection2("The beginning of the selection contains characters th
at do not belong to a statement.", new RefactoringStatusContext.forUnit(unit, ra
ngeBeforeFirstNode)); | |
| 2299 } | |
| 2300 } | |
| 2301 // some tokens after last selected node | |
| 2302 { | |
| 2303 AstNode lastNode = nodes.last; | |
| 2304 SourceRange rangeAfterLastNode = SourceRangeFactory.rangeEndEnd(lastNode,
selection); | |
| 2305 if (_hasTokens(rangeAfterLastNode)) { | |
| 2306 invalidSelection2("The end of the selection contains characters that do
not belong to a statement.", new RefactoringStatusContext.forUnit(unit, rangeAft
erLastNode)); | |
| 2307 } | |
| 2308 } | |
| 2309 } | |
| 2310 | |
| 2311 /** | |
| 2312 * @return the [Token]s in given [SourceRange]. | |
| 2313 */ | |
| 2314 List<Token> _getTokens(SourceRange range) { | |
| 2315 try { | |
| 2316 String text = utils.getText3(range); | |
| 2317 return TokenUtils.getTokens(text); | |
| 2318 } catch (e) { | |
| 2319 return []; | |
| 2320 } | |
| 2321 } | |
| 2322 | |
| 2323 /** | |
| 2324 * @return <code>true</code> if there are [Token]s in the given [SourceRange]. | |
| 2325 */ | |
| 2326 bool _hasTokens(SourceRange range) => !_getTokens(range).isEmpty; | |
| 2327 } | |
| 2328 | |
| 2329 /** | |
| 2330 * Utilities to work with [Token]s. | 1705 * Utilities to work with [Token]s. |
| 2331 */ | 1706 */ |
| 2332 class TokenUtils { | 1707 class TokenUtils { |
| 2333 /** | 1708 /** |
| 2334 * @return the first [KeywordToken] with given [Keyword], may be <code>null</c
ode> if | 1709 * @return the first [KeywordToken] with given [Keyword], may be <code>null</c
ode> if |
| 2335 * not found. | 1710 * not found. |
| 2336 */ | 1711 */ |
| 2337 static KeywordToken findKeywordToken(List<Token> tokens, Keyword keyword) { | 1712 static KeywordToken findKeywordToken(List<Token> tokens, Keyword keyword) { |
| 2338 for (Token token in tokens) { | 1713 for (Token token in tokens) { |
| 2339 if (token is KeywordToken) { | 1714 if (token is KeywordToken) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2377 return []; | 1752 return []; |
| 2378 } | 1753 } |
| 2379 } | 1754 } |
| 2380 | 1755 |
| 2381 /** | 1756 /** |
| 2382 * @return <code>true</code> if given [Token]s contain only single [Token] wit
h given | 1757 * @return <code>true</code> if given [Token]s contain only single [Token] wit
h given |
| 2383 * [TokenType]. | 1758 * [TokenType]. |
| 2384 */ | 1759 */ |
| 2385 static bool hasOnly(List<Token> tokens, TokenType type) => tokens.length == 1
&& tokens[0].type == type; | 1760 static bool hasOnly(List<Token> tokens, TokenType type) => tokens.length == 1
&& tokens[0].type == type; |
| 2386 } | 1761 } |
| 2387 | |
| 2388 class URIUtils { | |
| 2389 /** | |
| 2390 * Computes relative relative path to reference "target" from "base". Uses "..
" if needed, in | |
| 2391 * contrast to [URI#relativize]. | |
| 2392 */ | |
| 2393 static String computeRelativePath(String base, String target) { | |
| 2394 // convert to URI separator | |
| 2395 base = base.replaceAll("\\\\", "/"); | |
| 2396 target = target.replaceAll("\\\\", "/"); | |
| 2397 if (base.startsWith("/") && target.startsWith("/")) { | |
| 2398 base = base.substring(1); | |
| 2399 target = target.substring(1); | |
| 2400 } | |
| 2401 // equal paths - no relative | |
| 2402 if (base == target) { | |
| 2403 return null; | |
| 2404 } | |
| 2405 // split paths | |
| 2406 List<String> baseParts = base.split("/"); | |
| 2407 List<String> targetParts = target.split("/"); | |
| 2408 // prepare maximum possible common root length | |
| 2409 int length = baseParts.length < targetParts.length ? baseParts.length : targ
etParts.length; | |
| 2410 // find common root | |
| 2411 int lastCommonRoot = -1; | |
| 2412 for (int i = 0; i < length; i++) { | |
| 2413 if (baseParts[i] == targetParts[i]) { | |
| 2414 lastCommonRoot = i; | |
| 2415 } else { | |
| 2416 break; | |
| 2417 } | |
| 2418 } | |
| 2419 // append .. | |
| 2420 JavaStringBuilder relativePath = new JavaStringBuilder(); | |
| 2421 for (int i = lastCommonRoot + 1; i < baseParts.length; i++) { | |
| 2422 if (baseParts[i].length > 0) { | |
| 2423 relativePath.append("../"); | |
| 2424 } | |
| 2425 } | |
| 2426 // append target folder names | |
| 2427 for (int i = lastCommonRoot + 1; i < targetParts.length - 1; i++) { | |
| 2428 String p = targetParts[i]; | |
| 2429 relativePath.append(p); | |
| 2430 relativePath.append("/"); | |
| 2431 } | |
| 2432 // append target file name | |
| 2433 relativePath.append(targetParts[targetParts.length - 1]); | |
| 2434 // done | |
| 2435 return relativePath.toString(); | |
| 2436 } | |
| 2437 } | |
| OLD | NEW |