| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library services.src.refactoring.inline_local; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:analysis_server/src/protocol.dart' hide Element; |
| 10 import 'package:analysis_server/src/services/correction/status.dart'; |
| 11 import 'package:analysis_server/src/services/correction/util.dart'; |
| 12 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| 13 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; |
| 14 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 15 import 'package:analyzer/src/generated/ast.dart'; |
| 16 import 'package:analyzer/src/generated/element.dart'; |
| 17 import 'package:analyzer/src/generated/java_core.dart'; |
| 18 import 'package:analyzer/src/generated/scanner.dart'; |
| 19 import 'package:analyzer/src/generated/source.dart'; |
| 20 |
| 21 |
| 22 const String _TOKEN_SEPARATOR = "\uFFFF"; |
| 23 |
| 24 |
| 25 /** |
| 26 * [InlineLocalRefactoring] implementation. |
| 27 */ |
| 28 class InlineLocalRefactoringImpl extends RefactoringImpl implements |
| 29 InlineLocalRefactoring { |
| 30 final SearchEngine searchEngine; |
| 31 final CompilationUnit unit; |
| 32 final LocalVariableElement element; |
| 33 String file; |
| 34 CorrectionUtils utils; |
| 35 |
| 36 VariableDeclaration _variableNode; |
| 37 List<SearchMatch> _references; |
| 38 |
| 39 InlineLocalRefactoringImpl(this.searchEngine, this.unit, this.element) { |
| 40 file = unit.element.source.fullName; |
| 41 utils = new CorrectionUtils(unit); |
| 42 } |
| 43 |
| 44 @override |
| 45 String get refactoringName => 'Inline Local Variable'; |
| 46 |
| 47 @override |
| 48 int get referenceCount { |
| 49 return _references.length; |
| 50 } |
| 51 |
| 52 @override |
| 53 Future<RefactoringStatus> checkFinalConditions() { |
| 54 RefactoringStatus result = new RefactoringStatus(); |
| 55 return new Future.value(result); |
| 56 } |
| 57 |
| 58 @override |
| 59 Future<RefactoringStatus> checkInitialConditions() { |
| 60 RefactoringStatus result = new RefactoringStatus(); |
| 61 // prepare variable |
| 62 { |
| 63 AstNode elementNode = utils.findNode(element.nameOffset); |
| 64 _variableNode = elementNode != null ? |
| 65 elementNode.getAncestor((node) => node is VariableDeclaration) : |
| 66 null; |
| 67 } |
| 68 // should be normal variable declaration statement |
| 69 if (_variableNode.parent is! VariableDeclarationList || |
| 70 _variableNode.parent.parent is! VariableDeclarationStatement || |
| 71 _variableNode.parent.parent.parent is! Block) { |
| 72 result = new RefactoringStatus.fatal( |
| 73 'Local variable declared in ' |
| 74 'statement should be selected to activate this refactoring.'); |
| 75 return new Future.value(result); |
| 76 } |
| 77 // should have initializer at declaration |
| 78 if (_variableNode.initializer == null) { |
| 79 String message = format( |
| 80 "Local variable '{0}' is not initialized at declaration.", |
| 81 element.displayName); |
| 82 result = |
| 83 new RefactoringStatus.fatal(message, new Location.fromNode(_variableNo
de)); |
| 84 return new Future.value(result); |
| 85 } |
| 86 // prepare references |
| 87 return searchEngine.searchReferences(element).then((references) { |
| 88 this._references = references; |
| 89 // should not have assignments |
| 90 for (SearchMatch reference in _references) { |
| 91 if (reference.kind != MatchKind.READ) { |
| 92 String message = format( |
| 93 "Local variable '{0}' is assigned more than once.", |
| 94 [element.displayName]); |
| 95 return new RefactoringStatus.fatal( |
| 96 message, |
| 97 new Location.fromMatch(reference)); |
| 98 } |
| 99 } |
| 100 // done |
| 101 return result; |
| 102 }); |
| 103 } |
| 104 |
| 105 @override |
| 106 Future<SourceChange> createChange() { |
| 107 SourceChange change = new SourceChange(refactoringName); |
| 108 // remove declaration |
| 109 { |
| 110 Statement declarationStatement = |
| 111 _variableNode.getAncestor((node) => node is VariableDeclarationStateme
nt); |
| 112 SourceRange range = utils.getLinesRangeStatements([declarationStatement]); |
| 113 change.addEdit(file, new SourceEdit.range(range, '')); |
| 114 } |
| 115 // prepare initializer |
| 116 Expression initializer = _variableNode.initializer; |
| 117 String initializerSource = utils.getNodeText(initializer); |
| 118 int initializerPrecedence = getExpressionPrecedence(initializer); |
| 119 // replace references |
| 120 for (SearchMatch reference in _references) { |
| 121 SourceRange range = reference.sourceRange; |
| 122 String sourceForReference = |
| 123 _getSourceForReference(range, initializerSource, initializerPrecedence
); |
| 124 change.addEdit(file, new SourceEdit.range(range, sourceForReference)); |
| 125 } |
| 126 // done |
| 127 return new Future.value(change); |
| 128 } |
| 129 |
| 130 @override |
| 131 bool requiresPreview() => false; |
| 132 |
| 133 /** |
| 134 * Returns the source which should be used to replace the reference with the |
| 135 * given [SourceRange]. |
| 136 * |
| 137 * [range] - the [SourceRange] of the reference. |
| 138 * [source] - the source of the initializer, to be inserted at [range]. |
| 139 * [precedence] - the precedence of the initializer [source]. |
| 140 */ |
| 141 String _getSourceForReference(SourceRange range, String source, |
| 142 int precedence) { |
| 143 int offset = range.offset; |
| 144 AstNode node = utils.findNode(offset); |
| 145 AstNode parent = node.parent; |
| 146 if (_isIdentifierStringInterpolation(parent)) { |
| 147 return '{${source}}'; |
| 148 } |
| 149 if (precedence < getExpressionParentPrecedence(node)) { |
| 150 return '(${source})'; |
| 151 } |
| 152 return source; |
| 153 } |
| 154 |
| 155 /** |
| 156 * Checks if the given node is a string interpolation in form `$name`. |
| 157 */ |
| 158 bool _isIdentifierStringInterpolation(AstNode parent) { |
| 159 if (parent is InterpolationExpression) { |
| 160 InterpolationExpression element = parent; |
| 161 return element.beginToken.type == |
| 162 TokenType.STRING_INTERPOLATION_IDENTIFIER; |
| 163 } |
| 164 return false; |
| 165 } |
| 166 } |
| OLD | NEW |