| 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 // This code was auto-generated, is not intended to be edited, and is subject to | 5 // This code was auto-generated, is not intended to be edited, and is subject to |
| 6 // significant change. Please see the README file for more information. | 6 // significant change. Please see the README file for more information. |
| 7 | 7 |
| 8 library services.src.refactoring.rename; | 8 library services.src.refactoring.rename; |
| 9 | 9 |
| 10 import 'dart:async'; |
| 11 import 'dart:collection'; |
| 12 |
| 13 import 'package:analysis_services/correction/change.dart'; |
| 10 import 'package:analysis_services/correction/status.dart'; | 14 import 'package:analysis_services/correction/status.dart'; |
| 11 import 'package:analysis_services/refactoring/refactoring.dart'; | 15 import 'package:analysis_services/refactoring/refactoring.dart'; |
| 12 import 'package:analysis_services/search/search_engine.dart'; | 16 import 'package:analysis_services/search/search_engine.dart'; |
| 13 import 'package:analysis_services/src/correction/source_range.dart'; | 17 import 'package:analysis_services/src/correction/source_range.dart'; |
| 14 import 'package:analysis_services/src/generated/change.dart'; | |
| 15 import 'package:analysis_services/src/refactoring/refactoring.dart'; | 18 import 'package:analysis_services/src/refactoring/refactoring.dart'; |
| 16 import 'package:analyzer/src/generated/element.dart'; | 19 import 'package:analyzer/src/generated/element.dart'; |
| 17 import 'package:analyzer/src/generated/engine.dart'; | 20 import 'package:analyzer/src/generated/engine.dart'; |
| 18 import 'package:analyzer/src/generated/source.dart'; | 21 import 'package:analyzer/src/generated/source.dart'; |
| 19 | 22 |
| 20 | 23 |
| 21 /** | 24 /** |
| 22 * Returns the [Edit] to replace the given [SearchMatch] reference. | 25 * Returns the [Edit] to replace the given [SearchMatch] reference. |
| 23 */ | 26 */ |
| 24 Edit createReferenceEdit(SourceReference reference, String newText) { | 27 Edit createReferenceEdit(SourceReference reference, String newText) { |
| 25 return new Edit.range(reference.range, newText); | 28 return new Edit.range(reference.range, newText); |
| 26 } | 29 } |
| 27 | 30 |
| 28 | 31 |
| 29 /** | 32 /** |
| 33 * Returns the file containing declaration of the given [Element]. |
| 34 */ |
| 35 String getElementFile(Element element) { |
| 36 return element.source.fullName; |
| 37 } |
| 38 |
| 39 |
| 40 /** |
| 41 * When a [Source] (a file) is used in more than one context, [SearchEngine] |
| 42 * will return separate [SearchMatch]s for each context. But in rename |
| 43 * refactorings we want to update each [Source] only once. |
| 44 */ |
| 45 List<SourceReference> getSourceReferences(List<SearchMatch> matches) { |
| 46 var uniqueReferences = new HashMap<SourceReference, SourceReference>(); |
| 47 for (SearchMatch match in matches) { |
| 48 Element element = match.element; |
| 49 MatchKind kind = match.kind; |
| 50 String file = getElementFile(element); |
| 51 SourceRange range = match.sourceRange; |
| 52 SourceReference newReference = new SourceReference(kind, file, range); |
| 53 SourceReference oldReference = uniqueReferences[newReference]; |
| 54 if (oldReference == null) { |
| 55 uniqueReferences[newReference] = newReference; |
| 56 oldReference = newReference; |
| 57 } |
| 58 } |
| 59 return uniqueReferences.keys.toList(); |
| 60 } |
| 61 |
| 62 |
| 63 /** |
| 30 * Returns `true` if two given [Element]s are [LocalElement]s and have | 64 * Returns `true` if two given [Element]s are [LocalElement]s and have |
| 31 * intersecting with visibility ranges. | 65 * intersecting with visibility ranges. |
| 32 */ | 66 */ |
| 33 bool haveIntersectingRanges(LocalElement localElement, Element element) { | 67 bool haveIntersectingRanges(LocalElement localElement, Element element) { |
| 34 if (element is! LocalElement) { | 68 if (element is! LocalElement) { |
| 35 return false; | 69 return false; |
| 36 } | 70 } |
| 37 LocalElement localElement2 = element as LocalElement; | 71 LocalElement localElement2 = element as LocalElement; |
| 38 Source localSource = localElement.source; | 72 Source localSource = localElement.source; |
| 39 Source localSource2 = localElement2.source; | 73 Source localSource2 = localElement2.source; |
| 40 SourceRange localRange = localElement.visibleRange; | 74 SourceRange localRange = localElement.visibleRange; |
| 41 SourceRange localRange2 = localElement2.visibleRange; | 75 SourceRange localRange2 = localElement2.visibleRange; |
| 42 return localSource2 == localSource && | 76 return localSource2 == localSource && |
| 43 localRange != null && | 77 localRange != null && |
| 44 localRange2 != null && | 78 localRange2 != null && |
| 45 localRange2.intersects(localRange); | 79 localRange2.intersects(localRange); |
| 46 } | 80 } |
| 47 | 81 |
| 48 | 82 |
| 49 /** | 83 /** |
| 84 * Checks if [element] is defined in the library containing [source]. |
| 85 */ |
| 86 bool isDefinedInLibrary(Element element, AnalysisContext context, Source source) |
| 87 { |
| 88 // should be the same AnalysisContext |
| 89 if (!isInContext(element, context)) { |
| 90 return false; |
| 91 } |
| 92 // private elements are visible only in their library |
| 93 List<Source> librarySourcesOfSource = context.getLibrariesContaining(source); |
| 94 Source librarySourceOfElement = element.library.source; |
| 95 return librarySourcesOfSource.contains(librarySourceOfElement); |
| 96 } |
| 97 |
| 98 |
| 99 /** |
| 50 * Checks if the given [Element] is in the given [AnalysisContext]. | 100 * Checks if the given [Element] is in the given [AnalysisContext]. |
| 51 */ | 101 */ |
| 52 bool isInContext(Element element, AnalysisContext context) { | 102 bool isInContext(Element element, AnalysisContext context) { |
| 53 AnalysisContext elementContext = element.context; | 103 AnalysisContext elementContext = element.context; |
| 54 if (elementContext == context) { | 104 if (elementContext == context) { |
| 55 return true; | 105 return true; |
| 56 } | 106 } |
| 57 if (context is InstrumentedAnalysisContextImpl) { | 107 if (context is InstrumentedAnalysisContextImpl) { |
| 58 return elementContext == context.basis; | 108 return elementContext == context.basis; |
| 59 } | 109 } |
| 60 return false; | 110 return false; |
| 61 } | 111 } |
| 62 | 112 |
| 63 | 113 |
| 64 /** | 114 /** |
| 65 * Checks if [element] is defined in the library containing [source]. | 115 * Checks if the given unqualified [SearchMatch] intersects with visibility |
| 116 * range of [localElement]. |
| 66 */ | 117 */ |
| 67 bool isDefinedInLibrary(Element element, AnalysisContext context, Source source) | 118 bool isReferenceInLocalRange(LocalElement localElement, SearchMatch reference) { |
| 68 { | 119 if (reference.isQualified) { |
| 69 // should be the same AnalysisContext | |
| 70 if (!isInContext(element, context)) { | |
| 71 return false; | 120 return false; |
| 72 } | 121 } |
| 73 // private elements are visible only in their library | 122 Source localSource = localElement.source; |
| 74 List<Source> librarySourcesOfSource = context.getLibrariesContaining(source); | 123 Source referenceSource = reference.element.source; |
| 75 Source librarySourceOfElement = element.library.source; | 124 SourceRange localRange = localElement.visibleRange; |
| 76 return librarySourcesOfSource.contains(librarySourceOfElement); | 125 SourceRange referenceRange = reference.sourceRange; |
| 126 return referenceSource == localSource && |
| 127 referenceRange.intersects(localRange); |
| 77 } | 128 } |
| 78 | 129 |
| 79 | 130 |
| 80 /** | 131 /** |
| 81 * Checks if [element] is visible in the library containing [source]. | 132 * Checks if [element] is visible in the library containing [source]. |
| 82 */ | 133 */ |
| 83 bool isVisibleInLibrary(Element element, AnalysisContext context, Source source) | 134 bool isVisibleInLibrary(Element element, AnalysisContext context, Source source) |
| 84 { | 135 { |
| 85 // should be the same AnalysisContext | 136 // should be the same AnalysisContext |
| 86 if (!isInContext(element, context)) { | 137 if (!isInContext(element, context)) { |
| 87 return false; | 138 return false; |
| 88 } | 139 } |
| 89 // public elements are always visible | 140 // public elements are always visible |
| 90 if (element.isPublic) { | 141 if (element.isPublic) { |
| 91 return true; | 142 return true; |
| 92 } | 143 } |
| 93 // private elements are visible only in their library | 144 // private elements are visible only in their library |
| 94 return isDefinedInLibrary(element, context, source); | 145 return isDefinedInLibrary(element, context, source); |
| 95 } | 146 } |
| 96 | 147 |
| 97 | 148 |
| 98 /** | |
| 99 * Checks if the given unqualified [SearchMatch] intersects with visibility | |
| 100 * range of [localElement]. | |
| 101 */ | |
| 102 bool isReferenceInLocalRange(LocalElement localElement, SearchMatch reference) { | |
| 103 if (reference.isQualified) { | |
| 104 return false; | |
| 105 } | |
| 106 Source localSource = localElement.source; | |
| 107 Source referenceSource = reference.element.source; | |
| 108 SourceRange localRange = localElement.visibleRange; | |
| 109 SourceRange referenceRange = reference.sourceRange; | |
| 110 return referenceSource == localSource && | |
| 111 referenceRange.intersects(localRange); | |
| 112 } | |
| 113 | |
| 114 | 149 |
| 115 /** | 150 /** |
| 116 * An abstract implementation of [RenameRefactoring]. | 151 * An abstract implementation of [RenameRefactoring]. |
| 117 */ | 152 */ |
| 118 abstract class RenameRefactoringImpl extends RefactoringImpl implements | 153 abstract class RenameRefactoringImpl extends RefactoringImpl implements |
| 119 RenameRefactoring { | 154 RenameRefactoring { |
| 120 final SearchEngine searchEngine; | 155 final SearchEngine searchEngine; |
| 121 final Element element; | 156 final Element element; |
| 122 final AnalysisContext context; | 157 final AnalysisContext context; |
| 123 final String oldName; | 158 final String oldName; |
| 124 | 159 |
| 125 String newName; | 160 String newName; |
| 126 | 161 |
| 127 RenameRefactoringImpl(SearchEngine searchEngine, Element element) | 162 RenameRefactoringImpl(SearchEngine searchEngine, Element element) |
| 128 : searchEngine = searchEngine, | 163 : searchEngine = searchEngine, |
| 129 element = element, | 164 element = element, |
| 130 context = element.context, | 165 context = element.context, |
| 131 oldName = _getDisplayName(element); | 166 oldName = _getDisplayName(element); |
| 132 | 167 |
| 133 /** | 168 /** |
| 134 * Adds the "Update declaration" [Edit] to [sourceChange]. | 169 * Adds the "Update declaration" [Edit] to [change]. |
| 135 */ | 170 */ |
| 136 void addDeclarationEdit(SourceChange sourceChange, Element element) { | 171 void addDeclarationEdit(Change change, Element element) { |
| 172 String file = getElementFile(element); |
| 137 Edit edit = new Edit.range(rangeElementName(element), newName); | 173 Edit edit = new Edit.range(rangeElementName(element), newName); |
| 138 sourceChange.addEdit(edit, "Update declaration"); | 174 change.addEdit(file, edit); |
| 139 } | 175 } |
| 140 | 176 |
| 141 /** | 177 /** |
| 142 * Adds an "Update reference" [Edit] to [sourceChange]. | 178 * Adds an "Update reference" [Edit] to [change]. |
| 143 */ | 179 */ |
| 144 void addReferenceEdit(SourceChange sourceChange, SourceReference reference) { | 180 void addReferenceEdit(Change change, SourceReference reference) { |
| 145 Edit edit = createReferenceEdit(reference, newName); | 181 Edit edit = createReferenceEdit(reference, newName); |
| 146 sourceChange.addEdit(edit, "Update reference"); | 182 change.addEdit(reference.file, edit); |
| 147 } | 183 } |
| 148 | 184 |
| 149 @override | 185 @override |
| 150 RefactoringStatus checkInitialConditions() { | 186 Future<RefactoringStatus> checkInitialConditions() { |
| 151 return new RefactoringStatus(); | 187 var result = new RefactoringStatus(); |
| 188 return new Future.value(result); |
| 152 } | 189 } |
| 153 | 190 |
| 154 @override | 191 @override |
| 155 RefactoringStatus checkNewName() { | 192 RefactoringStatus checkNewName() { |
| 156 RefactoringStatus result = new RefactoringStatus(); | 193 RefactoringStatus result = new RefactoringStatus(); |
| 157 if (newName == oldName) { | 194 if (newName == oldName) { |
| 158 result.addFatalError( | 195 result.addFatalError( |
| 159 "The new name must be different than the current name."); | 196 "The new name must be different than the current name."); |
| 160 } | 197 } |
| 161 return result; | 198 return result; |
| 162 } | 199 } |
| 163 | 200 |
| 201 @override |
| 202 bool requiresPreview() { |
| 203 return false; |
| 204 } |
| 205 |
| 164 static String _getDisplayName(Element element) { | 206 static String _getDisplayName(Element element) { |
| 165 if (element is ImportElement) { | 207 if (element is ImportElement) { |
| 166 PrefixElement prefix = element.prefix; | 208 PrefixElement prefix = element.prefix; |
| 167 if (prefix != null) { | 209 if (prefix != null) { |
| 168 return prefix.displayName; | 210 return prefix.displayName; |
| 169 } | 211 } |
| 170 } | 212 } |
| 171 return element.displayName; | 213 return element.displayName; |
| 172 } | 214 } |
| 173 } | 215 } |
| 174 | 216 |
| 175 | 217 |
| 176 /** | 218 /** |
| 177 * The [SourceRange] in some [Source]. | 219 * The [SourceRange] in some [Source]. |
| 178 */ | 220 */ |
| 179 class SourceReference { | 221 class SourceReference { |
| 180 final MatchKind kind; | 222 final MatchKind kind; |
| 181 final Source source; | 223 final String file; |
| 182 final SourceRange range; | 224 final SourceRange range; |
| 183 | 225 |
| 184 SourceReference(this.kind, this.source, this.range); | 226 SourceReference(this.kind, this.file, this.range); |
| 185 | 227 |
| 186 @override | 228 @override |
| 187 int get hashCode { | 229 int get hashCode { |
| 188 int hash = source.hashCode; | 230 int hash = file.hashCode; |
| 189 hash = ((hash << 16) & 0xFFFFFFFF) + range.hashCode; | 231 hash = ((hash << 16) & 0xFFFFFFFF) + range.hashCode; |
| 190 return hash; | 232 return hash; |
| 191 } | 233 } |
| 192 | 234 |
| 193 @override | 235 @override |
| 194 bool operator ==(Object obj) { | 236 bool operator ==(Object other) { |
| 195 if (identical(obj, this)) { | 237 if (identical(other, this)) { |
| 196 return true; | 238 return true; |
| 197 } | 239 } |
| 198 if (obj is! SourceReference) { | 240 if (other is SourceReference) { |
| 199 return false; | 241 return other.file == file && other.range == range; |
| 200 } | 242 } |
| 201 SourceReference other = obj as SourceReference; | 243 return false; |
| 202 return other.source == source && other.range == range; | |
| 203 } | 244 } |
| 204 | 245 |
| 205 @override | 246 @override |
| 206 String toString() => '${source}@${range}'; | 247 String toString() => '${file}@${range}'; |
| 207 } | 248 } |
| OLD | NEW |