| 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 import 'dart:async'; | |
| 6 import 'dart:core'; | |
| 7 | |
| 8 import 'package:analysis_server/src/protocol_server.dart' hide Element; | |
| 9 import 'package:analysis_server/src/services/correction/status.dart'; | |
| 10 import 'package:analysis_server/src/services/refactoring/refactoring.dart'; | |
| 11 import 'package:analysis_server/src/services/refactoring/refactoring_internal.da
rt'; | |
| 12 import 'package:analysis_server/src/services/search/search_engine.dart'; | |
| 13 import 'package:analyzer/dart/element/element.dart'; | |
| 14 import 'package:analyzer/file_system/file_system.dart'; | |
| 15 import 'package:analyzer/src/generated/engine.dart'; | |
| 16 import 'package:analyzer/src/generated/source.dart'; | |
| 17 import 'package:path/path.dart' as pathos; | |
| 18 import 'package:source_span/src/span.dart'; | |
| 19 import 'package:yaml/yaml.dart'; | |
| 20 | |
| 21 /** | |
| 22 * [ExtractLocalRefactoring] implementation. | |
| 23 */ | |
| 24 class MoveFileRefactoringImpl extends RefactoringImpl | |
| 25 implements MoveFileRefactoring { | |
| 26 final ResourceProvider resourceProvider; | |
| 27 final pathos.Context pathContext; | |
| 28 final SearchEngine searchEngine; | |
| 29 final AnalysisContext context; | |
| 30 final Source source; | |
| 31 | |
| 32 String oldFile; | |
| 33 String newFile; | |
| 34 | |
| 35 SourceChange change; | |
| 36 LibraryElement library; | |
| 37 String oldLibraryDir; | |
| 38 String newLibraryDir; | |
| 39 | |
| 40 MoveFileRefactoringImpl(ResourceProvider resourceProvider, this.searchEngine, | |
| 41 this.context, this.source, this.oldFile) | |
| 42 : resourceProvider = resourceProvider, | |
| 43 pathContext = resourceProvider.pathContext { | |
| 44 if (source != null) { | |
| 45 oldFile = source.fullName; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 @override | |
| 50 String get refactoringName => 'Move File'; | |
| 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 return new Future.value(result); | |
| 62 } | |
| 63 | |
| 64 @override | |
| 65 Future<SourceChange> createChange() async { | |
| 66 // move file | |
| 67 if (source != null) { | |
| 68 return _createFileChange(); | |
| 69 } | |
| 70 // rename project | |
| 71 if (oldFile != null) { | |
| 72 Resource projectFolder = resourceProvider.getResource(oldFile); | |
| 73 if (projectFolder is Folder && projectFolder.exists) { | |
| 74 Resource pubspecFile = projectFolder.getChild('pubspec.yaml'); | |
| 75 if (pubspecFile is File && pubspecFile.exists) { | |
| 76 return _createProjectChange(projectFolder, pubspecFile); | |
| 77 } | |
| 78 } | |
| 79 } | |
| 80 // no change | |
| 81 return null; | |
| 82 } | |
| 83 | |
| 84 @override | |
| 85 bool requiresPreview() => false; | |
| 86 | |
| 87 /** | |
| 88 * Computes the URI to use to reference [newFile] from [reference]. | |
| 89 */ | |
| 90 String _computeNewUri(SourceReference reference) { | |
| 91 String refDir = pathContext.dirname(reference.file); | |
| 92 // try to keep package: URI | |
| 93 if (_isPackageReference(reference)) { | |
| 94 Source newSource = new NonExistingSource( | |
| 95 newFile, pathos.toUri(newFile), UriKind.FILE_URI); | |
| 96 Uri restoredUri = context.sourceFactory.restoreUri(newSource); | |
| 97 if (restoredUri != null) { | |
| 98 return restoredUri.toString(); | |
| 99 } | |
| 100 } | |
| 101 // if no package: URI, prepare relative | |
| 102 return _getRelativeUri(newFile, refDir); | |
| 103 } | |
| 104 | |
| 105 Future<SourceChange> _createFileChange() async { | |
| 106 change = new SourceChange('Update File References'); | |
| 107 List<Source> librarySources = context.getLibrariesContaining(source); | |
| 108 await Future.forEach(librarySources, (Source librarySource) async { | |
| 109 CompilationUnitElement unitElement = | |
| 110 context.getCompilationUnitElement(source, librarySource); | |
| 111 if (unitElement != null) { | |
| 112 // if a defining unit, update outgoing references | |
| 113 library = unitElement.library; | |
| 114 if (library.definingCompilationUnit == unitElement) { | |
| 115 oldLibraryDir = pathContext.dirname(oldFile); | |
| 116 newLibraryDir = pathContext.dirname(newFile); | |
| 117 _updateUriReferences(library.imports); | |
| 118 _updateUriReferences(library.exports); | |
| 119 _updateUriReferences(library.parts); | |
| 120 } | |
| 121 // update reference to the unit | |
| 122 List<SearchMatch> matches = | |
| 123 await searchEngine.searchReferences(unitElement); | |
| 124 List<SourceReference> references = getSourceReferences(matches); | |
| 125 for (SourceReference reference in references) { | |
| 126 String newUri = _computeNewUri(reference); | |
| 127 reference.addEdit(change, "'$newUri'"); | |
| 128 } | |
| 129 } | |
| 130 }); | |
| 131 return change; | |
| 132 } | |
| 133 | |
| 134 Future<SourceChange> _createProjectChange( | |
| 135 Folder project, File pubspecFile) async { | |
| 136 change = new SourceChange('Rename project'); | |
| 137 String oldPackageName = pathContext.basename(oldFile); | |
| 138 String newPackageName = pathContext.basename(newFile); | |
| 139 // add pubspec.yaml change | |
| 140 { | |
| 141 // prepare "name" field value location | |
| 142 SourceSpan nameSpan; | |
| 143 { | |
| 144 String pubspecString = pubspecFile.readAsStringSync(); | |
| 145 YamlMap pubspecNode = loadYamlNode(pubspecString); | |
| 146 YamlNode nameNode = pubspecNode.nodes['name']; | |
| 147 nameSpan = nameNode.span; | |
| 148 } | |
| 149 int nameOffset = nameSpan.start.offset; | |
| 150 int nameLength = nameSpan.length; | |
| 151 // add edit | |
| 152 change.addEdit(pubspecFile.path, pubspecFile.modificationStamp, | |
| 153 new SourceEdit(nameOffset, nameLength, newPackageName)); | |
| 154 } | |
| 155 // check all local libraries | |
| 156 for (Source librarySource in context.librarySources) { | |
| 157 // should be a local library | |
| 158 if (!project.contains(librarySource.fullName)) { | |
| 159 continue; | |
| 160 } | |
| 161 // we need LibraryElement | |
| 162 LibraryElement library = context.getLibraryElement(librarySource); | |
| 163 if (library == null) { | |
| 164 continue; | |
| 165 } | |
| 166 // update all imports | |
| 167 updateUriElements(List<UriReferencedElement> uriElements) { | |
| 168 for (UriReferencedElement element in uriElements) { | |
| 169 String uri = element.uri; | |
| 170 if (uri != null) { | |
| 171 String oldPrefix = 'package:$oldPackageName/'; | |
| 172 if (uri.startsWith(oldPrefix)) { | |
| 173 doSourceChange_addElementEdit( | |
| 174 change, | |
| 175 library, | |
| 176 new SourceEdit(element.uriOffset + 1, oldPrefix.length, | |
| 177 'package:$newPackageName/')); | |
| 178 } | |
| 179 } | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 updateUriElements(library.imports); | |
| 184 updateUriElements(library.exports); | |
| 185 } | |
| 186 // done | |
| 187 return change; | |
| 188 } | |
| 189 | |
| 190 String _getRelativeUri(String path, String from) { | |
| 191 String uri = pathContext.relative(path, from: from); | |
| 192 List<String> parts = pathContext.split(uri); | |
| 193 return pathos.posix.joinAll(parts); | |
| 194 } | |
| 195 | |
| 196 bool _isPackageReference(SourceReference reference) { | |
| 197 Source source = reference.element.source; | |
| 198 int offset = reference.range.offset + "'".length; | |
| 199 String content = context.getContents(source).data; | |
| 200 return content.startsWith('package:', offset); | |
| 201 } | |
| 202 | |
| 203 /** | |
| 204 * Checks if the given [path] represents a relative URI. | |
| 205 * | |
| 206 * The following URI's are not relative: | |
| 207 * `/absolute/path/file.dart` | |
| 208 * `dart:math` | |
| 209 */ | |
| 210 bool _isRelativeUri(String path) { | |
| 211 // absolute URI | |
| 212 if (Uri.parse(path).isAbsolute) { | |
| 213 return false; | |
| 214 } | |
| 215 // absolute path | |
| 216 if (pathContext.isAbsolute(path)) { | |
| 217 return false; | |
| 218 } | |
| 219 // OK | |
| 220 return true; | |
| 221 } | |
| 222 | |
| 223 void _updateUriReference(UriReferencedElement element) { | |
| 224 if (!element.isSynthetic) { | |
| 225 String elementUri = element.uri; | |
| 226 if (_isRelativeUri(elementUri)) { | |
| 227 String elementPath = pathContext.join(oldLibraryDir, elementUri); | |
| 228 String newUri = _getRelativeUri(elementPath, newLibraryDir); | |
| 229 int uriOffset = element.uriOffset; | |
| 230 int uriLength = element.uriEnd - uriOffset; | |
| 231 doSourceChange_addElementEdit( | |
| 232 change, library, new SourceEdit(uriOffset, uriLength, "'$newUri'")); | |
| 233 } | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 void _updateUriReferences(List<UriReferencedElement> elements) { | |
| 238 for (UriReferencedElement element in elements) { | |
| 239 _updateUriReference(element); | |
| 240 } | |
| 241 } | |
| 242 } | |
| OLD | NEW |