| 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 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analysis_server/src/protocol_server.dart' hide Element; | 10 import 'package:analysis_server/src/protocol_server.dart' hide Element; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 String stringLiteralPart; | 52 String stringLiteralPart; |
| 53 final List<SourceRange> occurrences = <SourceRange>[]; | 53 final List<SourceRange> occurrences = <SourceRange>[]; |
| 54 final Map<Element, int> elementIds = <Element, int>{}; | 54 final Map<Element, int> elementIds = <Element, int>{}; |
| 55 Set<String> excludedVariableNames = new Set<String>(); | 55 Set<String> excludedVariableNames = new Set<String>(); |
| 56 | 56 |
| 57 ExtractLocalRefactoringImpl( | 57 ExtractLocalRefactoringImpl( |
| 58 this.unit, this.selectionOffset, this.selectionLength) { | 58 this.unit, this.selectionOffset, this.selectionLength) { |
| 59 unitElement = unit.element; | 59 unitElement = unit.element; |
| 60 selectionRange = new SourceRange(selectionOffset, selectionLength); | 60 selectionRange = new SourceRange(selectionOffset, selectionLength); |
| 61 utils = new CorrectionUtils(unit); | 61 utils = new CorrectionUtils(unit); |
| 62 file = unitElement.source.fullName; |
| 62 } | 63 } |
| 63 | 64 |
| 64 @override | 65 @override |
| 65 String get refactoringName => 'Extract Local Variable'; | 66 String get refactoringName => 'Extract Local Variable'; |
| 66 | 67 |
| 67 String get _declarationKeyword { | 68 String get _declarationKeyword { |
| 68 if (_isPartOfConstantExpression(rootExpression)) { | 69 if (_isPartOfConstantExpression(rootExpression)) { |
| 69 return "const"; | 70 return "const"; |
| 70 } else { | 71 } else { |
| 71 return "var"; | 72 return "var"; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 @override | 111 @override |
| 111 Future<SourceChange> createChange() { | 112 Future<SourceChange> createChange() { |
| 112 SourceChange change = new SourceChange(refactoringName); | 113 SourceChange change = new SourceChange(refactoringName); |
| 113 // prepare occurrences | 114 // prepare occurrences |
| 114 List<SourceRange> occurrences; | 115 List<SourceRange> occurrences; |
| 115 if (extractAll) { | 116 if (extractAll) { |
| 116 occurrences = this.occurrences; | 117 occurrences = this.occurrences; |
| 117 } else { | 118 } else { |
| 118 occurrences = [selectionRange]; | 119 occurrences = [selectionRange]; |
| 119 } | 120 } |
| 121 occurrences.sort((a, b) => a.offset - b.offset); |
| 120 // If the whole expression of a statement is selected, like '1 + 2', | 122 // If the whole expression of a statement is selected, like '1 + 2', |
| 121 // then convert it into a variable declaration statement. | 123 // then convert it into a variable declaration statement. |
| 122 if (wholeStatementExpression && occurrences.length == 1) { | 124 if (wholeStatementExpression && occurrences.length == 1) { |
| 123 String keyword = _declarationKeyword; | 125 String keyword = _declarationKeyword; |
| 124 String declarationSource = '$keyword $name = '; | 126 String declarationSource = '$keyword $name = '; |
| 125 SourceEdit edit = | 127 SourceEdit edit = |
| 126 new SourceEdit(singleExpression.offset, 0, declarationSource); | 128 new SourceEdit(singleExpression.offset, 0, declarationSource); |
| 127 doSourceChange_addElementEdit(change, unitElement, edit); | 129 doSourceChange_addElementEdit(change, unitElement, edit); |
| 128 return new Future.value(change); | 130 return new Future.value(change); |
| 129 } | 131 } |
| 132 // prepare positions |
| 133 List<Position> positions = <Position>[]; |
| 134 int occurrencesShift = 0; |
| 135 void addPosition(int offset) { |
| 136 positions.add(new Position(file, offset)); |
| 137 } |
| 130 // add variable declaration | 138 // add variable declaration |
| 131 { | 139 { |
| 132 String declarationSource; | 140 String declarationCode; |
| 141 int nameOffsetInDeclarationCode; |
| 133 if (stringLiteralPart != null) { | 142 if (stringLiteralPart != null) { |
| 134 declarationSource = "var $name = '$stringLiteralPart';"; | 143 declarationCode = 'var '; |
| 144 nameOffsetInDeclarationCode = declarationCode.length; |
| 145 declarationCode += "$name = '$stringLiteralPart';"; |
| 135 } else { | 146 } else { |
| 136 String keyword = _declarationKeyword; | 147 String keyword = _declarationKeyword; |
| 137 String initializerSource = utils.getRangeText(selectionRange); | 148 String initializerCode = utils.getRangeText(selectionRange); |
| 138 declarationSource = "$keyword $name = $initializerSource;"; | 149 declarationCode = '$keyword '; |
| 150 nameOffsetInDeclarationCode = declarationCode.length; |
| 151 declarationCode += '$name = $initializerCode;'; |
| 139 } | 152 } |
| 140 String eol = utils.endOfLine; | |
| 141 // prepare location for declaration | 153 // prepare location for declaration |
| 142 AstNode target = _findDeclarationTarget(occurrences); | 154 AstNode target = _findDeclarationTarget(occurrences); |
| 155 String eol = utils.endOfLine; |
| 143 // insert variable declaration | 156 // insert variable declaration |
| 144 if (target is Statement) { | 157 if (target is Statement) { |
| 145 String prefix = utils.getNodePrefix(target); | 158 String prefix = utils.getNodePrefix(target); |
| 146 SourceEdit edit = | 159 SourceEdit edit = |
| 147 new SourceEdit(target.offset, 0, declarationSource + eol + prefix); | 160 new SourceEdit(target.offset, 0, declarationCode + eol + prefix); |
| 148 doSourceChange_addElementEdit(change, unitElement, edit); | 161 doSourceChange_addElementEdit(change, unitElement, edit); |
| 162 addPosition(edit.offset + nameOffsetInDeclarationCode); |
| 163 occurrencesShift = edit.replacement.length; |
| 149 } else if (target is ExpressionFunctionBody) { | 164 } else if (target is ExpressionFunctionBody) { |
| 150 String prefix = utils.getNodePrefix(target.parent); | 165 String prefix = utils.getNodePrefix(target.parent); |
| 151 String indent = utils.getIndent(1); | 166 String indent = utils.getIndent(1); |
| 152 String declStatement = prefix + indent + declarationSource + eol; | |
| 153 String exprStatement = prefix + indent + 'return '; | |
| 154 Expression expr = target.expression; | 167 Expression expr = target.expression; |
| 155 doSourceChange_addElementEdit( | 168 { |
| 156 change, | 169 String code = '{' + eol + prefix + indent; |
| 157 unitElement, | 170 addPosition( |
| 158 new SourceEdit(target.offset, expr.offset - target.offset, | 171 target.offset + code.length + nameOffsetInDeclarationCode); |
| 159 '{' + eol + declStatement + exprStatement)); | 172 code += declarationCode + eol; |
| 173 code += prefix + indent + 'return '; |
| 174 SourceEdit edit = |
| 175 new SourceEdit(target.offset, expr.offset - target.offset, code); |
| 176 occurrencesShift = target.offset + code.length - expr.offset; |
| 177 doSourceChange_addElementEdit(change, unitElement, edit); |
| 178 } |
| 160 doSourceChange_addElementEdit(change, unitElement, | 179 doSourceChange_addElementEdit(change, unitElement, |
| 161 new SourceEdit(expr.end, 0, ';' + eol + prefix + '}')); | 180 new SourceEdit(expr.end, 0, ';' + eol + prefix + '}')); |
| 162 } | 181 } |
| 163 } | 182 } |
| 164 // prepare replacement | 183 // prepare replacement |
| 165 String occurrenceReplacement = name; | 184 String occurrenceReplacement = name; |
| 166 if (stringLiteralPart != null) { | 185 if (stringLiteralPart != null) { |
| 167 occurrenceReplacement = "\${$name}"; | 186 occurrenceReplacement = "\${$name}"; |
| 187 occurrencesShift += 2; |
| 168 } | 188 } |
| 169 // replace occurrences with variable reference | 189 // replace occurrences with variable reference |
| 170 for (SourceRange range in occurrences) { | 190 for (SourceRange range in occurrences) { |
| 171 SourceEdit edit = newSourceEdit_range(range, occurrenceReplacement); | 191 SourceEdit edit = newSourceEdit_range(range, occurrenceReplacement); |
| 192 addPosition(range.offset + occurrencesShift); |
| 193 occurrencesShift += name.length - range.length; |
| 172 doSourceChange_addElementEdit(change, unitElement, edit); | 194 doSourceChange_addElementEdit(change, unitElement, edit); |
| 173 } | 195 } |
| 196 // add the linked group |
| 197 change.addLinkedEditGroup(new LinkedEditGroup( |
| 198 positions, |
| 199 name.length, |
| 200 names |
| 201 .map((name) => new LinkedEditSuggestion( |
| 202 name, LinkedEditSuggestionKind.VARIABLE)) |
| 203 .toList())); |
| 174 // done | 204 // done |
| 175 return new Future.value(change); | 205 return new Future.value(change); |
| 176 } | 206 } |
| 177 | 207 |
| 178 @override | 208 @override |
| 179 bool requiresPreview() => false; | 209 bool requiresPreview() => false; |
| 180 | 210 |
| 181 /** | 211 /** |
| 182 * Checks if [selectionRange] selects [Expression] which can be extracted, and | 212 * Checks if [selectionRange] selects [Expression] which can be extracted, and |
| 183 * location of this [Expression] in AST allows extracting. | 213 * location of this [Expression] in AST allows extracting. |
| 184 */ | 214 */ |
| 185 RefactoringStatus _checkSelection() { | 215 RefactoringStatus _checkSelection() { |
| 186 String selectionStr; | 216 String selectionStr; |
| 187 // exclude whitespaces | 217 // exclude whitespaces |
| 188 { | 218 { |
| 189 selectionStr = utils.getRangeText(selectionRange); | 219 selectionStr = utils.getRangeText(selectionRange); |
| 190 int numLeading = countLeadingWhitespaces(selectionStr); | 220 int numLeading = countLeadingWhitespaces(selectionStr); |
| 191 int numTrailing = countTrailingWhitespaces(selectionStr); | 221 int numTrailing = countTrailingWhitespaces(selectionStr); |
| 192 int offset = selectionRange.offset + numLeading; | 222 int offset = selectionRange.offset + numLeading; |
| 193 int end = selectionRange.end - numTrailing; | 223 int end = selectionRange.end - numTrailing; |
| 194 selectionRange = new SourceRange(offset, end - offset); | 224 selectionRange = new SourceRange(offset, end - offset); |
| 195 } | 225 } |
| 196 // get covering node | 226 // get covering node |
| 197 AstNode coveringNode = new NodeLocator( | 227 AstNode coveringNode = |
| 198 selectionRange.offset, selectionRange.end).searchWithin(unit); | 228 new NodeLocator(selectionRange.offset, selectionRange.end) |
| 229 .searchWithin(unit); |
| 199 // compute covering expressions | 230 // compute covering expressions |
| 200 for (AstNode node = coveringNode; | 231 for (AstNode node = coveringNode; |
| 201 node is Expression || node is ArgumentList; | 232 node is Expression || node is ArgumentList; |
| 202 node = node.parent) { | 233 node = node.parent) { |
| 203 AstNode parent = node.parent; | 234 AstNode parent = node.parent; |
| 204 // skip ArgumentList | 235 // skip ArgumentList |
| 205 if (node is ArgumentList) { | 236 if (node is ArgumentList) { |
| 206 continue; | 237 continue; |
| 207 } | 238 } |
| 208 // skip AssignmentExpression | 239 // skip AssignmentExpression |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 629 | 660 |
| 630 _TokenLocalElementVisitor(this.map); | 661 _TokenLocalElementVisitor(this.map); |
| 631 | 662 |
| 632 visitSimpleIdentifier(SimpleIdentifier node) { | 663 visitSimpleIdentifier(SimpleIdentifier node) { |
| 633 Element element = node.staticElement; | 664 Element element = node.staticElement; |
| 634 if (element is LocalVariableElement) { | 665 if (element is LocalVariableElement) { |
| 635 map[node.token] = element; | 666 map[node.token] = element; |
| 636 } | 667 } |
| 637 } | 668 } |
| 638 } | 669 } |
| OLD | NEW |