| 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.refactoring.extract_local; | 5 library services.src.refactoring.extract_local; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol.dart' hide Element; | 9 import 'package:analysis_server/src/protocol.dart' hide Element; |
| 10 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; | 10 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; |
| 11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart'
; | 11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart'
; |
| 12 import 'package:analysis_server/src/services/correction/source_range.dart'; | 12 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| 13 import 'package:analysis_server/src/services/correction/status.dart'; | 13 import 'package:analysis_server/src/services/correction/status.dart'; |
| 14 import 'package:analysis_server/src/services/correction/strings.dart'; | 14 import 'package:analysis_server/src/services/correction/strings.dart'; |
| 15 import 'package:analysis_server/src/services/correction/util.dart'; | 15 import 'package:analysis_server/src/services/correction/util.dart'; |
| 16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; | 16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart
'; |
| 17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; | 17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| 18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; | 18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; |
| 19 import 'package:analysis_server/src/services/search/element_visitors.dart'; | 19 import 'package:analysis_server/src/services/search/element_visitors.dart'; |
| 20 import 'package:analyzer/src/generated/ast.dart'; | 20 import 'package:analyzer/src/generated/ast.dart'; |
| 21 import 'package:analyzer/src/generated/element.dart'; | 21 import 'package:analyzer/src/generated/element.dart'; |
| 22 import 'package:analyzer/src/generated/java_core.dart'; | 22 import 'package:analyzer/src/generated/java_core.dart'; |
| 23 import 'package:analyzer/src/generated/scanner.dart'; | 23 import 'package:analyzer/src/generated/scanner.dart'; |
| 24 import 'package:analyzer/src/generated/source.dart'; | 24 import 'package:analyzer/src/generated/source.dart'; |
| 25 import 'dart:collection'; |
| 25 | 26 |
| 26 | 27 |
| 27 const String _TOKEN_SEPARATOR = "\uFFFF"; | 28 const String _TOKEN_SEPARATOR = "\uFFFF"; |
| 28 | 29 |
| 29 | 30 |
| 30 /** | 31 /** |
| 31 * [ExtractLocalRefactoring] implementation. | 32 * [ExtractLocalRefactoring] implementation. |
| 32 */ | 33 */ |
| 33 class ExtractLocalRefactoringImpl extends RefactoringImpl implements | 34 class ExtractLocalRefactoringImpl extends RefactoringImpl implements |
| 34 ExtractLocalRefactoring { | 35 ExtractLocalRefactoring { |
| 35 final CompilationUnit unit; | 36 final CompilationUnit unit; |
| 36 final int selectionOffset; | 37 final int selectionOffset; |
| 37 final int selectionLength; | 38 final int selectionLength; |
| 38 CompilationUnitElement unitElement; | 39 CompilationUnitElement unitElement; |
| 39 String file; | 40 String file; |
| 40 SourceRange selectionRange; | 41 SourceRange selectionRange; |
| 41 CorrectionUtils utils; | 42 CorrectionUtils utils; |
| 42 | 43 |
| 43 String name; | 44 String name; |
| 44 bool extractAll = true; | 45 bool extractAll = true; |
| 45 final List<String> names = <String>[]; | 46 final List<String> names = <String>[]; |
| 46 final List<int> offsets = <int>[]; | 47 final List<int> offsets = <int>[]; |
| 47 final List<int> lengths = <int>[]; | 48 final List<int> lengths = <int>[]; |
| 48 | 49 |
| 49 Expression rootExpression; | 50 Expression rootExpression; |
| 50 Expression singleExpression; | 51 Expression singleExpression; |
| 51 bool wholeStatementExpression = false; | 52 bool wholeStatementExpression = false; |
| 52 String stringLiteralPart; | 53 String stringLiteralPart; |
| 53 final List<SourceRange> occurrences = <SourceRange>[]; | 54 final List<SourceRange> occurrences = <SourceRange>[]; |
| 55 final Map<Element, int> elementIds = <Element, int>{}; |
| 54 final Set<String> excludedVariableNames = new Set<String>(); | 56 final Set<String> excludedVariableNames = new Set<String>(); |
| 55 | 57 |
| 56 ExtractLocalRefactoringImpl(this.unit, this.selectionOffset, | 58 ExtractLocalRefactoringImpl(this.unit, this.selectionOffset, |
| 57 this.selectionLength) { | 59 this.selectionLength) { |
| 58 unitElement = unit.element; | 60 unitElement = unit.element; |
| 59 selectionRange = new SourceRange(selectionOffset, selectionLength); | 61 selectionRange = new SourceRange(selectionOffset, selectionLength); |
| 60 utils = new CorrectionUtils(unit); | 62 utils = new CorrectionUtils(unit); |
| 61 } | 63 } |
| 62 | 64 |
| 63 @override | 65 @override |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 excludedVariableNames)); | 357 excludedVariableNames)); |
| 356 } | 358 } |
| 357 } | 359 } |
| 358 | 360 |
| 359 /** | 361 /** |
| 360 * Prepares all occurrences of the source which matches given selection, | 362 * Prepares all occurrences of the source which matches given selection, |
| 361 * sorted by offsets. | 363 * sorted by offsets. |
| 362 */ | 364 */ |
| 363 void _prepareOccurrences() { | 365 void _prepareOccurrences() { |
| 364 occurrences.clear(); | 366 occurrences.clear(); |
| 367 elementIds.clear(); |
| 365 // prepare selection | 368 // prepare selection |
| 366 String selectionSource; | 369 String selectionSource; |
| 367 { | 370 { |
| 368 String rawSelectionSource = utils.getRangeText(selectionRange); | 371 String rawSelectionSource = utils.getRangeText(selectionRange); |
| 369 List<Token> selectionTokens = TokenUtils.getTokens(rawSelectionSource); | 372 List<Token> selectionTokens = TokenUtils.getTokens(rawSelectionSource); |
| 370 selectionSource = selectionTokens.join(_TOKEN_SEPARATOR); | 373 selectionSource = |
| 374 _encodeExpressionTokens(rootExpression, selectionTokens); |
| 371 } | 375 } |
| 372 // prepare enclosing function | 376 // prepare enclosing function |
| 373 AstNode enclosingFunction; | 377 AstNode enclosingFunction; |
| 374 { | 378 { |
| 375 AstNode selectionNode = | 379 AstNode selectionNode = |
| 376 new NodeLocator.con1(selectionOffset).searchWithin(unit); | 380 new NodeLocator.con1(selectionOffset).searchWithin(unit); |
| 377 enclosingFunction = getEnclosingExecutableNode(selectionNode); | 381 enclosingFunction = getEnclosingExecutableNode(selectionNode); |
| 378 } | 382 } |
| 379 // visit function | 383 // visit function |
| 380 enclosingFunction.accept( | 384 enclosingFunction.accept( |
| 381 new _OccurrencesVisitor(this, occurrences, selectionSource)); | 385 new _OccurrencesVisitor(this, occurrences, selectionSource)); |
| 382 } | 386 } |
| 383 | 387 |
| 384 void _prepareOffsetsLengths() { | 388 void _prepareOffsetsLengths() { |
| 385 offsets.clear(); | 389 offsets.clear(); |
| 386 lengths.clear(); | 390 lengths.clear(); |
| 387 for (SourceRange occurrence in occurrences) { | 391 for (SourceRange occurrence in occurrences) { |
| 388 offsets.add(occurrence.offset); | 392 offsets.add(occurrence.offset); |
| 389 lengths.add(occurrence.length); | 393 lengths.add(occurrence.length); |
| 390 } | 394 } |
| 391 } | 395 } |
| 396 |
| 397 /** |
| 398 * Return an unique identifier for the given [Element], or `null` if [element] |
| 399 * is `null`. |
| 400 */ |
| 401 int _encodeElement(Element element) { |
| 402 if (element == null) { |
| 403 return null; |
| 404 } |
| 405 int id = elementIds[element]; |
| 406 if (id == null) { |
| 407 id = elementIds.length; |
| 408 elementIds[element] = id; |
| 409 } |
| 410 return id; |
| 411 } |
| 412 |
| 413 /** |
| 414 * Returns an [Element]-sensitive encoding of [tokens]. |
| 415 * Each [Token] with a [LocalVariableElement] has a suffix of the element id. |
| 416 * |
| 417 * So, we can distingush different local variables with the same name, if |
| 418 * there are multiple variables with the same name are declared in the |
| 419 * function we are searching occurrences in. |
| 420 */ |
| 421 String _encodeExpressionTokens(Expression expr, List<Token> tokens) { |
| 422 // no expression, i.e. a part of a string |
| 423 if (expr == null) { |
| 424 return tokens.join(_TOKEN_SEPARATOR); |
| 425 } |
| 426 // prepare Token -> LocalElement map |
| 427 Map<Token, Element> map = new HashMap<Token, Element>( |
| 428 equals: (Token a, Token b) => a.lexeme == b.lexeme, |
| 429 hashCode: (Token t) => t.lexeme.hashCode); |
| 430 expr.accept(new _TokenLocalElementVisitor(map)); |
| 431 // map and join tokens |
| 432 return tokens.map((Token token) { |
| 433 String tokenString = token.lexeme; |
| 434 // append token's Element id |
| 435 Element element = map[token]; |
| 436 if (element != null) { |
| 437 int elementId = _encodeElement(element); |
| 438 if (elementId != null) { |
| 439 tokenString += '-$elementId'; |
| 440 } |
| 441 } |
| 442 // done |
| 443 return tokenString; |
| 444 }).join(_TOKEN_SEPARATOR); |
| 445 } |
| 446 } |
| 447 |
| 448 |
| 449 class _TokenLocalElementVisitor extends RecursiveAstVisitor { |
| 450 final Map<Token, Element> map; |
| 451 |
| 452 _TokenLocalElementVisitor(this.map); |
| 453 |
| 454 visitSimpleIdentifier(SimpleIdentifier node) { |
| 455 Element element = node.staticElement; |
| 456 if (element is LocalVariableElement) { |
| 457 map[node.token] = element; |
| 458 } |
| 459 } |
| 392 } | 460 } |
| 393 | 461 |
| 394 | 462 |
| 395 /** | 463 /** |
| 396 * [SelectionAnalyzer] for [ExtractLocalRefactoringImpl]. | 464 * [SelectionAnalyzer] for [ExtractLocalRefactoringImpl]. |
| 397 */ | 465 */ |
| 398 class _ExtractExpressionAnalyzer extends SelectionAnalyzer { | 466 class _ExtractExpressionAnalyzer extends SelectionAnalyzer { |
| 399 final RefactoringStatus status = new RefactoringStatus(); | 467 final RefactoringStatus status = new RefactoringStatus(); |
| 400 | 468 |
| 401 _ExtractExpressionAnalyzer(SourceRange selection) : super(selection); | 469 _ExtractExpressionAnalyzer(SourceRange selection) : super(selection); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 471 class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> { | 539 class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> { |
| 472 final ExtractLocalRefactoringImpl ref; | 540 final ExtractLocalRefactoringImpl ref; |
| 473 final List<SourceRange> occurrences; | 541 final List<SourceRange> occurrences; |
| 474 final String selectionSource; | 542 final String selectionSource; |
| 475 | 543 |
| 476 _OccurrencesVisitor(this.ref, this.occurrences, this.selectionSource); | 544 _OccurrencesVisitor(this.ref, this.occurrences, this.selectionSource); |
| 477 | 545 |
| 478 @override | 546 @override |
| 479 Object visitBinaryExpression(BinaryExpression node) { | 547 Object visitBinaryExpression(BinaryExpression node) { |
| 480 if (!_hasStatements(node)) { | 548 if (!_hasStatements(node)) { |
| 481 _tryToFindOccurrenceFragment(node); | 549 _tryToFindOccurrenceFragments(node); |
| 482 return null; | 550 return null; |
| 483 } | 551 } |
| 484 return super.visitBinaryExpression(node); | 552 return super.visitBinaryExpression(node); |
| 485 } | 553 } |
| 486 | 554 |
| 487 @override | 555 @override |
| 488 Object visitExpression(Expression node) { | 556 Object visitExpression(Expression node) { |
| 489 if (ref._isExtractable(rangeNode(node))) { | 557 if (ref._isExtractable(rangeNode(node))) { |
| 490 _tryToFindOccurrence(node); | 558 _tryToFindOccurrence(node); |
| 491 } | 559 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 | 592 |
| 525 bool _hasStatements(AstNode root) { | 593 bool _hasStatements(AstNode root) { |
| 526 List<bool> result = [false]; | 594 List<bool> result = [false]; |
| 527 root.accept(new _HasStatementVisitor(result)); | 595 root.accept(new _HasStatementVisitor(result)); |
| 528 return result[0]; | 596 return result[0]; |
| 529 } | 597 } |
| 530 | 598 |
| 531 void _tryToFindOccurrence(Expression node) { | 599 void _tryToFindOccurrence(Expression node) { |
| 532 String nodeSource = ref.utils.getNodeText(node); | 600 String nodeSource = ref.utils.getNodeText(node); |
| 533 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); | 601 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); |
| 534 nodeSource = nodeTokens.join(_TOKEN_SEPARATOR); | 602 nodeSource = ref._encodeExpressionTokens(node, nodeTokens); |
| 535 if (nodeSource == selectionSource) { | 603 if (nodeSource == selectionSource) { |
| 536 SourceRange occuRange = rangeNode(node); | 604 SourceRange occuRange = rangeNode(node); |
| 537 _addOccurrence(occuRange); | 605 _addOccurrence(occuRange); |
| 538 } | 606 } |
| 539 } | 607 } |
| 540 | 608 |
| 541 void _tryToFindOccurrenceFragment(Expression node) { | 609 void _tryToFindOccurrenceFragments(Expression node) { |
| 542 int nodeOffset = node.offset; | 610 int nodeOffset = node.offset; |
| 543 String nodeSource = ref.utils.getNodeText(node); | 611 String nodeSource = ref.utils.getNodeText(node); |
| 544 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); | 612 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); |
| 545 nodeSource = nodeTokens.join(_TOKEN_SEPARATOR); | 613 nodeSource = ref._encodeExpressionTokens(node, nodeTokens); |
| 546 // find "selection" in "node" tokens | 614 // find "selection" in "node" tokens |
| 547 int lastIndex = 0; | 615 int lastIndex = 0; |
| 548 while (true) { | 616 while (true) { |
| 549 // find next occurrence | 617 // find next occurrence |
| 550 int index = nodeSource.indexOf(selectionSource, lastIndex); | 618 int index = nodeSource.indexOf(selectionSource, lastIndex); |
| 551 if (index == -1) { | 619 if (index == -1) { |
| 552 break; | 620 break; |
| 553 } | 621 } |
| 554 lastIndex = index + selectionSource.length; | 622 lastIndex = index + selectionSource.length; |
| 555 // find start/end tokens | 623 // find start/end tokens |
| 556 int startTokenIndex = | 624 int startTokenIndex = |
| 557 countMatches(nodeSource.substring(0, index), _TOKEN_SEPARATOR); | 625 countMatches(nodeSource.substring(0, index), _TOKEN_SEPARATOR); |
| 558 int endTokenIndex = | 626 int endTokenIndex = |
| 559 countMatches(nodeSource.substring(0, lastIndex), _TOKEN_SEPARATOR); | 627 countMatches(nodeSource.substring(0, lastIndex), _TOKEN_SEPARATOR); |
| 560 Token startToken = nodeTokens[startTokenIndex]; | 628 Token startToken = nodeTokens[startTokenIndex]; |
| 561 Token endToken = nodeTokens[endTokenIndex]; | 629 Token endToken = nodeTokens[endTokenIndex]; |
| 562 // add occurrence range | 630 // add occurrence range |
| 563 int occuStart = nodeOffset + startToken.offset; | 631 int occuStart = nodeOffset + startToken.offset; |
| 564 int occuEnd = nodeOffset + endToken.end; | 632 int occuEnd = nodeOffset + endToken.end; |
| 565 SourceRange occuRange = rangeStartEnd(occuStart, occuEnd); | 633 SourceRange occuRange = rangeStartEnd(occuStart, occuEnd); |
| 566 _addOccurrence(occuRange); | 634 _addOccurrence(occuRange); |
| 567 } | 635 } |
| 568 } | 636 } |
| 569 } | 637 } |
| OLD | NEW |