Index: pkg/analyzer/lib/src/generated/gn.dart |
diff --git a/pkg/analyzer/lib/src/generated/gn.dart b/pkg/analyzer/lib/src/generated/gn.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ea198335cf12ffe806c4b68a50d3a5885d855d63 |
--- /dev/null |
+++ b/pkg/analyzer/lib/src/generated/gn.dart |
@@ -0,0 +1,255 @@ |
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library analyzer.src.generated.gn; |
+ |
+import 'dart:collection'; |
+import 'dart:core'; |
+ |
+import 'package:analyzer/file_system/file_system.dart'; |
+import 'package:analyzer/src/generated/source.dart'; |
+import 'package:analyzer/src/generated/source_io.dart'; |
+import 'package:analyzer/src/util/fast_uri.dart'; |
+import 'package:path/path.dart'; |
+ |
+/** |
+ * The [UriResolver] used to resolve `file` URIs in a [GnWorkspace]. |
+ */ |
+class GnFileUriResolver extends ResourceUriResolver { |
+ /** |
+ * The workspace associated with this resolver. |
+ */ |
+ final GnWorkspace workspace; |
+ |
+ /** |
+ * Initialize a newly created resolver to be associated with the given |
+ * [workspace]. |
+ */ |
+ GnFileUriResolver(GnWorkspace workspace) |
+ : workspace = workspace, |
+ super(workspace.provider); |
+ |
+ @override |
+ Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
+ if (!ResourceUriResolver.isFileUri(uri)) { |
+ return null; |
+ } |
+ String path = provider.pathContext.fromUri(uri); |
+ File file = workspace.findFile(path); |
+ if (file != null) { |
+ return file.createSource(actualUri ?? uri); |
+ } |
+ return null; |
+ } |
+} |
+ |
+/** |
+ * The [UriResolver] used to resolve `package` URIs in a [GnWorkspace]. |
+ */ |
+class GnPackageUriResolver extends UriResolver { |
+ /** |
+ * The workspace associated with this resolver. |
+ */ |
+ final GnWorkspace workspace; |
+ |
+ /** |
+ * The path context that should be used to manipulate file system paths. |
+ */ |
+ final Context pathContext; |
+ |
+ /** |
+ * The map of package sources indexed by package name. |
+ */ |
+ final Map<String, String> _packages; |
+ |
+ /** |
+ * The cache of absolute [Uri]s to [Source]s mappings. |
+ */ |
+ final Map<Uri, Source> _sourceCache = new HashMap<Uri, Source>(); |
+ |
+ /** |
+ * Initialize a newly created resolver to be associated with the given |
+ * [workspace]. |
+ */ |
+ GnPackageUriResolver(GnWorkspace workspace) |
+ : workspace = workspace, |
+ pathContext = workspace.provider.pathContext, |
+ _packages = workspace.packages; |
+ |
+ @override |
+ Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
+ return _sourceCache.putIfAbsent(uri, () { |
+ if (uri.scheme != 'package') { |
+ return null; |
+ } |
+ |
+ String uriPath = uri.path; |
+ int slash = uriPath.indexOf('/'); |
+ |
+ // If the path either starts with a slash or has no slash, it is invalid. |
+ if (slash < 1) { |
+ return null; |
+ } |
+ |
+ String packageName = uriPath.substring(0, slash); |
+ String fileUriPart = uriPath.substring(slash + 1); |
+ String filePath = fileUriPart.replaceAll('/', pathContext.separator); |
+ |
+ if (!_packages.containsKey(packageName)) { |
+ return null; |
+ } |
+ String packageBase = _packages[packageName]; |
+ String path = pathContext.join(packageBase, filePath); |
+ File file = workspace.findFile(path); |
+ return file?.createSource(uri); |
+ }); |
+ } |
+ |
+ @override |
+ Uri restoreAbsolute(Source source) { |
+ Context context = workspace.provider.pathContext; |
+ String path = source.fullName; |
+ |
+ if (!context.isWithin(workspace.root, path)) { |
+ return null; |
+ } |
+ |
+ String package = _packages.keys.firstWhere( |
+ (key) => context.isWithin(_packages[key], path), |
+ orElse: () => null); |
+ |
+ if (package == null) { |
+ return null; |
+ } |
+ |
+ String sourcePath = context.relative(path, from: _packages[package]); |
+ |
+ return FastUri.parse('package:$package/$sourcePath'); |
+ } |
+} |
+ |
+/** |
+ * Information about a Gn workspace. |
+ */ |
+class GnWorkspace { |
+ /** |
+ * The name of the directory that identifies the root of the workspace. |
+ */ |
+ static const String _jiriRootName = '.jiri_root'; |
+ |
+ /** |
+ * The resource provider used to access the file system. |
+ */ |
+ final ResourceProvider provider; |
+ |
+ /** |
+ * The absolute workspace root path (the directory containing the `.jiri_root` |
+ * directory). |
+ */ |
+ final String root; |
+ |
+ /** |
+ * The map of package sources indexed by package name. |
+ */ |
+ final Map<String, String> packages; |
+ |
+ GnWorkspace._(this.provider, this.root, this.packages); |
+ |
+ /** |
+ * Return a map of package sources. |
+ */ |
+ Map<String, List<Folder>> get packageMap { |
+ Map<String, List<Folder>> result = new HashMap<String, List<Folder>>(); |
+ packages.forEach((package, sourceDir) { |
+ result[package] = [provider.getFolder(sourceDir)]; |
+ }); |
+ return result; |
+ } |
+ |
+ /** |
+ * Return the file with the given [absolutePath]. |
+ * |
+ * Return `null` if the given [absolutePath] is not in the workspace [root]. |
+ */ |
+ File findFile(String absolutePath) { |
+ try { |
+ File writableFile = provider.getFile(absolutePath); |
+ return writableFile; |
+ } catch (_) { |
+ return null; |
+ } |
+ } |
+ |
+ /** |
+ * Locate the output directory. |
+ * |
+ * Return `null` if it could not be found. |
+ */ |
+ static String _getOutDirectory(ResourceProvider provider, String root) => |
+ provider |
+ .getFolder('$root/out') |
+ .getChildren() |
+ .where((resource) => resource is Folder) |
+ .map((resource) => resource as Folder) |
+ .firstWhere((Folder folder) { |
+ String baseName = basename(folder.path); |
+ // TODO(pylaligand): find a better way to locate the proper directory. |
+ return baseName.startsWith('debug') || baseName.startsWith('release'); |
+ }, orElse: () => null)?.path; |
+ |
+ /** |
+ * Return a map of package source locations indexed by package name. |
+ */ |
+ static Map<String, String> _getPackages( |
+ ResourceProvider provider, String outDirectory) { |
+ String packagesDir = '$outDirectory/gen/dart.sources'; |
+ Map<String, String> result = new HashMap<String, String>(); |
+ provider |
+ .getFolder(packagesDir) |
+ .getChildren() |
+ .where((resource) => resource is File) |
+ .map((resource) => resource as File) |
+ .forEach((file) { |
+ String packageName = basename(file.path); |
+ String source = file.readAsStringSync(); |
+ result[packageName] = source; |
+ }); |
+ return result; |
+ } |
+ |
+ /** |
+ * Find the Gn workspace that contains the given [path]. |
+ * |
+ * Return `null` if a workspace markers, such as the `.jiri_root` directory |
+ * cannot be found. |
+ */ |
+ static GnWorkspace find(ResourceProvider provider, String path) { |
+ Context context = provider.pathContext; |
+ |
+ // Ensure that the path is absolute and normalized. |
+ if (!context.isAbsolute(path)) { |
+ throw new ArgumentError('not absolute: $path'); |
+ } |
+ path = context.normalize(path); |
+ |
+ Folder folder = provider.getFolder(path); |
+ while (true) { |
+ Folder parent = folder.parent; |
+ if (parent == null) { |
+ return null; |
+ } |
+ |
+ // Found the .jiri_root file, must be a non-git workspace. |
+ if (folder.getChildAssumingFolder(_jiriRootName).exists) { |
+ String root = folder.path; |
+ String outDirectory = _getOutDirectory(provider, root); |
+ Map<String, String> packages = _getPackages(provider, outDirectory); |
+ return new GnWorkspace._(provider, root, packages); |
+ } |
+ |
+ // Go up the folder. |
+ folder = parent; |
+ } |
+ } |
+} |