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

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

Issue 489413003: Finish 'Extract Local' refactoring. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 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/protocol2.dart' show SourceEdit; 9 import 'package:analysis_server/src/protocol2.dart' show SourceEdit;
10 import 'package:analysis_server/src/services/correction/change.dart'; 10 import 'package:analysis_server/src/services/correction/change.dart';
11 import 'package:analysis_server/src/services/correction/name_suggestion.dart';
11 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ; 12 import 'package:analysis_server/src/services/correction/selection_analyzer.dart' ;
12 import 'package:analysis_server/src/services/correction/source_range.dart'; 13 import 'package:analysis_server/src/services/correction/source_range.dart';
13 import 'package:analysis_server/src/services/correction/status.dart'; 14 import 'package:analysis_server/src/services/correction/status.dart';
14 import 'package:analysis_server/src/services/correction/strings.dart'; 15 import 'package:analysis_server/src/services/correction/strings.dart';
15 import 'package:analysis_server/src/services/correction/util.dart'; 16 import 'package:analysis_server/src/services/correction/util.dart';
16 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart '; 17 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart ';
17 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; 18 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
18 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da rt'; 19 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da rt';
19 import 'package:analysis_server/src/services/search/element_visitors.dart'; 20 import 'package:analysis_server/src/services/search/element_visitors.dart';
20 import 'package:analyzer/src/generated/ast.dart'; 21 import 'package:analyzer/src/generated/ast.dart';
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 final List<SourceRange> occurrences = <SourceRange>[]; 53 final List<SourceRange> occurrences = <SourceRange>[];
53 final Set<String> excludedVariableNames = new Set<String>(); 54 final Set<String> excludedVariableNames = new Set<String>();
54 55
55 ExtractLocalRefactoringImpl(this.unit, this.selectionOffset, 56 ExtractLocalRefactoringImpl(this.unit, this.selectionOffset,
56 this.selectionLength) { 57 this.selectionLength) {
57 file = unit.element.source.fullName; 58 file = unit.element.source.fullName;
58 selectionRange = new SourceRange(selectionOffset, selectionLength); 59 selectionRange = new SourceRange(selectionOffset, selectionLength);
59 utils = new CorrectionUtils(unit); 60 utils = new CorrectionUtils(unit);
60 } 61 }
61 62
62 String get declarationKeyword { 63 @override
64 String get refactoringName => 'Extract Local Variable';
65
66 String get _declarationKeyword {
63 if (_isPartOfConstantExpression(rootExpression)) { 67 if (_isPartOfConstantExpression(rootExpression)) {
64 return "const"; 68 return "const";
65 } else { 69 } else {
66 return "var"; 70 return "var";
67 } 71 }
68 } 72 }
69 73
70 @override 74 @override
71 String get refactoringName => 'Extract Local Variable';
72
73 @override
74 Future<RefactoringStatus> checkFinalConditions() { 75 Future<RefactoringStatus> checkFinalConditions() {
75 RefactoringStatus result = new RefactoringStatus(); 76 RefactoringStatus result = new RefactoringStatus();
76 if (excludedVariableNames.contains(name)) { 77 if (excludedVariableNames.contains(name)) {
77 result.addWarning( 78 result.addWarning(
78 format( 79 format(
79 "A variable with name '{0}' is already defined in the visible scop e.", 80 "A variable with name '{0}' is already defined in the visible scop e.",
80 name)); 81 name));
81 } 82 }
82 return new Future.value(result); 83 return new Future.value(result);
83 } 84 }
84 85
85 @override 86 @override
86 Future<RefactoringStatus> checkInitialConditions() { 87 Future<RefactoringStatus> checkInitialConditions() {
87 RefactoringStatus result = new RefactoringStatus(); 88 RefactoringStatus result = new RefactoringStatus();
88 // selection 89 // selection
89 result.addStatus(_checkSelection()); 90 result.addStatus(_checkSelection());
91 if (result.hasFatalError) {
92 return new Future.value(result);
93 }
90 // occurrences 94 // occurrences
91 if (!result.hasFatalError) { 95 _prepareOccurrences();
92 _prepareOccurrences(); 96 _prepareOffsetsLengths();
93 _prepareExcludedNames(); 97 // names
94 } 98 _prepareExcludedNames();
95 // suggested names
96 _prepareNames(); 99 _prepareNames();
97 // done 100 // done
98 return new Future.value(result); 101 return new Future.value(result);
99 } 102 }
100 103
101 @override 104 @override
102 RefactoringStatus checkName() { 105 RefactoringStatus checkName() {
103 return validateVariableName(name); 106 return validateVariableName(name);
104 } 107 }
105 108
106 @override 109 @override
107 Future<Change> createChange() { 110 Future<Change> createChange() {
108 Change change = new Change(refactoringName); 111 Change change = new Change(refactoringName);
109 // prepare occurrences 112 // prepare occurrences
110 List<SourceRange> occurrences; 113 List<SourceRange> occurrences;
111 if (extractAll) { 114 if (extractAll) {
112 occurrences = this.occurrences; 115 occurrences = this.occurrences;
113 } else { 116 } else {
114 occurrences = [selectionRange]; 117 occurrences = [selectionRange];
115 } 118 }
116 // If the whole expression of a statement is selected, like '1 + 2', 119 // If the whole expression of a statement is selected, like '1 + 2',
117 // then convert it into a variable declaration statement. 120 // then convert it into a variable declaration statement.
118 if (wholeStatementExpression && occurrences.length == 1) { 121 if (wholeStatementExpression && occurrences.length == 1) {
119 String keyword = declarationKeyword; 122 String keyword = _declarationKeyword;
120 String declarationSource = '$keyword $name = '; 123 String declarationSource = '$keyword $name = ';
121 SourceEdit edit = 124 SourceEdit edit =
122 new SourceEdit(singleExpression.offset, 0, declarationSource); 125 new SourceEdit(singleExpression.offset, 0, declarationSource);
123 change.addEdit(file, edit); 126 change.addEdit(file, edit);
124 return new Future.value(change); 127 return new Future.value(change);
125 } 128 }
126 // add variable declaration 129 // add variable declaration
127 { 130 {
128 String declarationSource; 131 String declarationSource;
129 if (stringLiteralPart != null) { 132 if (stringLiteralPart != null) {
130 declarationSource = "var ${name} = '${stringLiteralPart}';"; 133 declarationSource = "var ${name} = '${stringLiteralPart}';";
131 } else { 134 } else {
132 String keyword = declarationKeyword; 135 String keyword = _declarationKeyword;
133 String initializerSource = utils.getRangeText(selectionRange); 136 String initializerSource = utils.getRangeText(selectionRange);
134 declarationSource = "${keyword} ${name} = ${initializerSource};"; 137 declarationSource = "${keyword} ${name} = ${initializerSource};";
135 } 138 }
136 // prepare location for declaration 139 // prepare location for declaration
137 Statement targetStatement = _findTargetStatement(occurrences); 140 Statement targetStatement = _findTargetStatement(occurrences);
138 String prefix = utils.getNodePrefix(targetStatement); 141 String prefix = utils.getNodePrefix(targetStatement);
139 // insert variable declaration 142 // insert variable declaration
140 String eol = utils.endOfLine; 143 String eol = utils.endOfLine;
141 SourceEdit edit = new SourceEdit( 144 SourceEdit edit = new SourceEdit(
142 targetStatement.offset, 145 targetStatement.offset,
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 List<AstNode> _findNodes(List<SourceRange> ranges) { 235 List<AstNode> _findNodes(List<SourceRange> ranges) {
233 List<AstNode> nodes = <AstNode>[]; 236 List<AstNode> nodes = <AstNode>[];
234 for (SourceRange range in ranges) { 237 for (SourceRange range in ranges) {
235 AstNode node = new NodeLocator.con1(range.offset).searchWithin(unit); 238 AstNode node = new NodeLocator.con1(range.offset).searchWithin(unit);
236 nodes.add(node); 239 nodes.add(node);
237 } 240 }
238 return nodes; 241 return nodes;
239 } 242 }
240 243
241 /** 244 /**
242 * @return the [Statement] such that variable declaration added before it will be visible in 245 * Returns the [Statement] such that variable declaration added before it is
243 * all given occurrences. 246 * visible at all given occurrences.
244 */ 247 */
245 Statement _findTargetStatement(List<SourceRange> occurrences) { 248 Statement _findTargetStatement(List<SourceRange> occurrences) {
246 List<AstNode> nodes = _findNodes(occurrences); 249 List<AstNode> nodes = _findNodes(occurrences);
247 List<AstNode> firstParents = getParents(nodes[0]); 250 List<AstNode> firstParents = getParents(nodes[0]);
248 AstNode commonParent = getNearestCommonAncestor(nodes); 251 AstNode commonParent = getNearestCommonAncestor(nodes);
249 if (commonParent is Block) { 252 if (commonParent is Block) {
250 int commonIndex = firstParents.indexOf(commonParent); 253 int commonIndex = firstParents.indexOf(commonParent);
251 return firstParents[commonIndex + 1] as Statement; 254 return firstParents[commonIndex + 1] as Statement;
252 } else { 255 } else {
253 return commonParent.getAncestor((node) => node is Statement); 256 return commonParent.getAncestor((node) => node is Statement);
254 } 257 }
255 } 258 }
256 259
257 /** 260 /**
258 * @return `true` if it is OK to extract the node with the given [SourceRange] . 261 * Checks if it is OK to extract the node with the given [SourceRange].
259 */ 262 */
260 bool _isExtractable(SourceRange range) { 263 bool _isExtractable(SourceRange range) {
261 _ExtractExpressionAnalyzer analyzer = new _ExtractExpressionAnalyzer(range); 264 _ExtractExpressionAnalyzer analyzer = new _ExtractExpressionAnalyzer(range);
262 utils.unit.accept(analyzer); 265 utils.unit.accept(analyzer);
263 return analyzer.status.isOK; 266 return analyzer.status.isOK;
264 } 267 }
265 268
266 bool _isPartOfConstantExpression(AstNode node) { 269 bool _isPartOfConstantExpression(AstNode node) {
267 if (node is TypedLiteral) { 270 if (node is TypedLiteral) {
268 return node.constKeyword != null; 271 return node.constKeyword != null;
269 } 272 }
270 if (node is InstanceCreationExpression) { 273 if (node is InstanceCreationExpression) {
271 InstanceCreationExpression creation = node; 274 InstanceCreationExpression creation = node;
272 return creation.isConst; 275 return creation.isConst;
273 } 276 }
274 if (node is ArgumentList || 277 if (node is ArgumentList ||
275 node is ConditionalExpression || 278 node is ConditionalExpression ||
276 node is BinaryExpression || 279 node is BinaryExpression ||
277 node is ParenthesizedExpression || 280 node is ParenthesizedExpression ||
278 node is PrefixExpression || 281 node is PrefixExpression ||
279 node is Literal || 282 node is Literal ||
280 node is MapLiteralEntry) { 283 node is MapLiteralEntry) {
281 return _isPartOfConstantExpression(node.parent); 284 return _isPartOfConstantExpression(node.parent);
282 } 285 }
283 return false; 286 return false;
284 } 287 }
285 288
286 void _prepareExcludedNames() { 289 void _prepareExcludedNames() {
287 excludedVariableNames.clear(); 290 excludedVariableNames.clear();
288 // TODO(scheglov) clean up?
289 AstNode enclosingNode = 291 AstNode enclosingNode =
290 new NodeLocator.con1(selectionOffset).searchWithin(unit); 292 new NodeLocator.con1(selectionOffset).searchWithin(unit);
291 Block enclosingBlock = enclosingNode.getAncestor((node) => node is Block); 293 Block enclosingBlock = enclosingNode.getAncestor((node) => node is Block);
292 if (enclosingBlock != null) { 294 if (enclosingBlock != null) {
293 SourceRange newVariableVisibleRange = 295 SourceRange newVariableVisibleRange =
294 rangeStartEnd(selectionRange, enclosingBlock.end); 296 rangeStartEnd(selectionRange, enclosingBlock.end);
295 ExecutableElement enclosingExecutable = 297 ExecutableElement enclosingExecutable =
296 getEnclosingExecutableElement(enclosingNode); 298 getEnclosingExecutableElement(enclosingNode);
297 if (enclosingExecutable != null) { 299 if (enclosingExecutable != null) {
298 visitChildren(enclosingExecutable, (Element element) { 300 visitChildren(enclosingExecutable, (Element element) {
299 if (element is LocalElement) { 301 if (element is LocalElement) {
300 SourceRange elementRange = element.visibleRange; 302 SourceRange elementRange = element.visibleRange;
301 if (elementRange != null && 303 if (elementRange != null &&
302 elementRange.intersects(newVariableVisibleRange)) { 304 elementRange.intersects(newVariableVisibleRange)) {
303 excludedVariableNames.add(element.displayName); 305 excludedVariableNames.add(element.displayName);
304 } 306 }
305 } 307 }
306 return true; 308 return true;
307 }); 309 });
308 } 310 }
309 } 311 }
310 } 312 }
311 313
312 void _prepareNames() { 314 void _prepareNames() {
313 names.clear(); 315 names.clear();
314 // TODO(scheglov) implement 316 if (stringLiteralPart != null) {
315 // Set<String> excluded = excludedVariableNames; 317 names.addAll(
316 // if (_stringLiteralPart != null) { 318 getVariableNameSuggestionsForText(stringLiteralPart, excludedVariableN ames));
317 // return getVariableNameSuggestions(_stringLiteralPart, excluded); 319 } else if (singleExpression != null) {
318 // } else if (_singleExpression != null) { 320 names.addAll(
319 // _guessedNames = CorrectionUtils.getVariableNameSuggestions2(_singleExpre ssion.staticType, _singleExpression, excluded); 321 getVariableNameSuggestionsForExpression(
320 // } else { 322 singleExpression.staticType,
321 // _guessedNames = ArrayUtils.EMPTY_STRING_ARRAY; 323 singleExpression,
322 // } 324 excludedVariableNames));
325 }
323 } 326 }
324 327
325 /** 328 /**
326 * @return all occurrences of the source which matches given selection, sorted by offset. First 329 * Prepares all occurrences of the source which matches given selection,
327 * [SourceRange] is same as the given selection. May be empty, but not 330 * sorted by offsets.
328 * <code>null</code>.
329 */ 331 */
330 List<SourceRange> _prepareOccurrences() { 332 void _prepareOccurrences() {
333 occurrences.clear();
331 // prepare selection 334 // prepare selection
332 String selectionSource; 335 String selectionSource;
333 { 336 {
334 String rawSelectionSource = utils.getRangeText(selectionRange); 337 String rawSelectionSource = utils.getRangeText(selectionRange);
335 List<Token> selectionTokens = TokenUtils.getTokens(rawSelectionSource); 338 List<Token> selectionTokens = TokenUtils.getTokens(rawSelectionSource);
336 selectionSource = selectionTokens.join(_TOKEN_SEPARATOR); 339 selectionSource = selectionTokens.join(_TOKEN_SEPARATOR);
337 } 340 }
338 // prepare enclosing function 341 // prepare enclosing function
339 AstNode enclosingFunction; 342 AstNode enclosingFunction;
340 { 343 {
341 AstNode selectionNode = 344 AstNode selectionNode =
342 new NodeLocator.con1(selectionOffset).searchWithin(unit); 345 new NodeLocator.con1(selectionOffset).searchWithin(unit);
343 enclosingFunction = getEnclosingExecutableNode(selectionNode); 346 enclosingFunction = getEnclosingExecutableNode(selectionNode);
344 } 347 }
345 // visit function 348 // visit function
346 enclosingFunction.accept( 349 enclosingFunction.accept(
347 new _OccurrencesVisitor(this, occurrences, selectionSource)); 350 new _OccurrencesVisitor(this, occurrences, selectionSource));
348 // done 351 }
349 return occurrences; 352
353 void _prepareOffsetsLengths() {
354 offsets.clear();
355 lengths.clear();
356 for (SourceRange occurrence in occurrences) {
357 offsets.add(occurrence.offset);
358 lengths.add(occurrence.length);
359 }
350 } 360 }
351 } 361 }
352 362
353 363
354 /** 364 /**
355 * [SelectionAnalyzer] for [ExtractLocalRefactoringImpl]. 365 * [SelectionAnalyzer] for [ExtractLocalRefactoringImpl].
356 */ 366 */
357 class _ExtractExpressionAnalyzer extends SelectionAnalyzer { 367 class _ExtractExpressionAnalyzer extends SelectionAnalyzer {
358 final RefactoringStatus status = new RefactoringStatus(); 368 final RefactoringStatus status = new RefactoringStatus();
359 369
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 } 414 }
405 415
406 /** 416 /**
407 * Records fatal error with given message and [RefactoringStatusContext]. 417 * Records fatal error with given message and [RefactoringStatusContext].
408 */ 418 */
409 void _invalidSelection(String message, RefactoringStatusContext context) { 419 void _invalidSelection(String message, RefactoringStatusContext context) {
410 status.addFatalError(message, context); 420 status.addFatalError(message, context);
411 reset(); 421 reset();
412 } 422 }
413 423
414 bool _isFirstSelectedNode(AstNode node) => identical(firstSelectedNode, node); 424 bool _isFirstSelectedNode(AstNode node) => node == firstSelectedNode;
415 } 425 }
416 426
417 427
418 class _HasStatementVisitor extends GeneralizingAstVisitor { 428 class _HasStatementVisitor extends GeneralizingAstVisitor {
419 final List<bool> result; 429 final List<bool> result;
420 430
421 _HasStatementVisitor(this.result); 431 _HasStatementVisitor(this.result);
422 432
423 @override 433 @override
424 visitStatement(Statement node) { 434 visitStatement(Statement node) {
425 result[0] = true; 435 result[0] = true;
426 } 436 }
427 } 437 }
428 438
429 439
430 class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> { 440 class _OccurrencesVisitor extends GeneralizingAstVisitor<Object> {
431 final ExtractLocalRefactoringImpl ref; 441 final ExtractLocalRefactoringImpl ref;
432 442 final List<SourceRange> occurrences;
433 List<SourceRange> occurrences; 443 final String selectionSource;
434
435 String selectionSource;
436 444
437 _OccurrencesVisitor(this.ref, this.occurrences, this.selectionSource); 445 _OccurrencesVisitor(this.ref, this.occurrences, this.selectionSource);
438 446
439 @override 447 @override
440 Object visitBinaryExpression(BinaryExpression node) { 448 Object visitBinaryExpression(BinaryExpression node) {
441 if (!_hasStatements(node)) { 449 if (!_hasStatements(node)) {
442 _tryToFindOccurrenceFragment(node); 450 _tryToFindOccurrenceFragment(node);
443 return null; 451 return null;
444 } 452 }
445 return super.visitBinaryExpression(node); 453 return super.visitBinaryExpression(node);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 Token startToken = nodeTokens[startTokenIndex]; 529 Token startToken = nodeTokens[startTokenIndex];
522 Token endToken = nodeTokens[endTokenIndex]; 530 Token endToken = nodeTokens[endTokenIndex];
523 // add occurrence range 531 // add occurrence range
524 int occuStart = nodeOffset + startToken.offset; 532 int occuStart = nodeOffset + startToken.offset;
525 int occuEnd = nodeOffset + endToken.end; 533 int occuEnd = nodeOffset + endToken.end;
526 SourceRange occuRange = rangeStartEnd(occuStart, occuEnd); 534 SourceRange occuRange = rangeStartEnd(occuStart, occuEnd);
527 _addOccurrence(occuRange); 535 _addOccurrence(occuRange);
528 } 536 }
529 } 537 }
530 } 538 }
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