| 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_method; | 5 library services.src.refactoring.extract_method; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' hide Element; | 9 import 'package:analysis_server/src/protocol_server.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/statement_analyzer.dart'
; | 13 import 'package:analysis_server/src/services/correction/statement_analyzer.dart'
; |
| 14 import 'package:analysis_server/src/services/correction/status.dart'; | 14 import 'package:analysis_server/src/services/correction/status.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/refactoring/rename_class_member.dar
t'; | 19 import 'package:analysis_server/src/services/refactoring/rename_class_member.dar
t'; |
| 20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart
'; | 20 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart
'; |
| 21 import 'package:analysis_server/src/services/search/element_visitors.dart'; | 21 import 'package:analysis_server/src/services/search/element_visitors.dart'; |
| 22 import 'package:analysis_server/src/services/search/search_engine.dart'; | 22 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 23 import 'package:analyzer/src/generated/ast.dart'; | 23 import 'package:analyzer/src/generated/ast.dart'; |
| 24 import 'package:analyzer/src/generated/element.dart'; | 24 import 'package:analyzer/src/generated/element.dart'; |
| 25 import 'package:analyzer/src/generated/java_core.dart'; | 25 import 'package:analyzer/src/generated/java_core.dart'; |
| 26 import 'package:analyzer/src/generated/resolver.dart' show ExitDetector; |
| 26 import 'package:analyzer/src/generated/scanner.dart'; | 27 import 'package:analyzer/src/generated/scanner.dart'; |
| 27 import 'package:analyzer/src/generated/source.dart'; | 28 import 'package:analyzer/src/generated/source.dart'; |
| 28 import 'package:analyzer/src/generated/resolver.dart' show ExitDetector; | |
| 29 | 29 |
| 30 | 30 |
| 31 const String _TOKEN_SEPARATOR = '\uFFFF'; | 31 const String _TOKEN_SEPARATOR = '\uFFFF'; |
| 32 | 32 |
| 33 | 33 |
| 34 /** | 34 /** |
| 35 * Returns the "normalized" version of the given source, which is reconstructed | 35 * Returns the "normalized" version of the given source, which is reconstructed |
| 36 * from tokens, so ignores all the comments and spaces. | 36 * from tokens, so ignores all the comments and spaces. |
| 37 */ | 37 */ |
| 38 String _getNormalizedSource(String src) { | 38 String _getNormalizedSource(String src) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 60 ExtractMethodRefactoring { | 60 ExtractMethodRefactoring { |
| 61 static const ERROR_EXITS = | 61 static const ERROR_EXITS = |
| 62 'Selected statements contain a return statement, but not all possible ' | 62 'Selected statements contain a return statement, but not all possible ' |
| 63 'execuion flows exit. Semantics may not be preserved.'; | 63 'execuion flows exit. Semantics may not be preserved.'; |
| 64 | 64 |
| 65 final SearchEngine searchEngine; | 65 final SearchEngine searchEngine; |
| 66 final CompilationUnit unit; | 66 final CompilationUnit unit; |
| 67 final int selectionOffset; | 67 final int selectionOffset; |
| 68 final int selectionLength; | 68 final int selectionLength; |
| 69 CompilationUnitElement unitElement; | 69 CompilationUnitElement unitElement; |
| 70 LibraryElement libraryElement; |
| 70 SourceRange selectionRange; | 71 SourceRange selectionRange; |
| 71 CorrectionUtils utils; | 72 CorrectionUtils utils; |
| 73 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); |
| 72 | 74 |
| 73 String returnType; | 75 String returnType; |
| 74 String name; | 76 String name; |
| 75 bool extractAll = true; | 77 bool extractAll = true; |
| 76 bool createGetter = false; | 78 bool createGetter = false; |
| 77 final List<String> names = <String>[]; | 79 final List<String> names = <String>[]; |
| 78 final List<int> offsets = <int>[]; | 80 final List<int> offsets = <int>[]; |
| 79 final List<int> lengths = <int>[]; | 81 final List<int> lengths = <int>[]; |
| 80 | 82 |
| 81 Set<String> _usedNames = new Set<String>(); | 83 Set<String> _usedNames = new Set<String>(); |
| 82 Set<String> _excludedNames = new Set<String>(); | 84 Set<String> _excludedNames = new Set<String>(); |
| 83 List<RefactoringMethodParameter> _parameters = <RefactoringMethodParameter>[]; | 85 List<RefactoringMethodParameter> _parameters = <RefactoringMethodParameter>[]; |
| 84 Map<String, RefactoringMethodParameter> _parametersMap = <String, | 86 Map<String, RefactoringMethodParameter> _parametersMap = <String, |
| 85 RefactoringMethodParameter>{}; | 87 RefactoringMethodParameter>{}; |
| 86 Map<String, List<SourceRange>> _parameterReferencesMap = <String, | 88 Map<String, List<SourceRange>> _parameterReferencesMap = <String, |
| 87 List<SourceRange>>{}; | 89 List<SourceRange>>{}; |
| 88 DartType _returnType; | 90 DartType _returnType; |
| 89 String _returnVariableName; | 91 String _returnVariableName; |
| 90 AstNode _parentMember; | 92 AstNode _parentMember; |
| 91 Expression _selectionExpression; | 93 Expression _selectionExpression; |
| 92 FunctionExpression _selectionFunctionExpression; | 94 FunctionExpression _selectionFunctionExpression; |
| 93 List<Statement> _selectionStatements; | 95 List<Statement> _selectionStatements; |
| 94 List<_Occurrence> _occurrences = []; | 96 List<_Occurrence> _occurrences = []; |
| 95 bool _staticContext = false; | 97 bool _staticContext = false; |
| 96 | 98 |
| 97 ExtractMethodRefactoringImpl(this.searchEngine, this.unit, | 99 ExtractMethodRefactoringImpl(this.searchEngine, this.unit, |
| 98 this.selectionOffset, this.selectionLength) { | 100 this.selectionOffset, this.selectionLength) { |
| 99 unitElement = unit.element; | 101 unitElement = unit.element; |
| 102 libraryElement = unitElement.library; |
| 100 selectionRange = new SourceRange(selectionOffset, selectionLength); | 103 selectionRange = new SourceRange(selectionOffset, selectionLength); |
| 101 utils = new CorrectionUtils(unit); | 104 utils = new CorrectionUtils(unit); |
| 102 } | 105 } |
| 103 | 106 |
| 104 bool get canCreateGetter { | 107 bool get canCreateGetter { |
| 105 if (!parameters.isEmpty) { | 108 if (!parameters.isEmpty) { |
| 106 return false; | 109 return false; |
| 107 } | 110 } |
| 108 if (_selectionExpression != null) { | 111 if (_selectionExpression != null) { |
| 109 if (_selectionExpression is AssignmentExpression) { | 112 if (_selectionExpression is AssignmentExpression) { |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 } | 210 } |
| 208 return new Future.value(result); | 211 return new Future.value(result); |
| 209 } | 212 } |
| 210 | 213 |
| 211 @override | 214 @override |
| 212 RefactoringStatus checkName() { | 215 RefactoringStatus checkName() { |
| 213 return validateMethodName(name); | 216 return validateMethodName(name); |
| 214 } | 217 } |
| 215 | 218 |
| 216 @override | 219 @override |
| 217 Future<SourceChange> createChange() { | 220 Future<SourceChange> createChange() async { |
| 218 SourceChange change = new SourceChange(refactoringName); | 221 SourceChange change = new SourceChange(refactoringName); |
| 219 // replace occurrences with method invocation | 222 // replace occurrences with method invocation |
| 220 for (_Occurrence occurence in _occurrences) { | 223 for (_Occurrence occurence in _occurrences) { |
| 221 SourceRange range = occurence.range; | 224 SourceRange range = occurence.range; |
| 222 // may be replacement of duplicates disabled | 225 // may be replacement of duplicates disabled |
| 223 if (!extractAll && !occurence.isSelection) { | 226 if (!extractAll && !occurence.isSelection) { |
| 224 continue; | 227 continue; |
| 225 } | 228 } |
| 226 // prepare invocation source | 229 // prepare invocation source |
| 227 String invocationSource; | 230 String invocationSource; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 // closure | 305 // closure |
| 303 if (_selectionFunctionExpression != null) { | 306 if (_selectionFunctionExpression != null) { |
| 304 declarationSource = '${name}${returnExpressionSource}'; | 307 declarationSource = '${name}${returnExpressionSource}'; |
| 305 if (_selectionFunctionExpression.body is ExpressionFunctionBody) { | 308 if (_selectionFunctionExpression.body is ExpressionFunctionBody) { |
| 306 declarationSource += ';'; | 309 declarationSource += ';'; |
| 307 } | 310 } |
| 308 } | 311 } |
| 309 // expression | 312 // expression |
| 310 if (_selectionExpression != null) { | 313 if (_selectionExpression != null) { |
| 311 // add return type | 314 // add return type |
| 312 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); | 315 if (returnType.isNotEmpty) { |
| 313 // TODO(scheglov) use librariesToImport | 316 annotations += '$returnType '; |
| 314 String returnTypeName = | |
| 315 utils.getExpressionTypeSource(_selectionExpression, librariesToImp
ort); | |
| 316 if (returnTypeName != null && returnTypeName != 'dynamic') { | |
| 317 annotations += '${returnTypeName} '; | |
| 318 } | 317 } |
| 319 // just return expression | 318 // just return expression |
| 320 declarationSource = | 319 declarationSource = |
| 321 '${annotations}${signature} => ${returnExpressionSource};'; | 320 '${annotations}${signature} => ${returnExpressionSource};'; |
| 322 } | 321 } |
| 323 // statements | 322 // statements |
| 324 if (_selectionStatements != null) { | 323 if (_selectionStatements != null) { |
| 325 if (returnType.isNotEmpty) { | 324 if (returnType.isNotEmpty) { |
| 326 annotations += returnType + ' '; | 325 annotations += returnType + ' '; |
| 327 } | 326 } |
| 328 declarationSource = '${annotations}${signature} {${eol}'; | 327 declarationSource = '${annotations}${signature} {${eol}'; |
| 329 declarationSource += returnExpressionSource; | 328 declarationSource += returnExpressionSource; |
| 330 if (_returnVariableName != null) { | 329 if (_returnVariableName != null) { |
| 331 declarationSource += | 330 declarationSource += |
| 332 '${prefix} return ${_returnVariableName};$eol'; | 331 '${prefix} return ${_returnVariableName};$eol'; |
| 333 } | 332 } |
| 334 declarationSource += '${prefix}}'; | 333 declarationSource += '${prefix}}'; |
| 335 } | 334 } |
| 336 } | 335 } |
| 337 // insert declaration | 336 // insert declaration |
| 338 if (declarationSource != null) { | 337 if (declarationSource != null) { |
| 339 int offset = _parentMember.end; | 338 int offset = _parentMember.end; |
| 340 SourceEdit edit = | 339 SourceEdit edit = |
| 341 new SourceEdit(offset, 0, '${eol}${eol}${prefix}${declarationSource}
'); | 340 new SourceEdit(offset, 0, '${eol}${eol}${prefix}${declarationSource}
'); |
| 342 doSourceChange_addElementEdit(change, unitElement, edit); | 341 doSourceChange_addElementEdit(change, unitElement, edit); |
| 343 } | 342 } |
| 344 } | 343 } |
| 345 // done | 344 // done |
| 346 return new Future.value(change); | 345 addLibraryImports(change, libraryElement, librariesToImport); |
| 346 return change; |
| 347 } | 347 } |
| 348 | 348 |
| 349 @override | 349 @override |
| 350 bool requiresPreview() => false; | 350 bool requiresPreview() => false; |
| 351 | 351 |
| 352 /** | 352 /** |
| 353 * Adds a new reference to the parameter with the given name. | 353 * Adds a new reference to the parameter with the given name. |
| 354 */ | 354 */ |
| 355 void _addParameterReference(String name, SourceRange range) { | 355 void _addParameterReference(String name, SourceRange range) { |
| 356 List<SourceRange> references = _parameterReferencesMap[name]; | 356 List<SourceRange> references = _parameterReferencesMap[name]; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 377 format("'{0}' is already used as a name in the selected code", param
eter.name)); | 377 format("'{0}' is already used as a name in the selected code", param
eter.name)); |
| 378 return result; | 378 return result; |
| 379 } | 379 } |
| 380 } | 380 } |
| 381 return result; | 381 return result; |
| 382 } | 382 } |
| 383 | 383 |
| 384 /** | 384 /** |
| 385 * Checks if created method will shadow or will be shadowed by other elements. | 385 * Checks if created method will shadow or will be shadowed by other elements. |
| 386 */ | 386 */ |
| 387 Future<RefactoringStatus> _checkPossibleConflicts() { | 387 Future<RefactoringStatus> _checkPossibleConflicts() async { |
| 388 RefactoringStatus result = new RefactoringStatus(); | 388 RefactoringStatus result = new RefactoringStatus(); |
| 389 AstNode parent = _parentMember.parent; | 389 AstNode parent = _parentMember.parent; |
| 390 // top-level function | 390 // top-level function |
| 391 if (parent is CompilationUnit) { | 391 if (parent is CompilationUnit) { |
| 392 LibraryElement libraryElement = parent.element.library; | 392 LibraryElement libraryElement = parent.element.library; |
| 393 return validateCreateFunction(searchEngine, libraryElement, name); | 393 return validateCreateFunction(searchEngine, libraryElement, name); |
| 394 } | 394 } |
| 395 // method of class | 395 // method of class |
| 396 if (parent is ClassDeclaration) { | 396 if (parent is ClassDeclaration) { |
| 397 ClassElement classElement = parent.element; | 397 ClassElement classElement = parent.element; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 String originalSource = utils.getText(range.offset, range.length); | 505 String originalSource = utils.getText(range.offset, range.length); |
| 506 _SourcePattern pattern = new _SourcePattern(); | 506 _SourcePattern pattern = new _SourcePattern(); |
| 507 List<SourceEdit> replaceEdits = <SourceEdit>[]; | 507 List<SourceEdit> replaceEdits = <SourceEdit>[]; |
| 508 unit.accept(new _GetSourcePatternVisitor(range, pattern, replaceEdits)); | 508 unit.accept(new _GetSourcePatternVisitor(range, pattern, replaceEdits)); |
| 509 replaceEdits = replaceEdits.reversed.toList(); | 509 replaceEdits = replaceEdits.reversed.toList(); |
| 510 String source = SourceEdit.applySequence(originalSource, replaceEdits); | 510 String source = SourceEdit.applySequence(originalSource, replaceEdits); |
| 511 pattern.normalizedSource = _getNormalizedSource(source); | 511 pattern.normalizedSource = _getNormalizedSource(source); |
| 512 return pattern; | 512 return pattern; |
| 513 } | 513 } |
| 514 | 514 |
| 515 String _getTypeCode(DartType type) { |
| 516 return utils.getTypeSource(type, librariesToImport); |
| 517 } |
| 518 |
| 515 /** | 519 /** |
| 516 * Initializes [createGetter] flag. | 520 * Initializes [createGetter] flag. |
| 517 */ | 521 */ |
| 518 void _initializeGetter() { | 522 void _initializeGetter() { |
| 519 createGetter = false; | 523 createGetter = false; |
| 520 // maybe we cannot at all | 524 // maybe we cannot at all |
| 521 if (!canCreateGetter) { | 525 if (!canCreateGetter) { |
| 522 return; | 526 return; |
| 523 } | 527 } |
| 524 // OK, just expression | 528 // OK, just expression |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 619 sb.toString().trim())); | 623 sb.toString().trim())); |
| 620 } | 624 } |
| 621 // done | 625 // done |
| 622 return result; | 626 return result; |
| 623 } | 627 } |
| 624 | 628 |
| 625 void _initializeReturnType() { | 629 void _initializeReturnType() { |
| 626 if (_returnType == null) { | 630 if (_returnType == null) { |
| 627 returnType = 'void'; | 631 returnType = 'void'; |
| 628 } else { | 632 } else { |
| 629 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); | 633 returnType = _getTypeCode(_returnType); |
| 630 // TODO(scheglov) use librariesToImport | |
| 631 returnType = utils.getTypeSource(_returnType, librariesToImport); | |
| 632 } | 634 } |
| 633 if (returnType == 'dynamic') { | 635 if (returnType == 'dynamic') { |
| 634 returnType = ''; | 636 returnType = ''; |
| 635 } | 637 } |
| 636 } | 638 } |
| 637 | 639 |
| 638 /** | 640 /** |
| 639 * Checks if the given [VariableElement] is declared in [selectionRange]. | 641 * Checks if the given [VariableElement] is declared in [selectionRange]. |
| 640 */ | 642 */ |
| 641 bool _isDeclaredInSelection(VariableElement element) { | 643 bool _isDeclaredInSelection(VariableElement element) { |
| (...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1025 return null; | 1027 return null; |
| 1026 } | 1028 } |
| 1027 // if declared outside, add parameter | 1029 // if declared outside, add parameter |
| 1028 if (!ref._isDeclaredInSelection(variableElement)) { | 1030 if (!ref._isDeclaredInSelection(variableElement)) { |
| 1029 String variableName = variableElement.displayName; | 1031 String variableName = variableElement.displayName; |
| 1030 // add parameter | 1032 // add parameter |
| 1031 RefactoringMethodParameter parameter = | 1033 RefactoringMethodParameter parameter = |
| 1032 ref._parametersMap[variableName]; | 1034 ref._parametersMap[variableName]; |
| 1033 if (parameter == null) { | 1035 if (parameter == null) { |
| 1034 DartType parameterType = node.bestType; | 1036 DartType parameterType = node.bestType; |
| 1035 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); | 1037 String parameterTypeCode = ref._getTypeCode(parameterType); |
| 1036 // TODO(scheglov) use librariesToImport | |
| 1037 String parameterTypeName = | |
| 1038 ref.utils.getTypeSource(parameterType, librariesToImport); | |
| 1039 parameter = new RefactoringMethodParameter( | 1038 parameter = new RefactoringMethodParameter( |
| 1040 RefactoringMethodParameterKind.REQUIRED, | 1039 RefactoringMethodParameterKind.REQUIRED, |
| 1041 parameterTypeName, | 1040 parameterTypeCode, |
| 1042 variableName, | 1041 variableName, |
| 1043 id: variableName); | 1042 id: variableName); |
| 1044 ref._parameters.add(parameter); | 1043 ref._parameters.add(parameter); |
| 1045 ref._parametersMap[variableName] = parameter; | 1044 ref._parametersMap[variableName] = parameter; |
| 1046 } | 1045 } |
| 1047 // add reference to parameter | 1046 // add reference to parameter |
| 1048 ref._addParameterReference(variableName, nodeRange); | 1047 ref._addParameterReference(variableName, nodeRange); |
| 1049 } | 1048 } |
| 1050 // remember, if assigned and used after selection | 1049 // remember, if assigned and used after selection |
| 1051 if (isLeftHandOfAssignment(node) && | 1050 if (isLeftHandOfAssignment(node) && |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1181 return false; | 1180 return false; |
| 1182 } | 1181 } |
| 1183 for (int i = 0; i < parameterTypes.length; i++) { | 1182 for (int i = 0; i < parameterTypes.length; i++) { |
| 1184 if (other.parameterTypes[i] != parameterTypes[i]) { | 1183 if (other.parameterTypes[i] != parameterTypes[i]) { |
| 1185 return false; | 1184 return false; |
| 1186 } | 1185 } |
| 1187 } | 1186 } |
| 1188 return true; | 1187 return true; |
| 1189 } | 1188 } |
| 1190 } | 1189 } |
| OLD | NEW |