Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: pkg/analysis_server/lib/src/services/refactoring/extract_local.dart

Issue 619723002: Fix for 'Extract Local' refactoring - take into account local variable elements. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_local_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/services/refactoring/extract_local_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698