| 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 library services.src.correction.util; | 5 library services.src.correction.util; |
| 6 | 6 |
| 7 import 'dart:math'; | 7 import 'dart:math'; |
| 8 | 8 |
| 9 import 'package:analysis_server/plugin/protocol/protocol.dart' | 9 import 'package:analysis_server/plugin/protocol/protocol.dart' |
| 10 show SourceChange, SourceEdit; | 10 show SourceChange, SourceEdit; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 Object element = lists[0][position]; | 71 Object element = lists[0][position]; |
| 72 for (List list in lists) { | 72 for (List list in lists) { |
| 73 if (list[position] != element) { | 73 if (list[position] != element) { |
| 74 return false; | 74 return false; |
| 75 } | 75 } |
| 76 } | 76 } |
| 77 return true; | 77 return true; |
| 78 } | 78 } |
| 79 | 79 |
| 80 /** | 80 /** |
| 81 * Climbs up [PrefixedIdentifier] and [ProperyAccess] nodes that include [node]. | 81 * Climbs up [PrefixedIdentifier] and [PropertyAccess] nodes that include [node]
. |
| 82 */ | 82 */ |
| 83 Expression climbPropertyAccess(AstNode node) { | 83 Expression climbPropertyAccess(AstNode node) { |
| 84 while (true) { | 84 while (true) { |
| 85 AstNode parent = node.parent; | 85 AstNode parent = node.parent; |
| 86 if (parent is PrefixedIdentifier && parent.identifier == node) { | 86 if (parent is PrefixedIdentifier && parent.identifier == node) { |
| 87 node = parent; | 87 node = parent; |
| 88 continue; | 88 continue; |
| 89 } | 89 } |
| 90 if (parent is PropertyAccess && parent.propertyName == node) { | 90 if (parent is PropertyAccess && parent.propertyName == node) { |
| 91 node = parent; | 91 node = parent; |
| (...skipping 1136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1228 * @return <code>true</code> if "selection" covers "node" and there are any no
n-whitespace tokens | 1228 * @return <code>true</code> if "selection" covers "node" and there are any no
n-whitespace tokens |
| 1229 * between "selection" and "node" start/end. | 1229 * between "selection" and "node" start/end. |
| 1230 */ | 1230 */ |
| 1231 bool selectionIncludesNonWhitespaceOutsideNode( | 1231 bool selectionIncludesNonWhitespaceOutsideNode( |
| 1232 SourceRange selection, AstNode node) { | 1232 SourceRange selection, AstNode node) { |
| 1233 return _selectionIncludesNonWhitespaceOutsideRange( | 1233 return _selectionIncludesNonWhitespaceOutsideRange( |
| 1234 selection, rangeNode(node)); | 1234 selection, rangeNode(node)); |
| 1235 } | 1235 } |
| 1236 | 1236 |
| 1237 /** | 1237 /** |
| 1238 * @return <code>true</code> if given range of [BinaryExpression] can be extra
cted. | |
| 1239 */ | |
| 1240 bool validateBinaryExpressionRange( | |
| 1241 BinaryExpression binaryExpression, SourceRange range) { | |
| 1242 // only parts of associative expression are safe to extract | |
| 1243 if (!binaryExpression.operator.type.isAssociativeOperator) { | |
| 1244 return false; | |
| 1245 } | |
| 1246 // prepare selected operands | |
| 1247 List<Expression> operands = _getOperandsInOrderFor(binaryExpression); | |
| 1248 List<Expression> subOperands = _getOperandsForSourceRange(operands, range); | |
| 1249 // if empty, then something wrong with selection | |
| 1250 if (subOperands.isEmpty) { | |
| 1251 return false; | |
| 1252 } | |
| 1253 // may be some punctuation included into selection - operators, braces, etc | |
| 1254 if (_selectionIncludesNonWhitespaceOutsideOperands(range, subOperands)) { | |
| 1255 return false; | |
| 1256 } | |
| 1257 // OK | |
| 1258 return true; | |
| 1259 } | |
| 1260 | |
| 1261 /** | |
| 1262 * @return the [ImportElement] used to import given [Element] into [library]. | 1238 * @return the [ImportElement] used to import given [Element] into [library]. |
| 1263 * May be `null` if was not imported, i.e. declared in the same librar
y. | 1239 * May be `null` if was not imported, i.e. declared in the same librar
y. |
| 1264 */ | 1240 */ |
| 1265 ImportElement _getImportElement(Element element) { | 1241 ImportElement _getImportElement(Element element) { |
| 1266 for (ImportElement imp in _library.imports) { | 1242 for (ImportElement imp in _library.imports) { |
| 1267 Map<String, Element> definedNames = getImportNamespace(imp); | 1243 Map<String, Element> definedNames = getImportNamespace(imp); |
| 1268 if (definedNames.containsValue(element)) { | 1244 if (definedNames.containsValue(element)) { |
| 1269 return imp; | 1245 return imp; |
| 1270 } | 1246 } |
| 1271 } | 1247 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1337 Expression operand = prefixExpression.operand; | 1313 Expression operand = prefixExpression.operand; |
| 1338 while (operand is ParenthesizedExpression) { | 1314 while (operand is ParenthesizedExpression) { |
| 1339 ParenthesizedExpression pe = operand as ParenthesizedExpression; | 1315 ParenthesizedExpression pe = operand as ParenthesizedExpression; |
| 1340 operand = pe.expression; | 1316 operand = pe.expression; |
| 1341 } | 1317 } |
| 1342 return _InvertedCondition._simple(getNodeText(operand)); | 1318 return _InvertedCondition._simple(getNodeText(operand)); |
| 1343 } | 1319 } |
| 1344 } | 1320 } |
| 1345 if (expression is ParenthesizedExpression) { | 1321 if (expression is ParenthesizedExpression) { |
| 1346 ParenthesizedExpression pe = expression; | 1322 ParenthesizedExpression pe = expression; |
| 1347 Expression innerExpresion = pe.expression; | 1323 Expression innerExpression = pe.expression; |
| 1348 while (innerExpresion is ParenthesizedExpression) { | 1324 while (innerExpression is ParenthesizedExpression) { |
| 1349 innerExpresion = (innerExpresion as ParenthesizedExpression).expression; | 1325 innerExpression = |
| 1326 (innerExpression as ParenthesizedExpression).expression; |
| 1350 } | 1327 } |
| 1351 return _invertCondition0(innerExpresion); | 1328 return _invertCondition0(innerExpression); |
| 1352 } | 1329 } |
| 1353 DartType type = expression.bestType; | 1330 DartType type = expression.bestType; |
| 1354 if (type.displayName == "bool") { | 1331 if (type.displayName == "bool") { |
| 1355 return _InvertedCondition._simple("!${getNodeText(expression)}"); | 1332 return _InvertedCondition._simple("!${getNodeText(expression)}"); |
| 1356 } | 1333 } |
| 1357 return _InvertedCondition._simple(getNodeText(expression)); | 1334 return _InvertedCondition._simple(getNodeText(expression)); |
| 1358 } | 1335 } |
| 1359 | 1336 |
| 1360 /** | 1337 /** |
| 1361 * Checks if [type] is visible at [targetOffset]. | 1338 * Checks if [type] is visible at [targetOffset]. |
| 1362 */ | 1339 */ |
| 1363 bool _isTypeVisible(DartType type) { | 1340 bool _isTypeVisible(DartType type) { |
| 1364 if (type is TypeParameterType) { | 1341 if (type is TypeParameterType) { |
| 1365 TypeParameterElement parameterElement = type.element; | 1342 TypeParameterElement parameterElement = type.element; |
| 1366 Element parameterClassElement = parameterElement.enclosingElement; | 1343 Element parameterClassElement = parameterElement.enclosingElement; |
| 1367 return identical(parameterClassElement, targetClassElement); | 1344 return identical(parameterClassElement, targetClassElement); |
| 1368 } | 1345 } |
| 1369 return true; | 1346 return true; |
| 1370 } | 1347 } |
| 1371 | 1348 |
| 1372 bool _selectionIncludesNonWhitespaceOutsideOperands( | |
| 1373 SourceRange selection, List<Expression> operands) { | |
| 1374 return _selectionIncludesNonWhitespaceOutsideRange( | |
| 1375 selection, rangeNodes(operands)); | |
| 1376 } | |
| 1377 | |
| 1378 /** | 1349 /** |
| 1379 * @return <code>true</code> if "selection" covers "range" and there are any n
on-whitespace tokens | 1350 * @return <code>true</code> if "selection" covers "range" and there are any n
on-whitespace tokens |
| 1380 * between "selection" and "range" start/end. | 1351 * between "selection" and "range" start/end. |
| 1381 */ | 1352 */ |
| 1382 bool _selectionIncludesNonWhitespaceOutsideRange( | 1353 bool _selectionIncludesNonWhitespaceOutsideRange( |
| 1383 SourceRange selection, SourceRange range) { | 1354 SourceRange selection, SourceRange range) { |
| 1384 // selection should cover range | 1355 // selection should cover range |
| 1385 if (!selection.covers(range)) { | 1356 if (!selection.covers(range)) { |
| 1386 return false; | 1357 return false; |
| 1387 } | 1358 } |
| 1388 // non-whitespace between selection start and range start | 1359 // non-whitespace between selection start and range start |
| 1389 if (!isJustWhitespaceOrComment(rangeStartStart(selection, range))) { | 1360 if (!isJustWhitespaceOrComment(rangeStartStart(selection, range))) { |
| 1390 return true; | 1361 return true; |
| 1391 } | 1362 } |
| 1392 // non-whitespace after range | 1363 // non-whitespace after range |
| 1393 if (!isJustWhitespaceOrComment(rangeEndEnd(range, selection))) { | 1364 if (!isJustWhitespaceOrComment(rangeEndEnd(range, selection))) { |
| 1394 return true; | 1365 return true; |
| 1395 } | 1366 } |
| 1396 // only whitespace in selection around range | 1367 // only whitespace in selection around range |
| 1397 return false; | 1368 return false; |
| 1398 } | 1369 } |
| 1399 | |
| 1400 /** | |
| 1401 * @return [Expression]s from <code>operands</code> which are completely cover
ed by given | |
| 1402 * [SourceRange]. Range should start and end between given [Expression
]s. | |
| 1403 */ | |
| 1404 static List<Expression> _getOperandsForSourceRange( | |
| 1405 List<Expression> operands, SourceRange range) { | |
| 1406 assert(!operands.isEmpty); | |
| 1407 List<Expression> subOperands = []; | |
| 1408 // track range enter/exit | |
| 1409 bool entered = false; | |
| 1410 bool exited = false; | |
| 1411 // may be range starts before or on first operand | |
| 1412 if (range.offset <= operands[0].offset) { | |
| 1413 entered = true; | |
| 1414 } | |
| 1415 // iterate over gaps between operands | |
| 1416 for (int i = 0; i < operands.length - 1; i++) { | |
| 1417 Expression operand = operands[i]; | |
| 1418 Expression nextOperand = operands[i + 1]; | |
| 1419 SourceRange inclusiveGap = | |
| 1420 rangeEndStart(operand, nextOperand).getMoveEnd(1); | |
| 1421 // add operand, if already entered range | |
| 1422 if (entered) { | |
| 1423 subOperands.add(operand); | |
| 1424 // may be last operand in range | |
| 1425 if (range.endsIn(inclusiveGap)) { | |
| 1426 exited = true; | |
| 1427 } | |
| 1428 } else { | |
| 1429 // may be first operand in range | |
| 1430 if (range.startsIn(inclusiveGap)) { | |
| 1431 entered = true; | |
| 1432 } | |
| 1433 } | |
| 1434 } | |
| 1435 // check if last operand is in range | |
| 1436 Expression lastGroupMember = operands[operands.length - 1]; | |
| 1437 if (range.end == lastGroupMember.end) { | |
| 1438 subOperands.add(lastGroupMember); | |
| 1439 exited = true; | |
| 1440 } | |
| 1441 // we expect that range covers only given operands | |
| 1442 if (!exited) { | |
| 1443 return []; | |
| 1444 } | |
| 1445 // done | |
| 1446 return subOperands; | |
| 1447 } | |
| 1448 | |
| 1449 /** | |
| 1450 * @return all operands of the given [BinaryExpression] and its children with
the same | |
| 1451 * operator. | |
| 1452 */ | |
| 1453 static List<Expression> _getOperandsInOrderFor(BinaryExpression groupRoot) { | |
| 1454 List<Expression> operands = []; | |
| 1455 TokenType groupOperatorType = groupRoot.operator.type; | |
| 1456 groupRoot.accept(new _OrderedOperandsVisitor(groupOperatorType, operands)); | |
| 1457 return operands; | |
| 1458 } | |
| 1459 } | 1370 } |
| 1460 | 1371 |
| 1461 /** | 1372 /** |
| 1462 * Describes where to insert new directive or top-level declaration. | 1373 * Describes where to insert new directive or top-level declaration. |
| 1463 */ | 1374 */ |
| 1464 class CorrectionUtils_InsertDesc { | 1375 class CorrectionUtils_InsertDesc { |
| 1465 int offset = 0; | 1376 int offset = 0; |
| 1466 String prefix = ""; | 1377 String prefix = ""; |
| 1467 String suffix = ""; | 1378 String suffix = ""; |
| 1468 } | 1379 } |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1540 static _InvertedCondition _binary(int precedence, _InvertedCondition left, | 1451 static _InvertedCondition _binary(int precedence, _InvertedCondition left, |
| 1541 String operation, _InvertedCondition right) { | 1452 String operation, _InvertedCondition right) { |
| 1542 String src = _parenthesizeIfRequired(left, precedence) + | 1453 String src = _parenthesizeIfRequired(left, precedence) + |
| 1543 operation + | 1454 operation + |
| 1544 _parenthesizeIfRequired(right, precedence); | 1455 _parenthesizeIfRequired(right, precedence); |
| 1545 return new _InvertedCondition(precedence, src); | 1456 return new _InvertedCondition(precedence, src); |
| 1546 } | 1457 } |
| 1547 | 1458 |
| 1548 static _InvertedCondition _binary2( | 1459 static _InvertedCondition _binary2( |
| 1549 _InvertedCondition left, String operation, _InvertedCondition right) { | 1460 _InvertedCondition left, String operation, _InvertedCondition right) { |
| 1550 // TODO(scheglov) conside merging with "_binary()" after testing | 1461 // TODO(scheglov) consider merging with "_binary()" after testing |
| 1551 return new _InvertedCondition( | 1462 return new _InvertedCondition( |
| 1552 1 << 20, "${left._source}${operation}${right._source}"); | 1463 1 << 20, "${left._source}${operation}${right._source}"); |
| 1553 } | 1464 } |
| 1554 | 1465 |
| 1555 /** | 1466 /** |
| 1556 * Adds enclosing parenthesis if the precedence of the [_InvertedCondition] if
less than the | 1467 * Adds enclosing parenthesis if the precedence of the [_InvertedCondition] if
less than the |
| 1557 * precedence of the expression we are going it to use in. | 1468 * precedence of the expression we are going it to use in. |
| 1558 */ | 1469 */ |
| 1559 static String _parenthesizeIfRequired( | 1470 static String _parenthesizeIfRequired( |
| 1560 _InvertedCondition expr, int newOperatorPrecedence) { | 1471 _InvertedCondition expr, int newOperatorPrecedence) { |
| 1561 if (expr._precedence < newOperatorPrecedence) { | 1472 if (expr._precedence < newOperatorPrecedence) { |
| 1562 return "(${expr._source})"; | 1473 return "(${expr._source})"; |
| 1563 } | 1474 } |
| 1564 return expr._source; | 1475 return expr._source; |
| 1565 } | 1476 } |
| 1566 | 1477 |
| 1567 static _InvertedCondition _simple(String source) => | 1478 static _InvertedCondition _simple(String source) => |
| 1568 new _InvertedCondition(2147483647, source); | 1479 new _InvertedCondition(2147483647, source); |
| 1569 } | 1480 } |
| 1570 | |
| 1571 class _OrderedOperandsVisitor extends GeneralizingAstVisitor { | |
| 1572 final TokenType groupOperatorType; | |
| 1573 final List<Expression> operands; | |
| 1574 | |
| 1575 _OrderedOperandsVisitor(this.groupOperatorType, this.operands); | |
| 1576 | |
| 1577 @override | |
| 1578 Object visitExpression(Expression node) { | |
| 1579 if (node is BinaryExpression && node.operator.type == groupOperatorType) { | |
| 1580 return super.visitNode(node); | |
| 1581 } | |
| 1582 operands.add(node); | |
| 1583 return null; | |
| 1584 } | |
| 1585 } | |
| OLD | NEW |