OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017, 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 library analyzer.src.generated.gn; |
| 6 |
| 7 import 'dart:collection'; |
| 8 import 'dart:core'; |
| 9 |
| 10 import 'package:analyzer/file_system/file_system.dart'; |
| 11 import 'package:analyzer/src/generated/source.dart'; |
| 12 import 'package:analyzer/src/generated/source_io.dart'; |
| 13 import 'package:analyzer/src/util/fast_uri.dart'; |
| 14 import 'package:path/path.dart'; |
| 15 |
| 16 /** |
| 17 * The [UriResolver] used to resolve `file` URIs in a [GnWorkspace]. |
| 18 */ |
| 19 class GnFileUriResolver extends ResourceUriResolver { |
| 20 /** |
| 21 * The workspace associated with this resolver. |
| 22 */ |
| 23 final GnWorkspace workspace; |
| 24 |
| 25 /** |
| 26 * Initialize a newly created resolver to be associated with the given |
| 27 * [workspace]. |
| 28 */ |
| 29 GnFileUriResolver(GnWorkspace workspace) |
| 30 : workspace = workspace, |
| 31 super(workspace.provider); |
| 32 |
| 33 @override |
| 34 Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
| 35 if (!ResourceUriResolver.isFileUri(uri)) { |
| 36 return null; |
| 37 } |
| 38 String path = provider.pathContext.fromUri(uri); |
| 39 File file = workspace.findFile(path); |
| 40 if (file != null) { |
| 41 return file.createSource(actualUri ?? uri); |
| 42 } |
| 43 return null; |
| 44 } |
| 45 } |
| 46 |
| 47 /** |
| 48 * The [UriResolver] used to resolve `package` URIs in a [GnWorkspace]. |
| 49 */ |
| 50 class GnPackageUriResolver extends UriResolver { |
| 51 /** |
| 52 * The workspace associated with this resolver. |
| 53 */ |
| 54 final GnWorkspace workspace; |
| 55 |
| 56 /** |
| 57 * The path context that should be used to manipulate file system paths. |
| 58 */ |
| 59 final Context pathContext; |
| 60 |
| 61 /** |
| 62 * The map of package sources indexed by package name. |
| 63 */ |
| 64 final Map<String, String> _packages; |
| 65 |
| 66 /** |
| 67 * The cache of absolute [Uri]s to [Source]s mappings. |
| 68 */ |
| 69 final Map<Uri, Source> _sourceCache = new HashMap<Uri, Source>(); |
| 70 |
| 71 /** |
| 72 * Initialize a newly created resolver to be associated with the given |
| 73 * [workspace]. |
| 74 */ |
| 75 GnPackageUriResolver(GnWorkspace workspace) |
| 76 : workspace = workspace, |
| 77 pathContext = workspace.provider.pathContext, |
| 78 _packages = workspace.packages; |
| 79 |
| 80 @override |
| 81 Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
| 82 return _sourceCache.putIfAbsent(uri, () { |
| 83 if (uri.scheme != 'package') { |
| 84 return null; |
| 85 } |
| 86 |
| 87 String uriPath = uri.path; |
| 88 int slash = uriPath.indexOf('/'); |
| 89 |
| 90 // If the path either starts with a slash or has no slash, it is invalid. |
| 91 if (slash < 1) { |
| 92 return null; |
| 93 } |
| 94 |
| 95 String packageName = uriPath.substring(0, slash); |
| 96 String fileUriPart = uriPath.substring(slash + 1); |
| 97 String filePath = fileUriPart.replaceAll('/', pathContext.separator); |
| 98 |
| 99 if (!_packages.containsKey(packageName)) { |
| 100 return null; |
| 101 } |
| 102 String packageBase = _packages[packageName]; |
| 103 String path = pathContext.join(packageBase, filePath); |
| 104 File file = workspace.findFile(path); |
| 105 return file?.createSource(uri); |
| 106 }); |
| 107 } |
| 108 |
| 109 @override |
| 110 Uri restoreAbsolute(Source source) { |
| 111 Context context = workspace.provider.pathContext; |
| 112 String path = source.fullName; |
| 113 |
| 114 if (!context.isWithin(workspace.root, path)) { |
| 115 return null; |
| 116 } |
| 117 |
| 118 String package = _packages.keys.firstWhere( |
| 119 (key) => context.isWithin(_packages[key], path), |
| 120 orElse: () => null); |
| 121 |
| 122 if (package == null) { |
| 123 return null; |
| 124 } |
| 125 |
| 126 String sourcePath = context.relative(path, from: _packages[package]); |
| 127 |
| 128 return FastUri.parse('package:$package/$sourcePath'); |
| 129 } |
| 130 } |
| 131 |
| 132 /** |
| 133 * Information about a Gn workspace. |
| 134 */ |
| 135 class GnWorkspace { |
| 136 /** |
| 137 * The name of the directory that identifies the root of the workspace. |
| 138 */ |
| 139 static const String _jiriRootName = '.jiri_root'; |
| 140 |
| 141 /** |
| 142 * The resource provider used to access the file system. |
| 143 */ |
| 144 final ResourceProvider provider; |
| 145 |
| 146 /** |
| 147 * The absolute workspace root path (the directory containing the `.jiri_root` |
| 148 * directory). |
| 149 */ |
| 150 final String root; |
| 151 |
| 152 /** |
| 153 * The map of package sources indexed by package name. |
| 154 */ |
| 155 final Map<String, String> packages; |
| 156 |
| 157 GnWorkspace._(this.provider, this.root, this.packages); |
| 158 |
| 159 /** |
| 160 * Return a map of package sources. |
| 161 */ |
| 162 Map<String, List<Folder>> get packageMap { |
| 163 Map<String, List<Folder>> result = new HashMap<String, List<Folder>>(); |
| 164 packages.forEach((package, sourceDir) { |
| 165 result[package] = [provider.getFolder(sourceDir)]; |
| 166 }); |
| 167 return result; |
| 168 } |
| 169 |
| 170 /** |
| 171 * Return the file with the given [absolutePath]. |
| 172 * |
| 173 * Return `null` if the given [absolutePath] is not in the workspace [root]. |
| 174 */ |
| 175 File findFile(String absolutePath) { |
| 176 try { |
| 177 File writableFile = provider.getFile(absolutePath); |
| 178 return writableFile; |
| 179 } catch (_) { |
| 180 return null; |
| 181 } |
| 182 } |
| 183 |
| 184 /** |
| 185 * Locate the output directory. |
| 186 * |
| 187 * Return `null` if it could not be found. |
| 188 */ |
| 189 static String _getOutDirectory(ResourceProvider provider, String root) => |
| 190 provider |
| 191 .getFolder('$root/out') |
| 192 .getChildren() |
| 193 .where((resource) => resource is Folder) |
| 194 .map((resource) => resource as Folder) |
| 195 .firstWhere((Folder folder) { |
| 196 String baseName = basename(folder.path); |
| 197 // TODO(pylaligand): find a better way to locate the proper directory. |
| 198 return baseName.startsWith('debug') || baseName.startsWith('release'); |
| 199 }, orElse: () => null)?.path; |
| 200 |
| 201 /** |
| 202 * Return a map of package source locations indexed by package name. |
| 203 */ |
| 204 static Map<String, String> _getPackages( |
| 205 ResourceProvider provider, String outDirectory) { |
| 206 String packagesDir = '$outDirectory/gen/dart.sources'; |
| 207 Map<String, String> result = new HashMap<String, String>(); |
| 208 provider |
| 209 .getFolder(packagesDir) |
| 210 .getChildren() |
| 211 .where((resource) => resource is File) |
| 212 .map((resource) => resource as File) |
| 213 .forEach((file) { |
| 214 String packageName = basename(file.path); |
| 215 String source = file.readAsStringSync(); |
| 216 result[packageName] = source; |
| 217 }); |
| 218 return result; |
| 219 } |
| 220 |
| 221 /** |
| 222 * Find the Gn workspace that contains the given [path]. |
| 223 * |
| 224 * Return `null` if a workspace markers, such as the `.jiri_root` directory |
| 225 * cannot be found. |
| 226 */ |
| 227 static GnWorkspace find(ResourceProvider provider, String path) { |
| 228 Context context = provider.pathContext; |
| 229 |
| 230 // Ensure that the path is absolute and normalized. |
| 231 if (!context.isAbsolute(path)) { |
| 232 throw new ArgumentError('not absolute: $path'); |
| 233 } |
| 234 path = context.normalize(path); |
| 235 |
| 236 Folder folder = provider.getFolder(path); |
| 237 while (true) { |
| 238 Folder parent = folder.parent; |
| 239 if (parent == null) { |
| 240 return null; |
| 241 } |
| 242 |
| 243 // Found the .jiri_root file, must be a non-git workspace. |
| 244 if (folder.getChildAssumingFolder(_jiriRootName).exists) { |
| 245 String root = folder.path; |
| 246 String outDirectory = _getOutDirectory(provider, root); |
| 247 Map<String, String> packages = _getPackages(provider, outDirectory); |
| 248 return new GnWorkspace._(provider, root, packages); |
| 249 } |
| 250 |
| 251 // Go up the folder. |
| 252 folder = parent; |
| 253 } |
| 254 } |
| 255 } |
OLD | NEW |