| Index: packages/analyzer/lib/file_system/memory_file_system.dart
|
| diff --git a/packages/analyzer/lib/file_system/memory_file_system.dart b/packages/analyzer/lib/file_system/memory_file_system.dart
|
| index 058f9eb9ef94b35bb562e826e1e1f0d514206ad3..a69e15eabd5820e2e34ef7da86da0e5e05ed411b 100644
|
| --- a/packages/analyzer/lib/file_system/memory_file_system.dart
|
| +++ b/packages/analyzer/lib/file_system/memory_file_system.dart
|
| @@ -2,19 +2,20 @@
|
| // 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 memory_file_system;
|
| +library analyzer.file_system.memory_file_system;
|
|
|
| import 'dart:async';
|
| import 'dart:collection';
|
| -import 'dart:core' hide Resource;
|
| +import 'dart:convert';
|
| +import 'dart:core';
|
|
|
| -import 'package:analyzer/src/generated/engine.dart' show TimestampedData;
|
| +import 'package:analyzer/file_system/file_system.dart';
|
| import 'package:analyzer/src/generated/source_io.dart';
|
| -import 'package:path/path.dart';
|
| +import 'package:analyzer/src/source/source_resource.dart';
|
| +import 'package:analyzer/src/util/absolute_path.dart';
|
| +import 'package:path/path.dart' as pathos;
|
| import 'package:watcher/watcher.dart';
|
|
|
| -import 'file_system.dart';
|
| -
|
| /**
|
| * An in-memory implementation of [ResourceProvider].
|
| * Use `/` as a path separator.
|
| @@ -22,14 +23,40 @@ import 'file_system.dart';
|
| class MemoryResourceProvider implements ResourceProvider {
|
| final Map<String, _MemoryResource> _pathToResource =
|
| new HashMap<String, _MemoryResource>();
|
| - final Map<String, String> _pathToContent = new HashMap<String, String>();
|
| + final Map<String, List<int>> _pathToBytes = new HashMap<String, List<int>>();
|
| final Map<String, int> _pathToTimestamp = new HashMap<String, int>();
|
| final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers =
|
| new HashMap<String, List<StreamController<WatchEvent>>>();
|
| int nextStamp = 0;
|
|
|
| + final pathos.Context _pathContext;
|
| +
|
| + @override
|
| + final AbsolutePathContext absolutePathContext;
|
| +
|
| + MemoryResourceProvider(
|
| + {pathos.Context context, @deprecated bool isWindows: false})
|
| + : _pathContext = (context ??= pathos.context),
|
| + absolutePathContext =
|
| + new AbsolutePathContext(context.style == pathos.Style.windows);
|
| +
|
| @override
|
| - Context get pathContext => posix;
|
| + pathos.Context get pathContext => _pathContext;
|
| +
|
| + /**
|
| + * Convert the given posix [path] to conform to this provider's path context.
|
| + *
|
| + * This is a utility method for testing; paths passed in to other methods in
|
| + * this class are never converted automatically.
|
| + */
|
| + String convertPath(String path) {
|
| + if (pathContext.style == pathos.windows.style &&
|
| + path.startsWith(pathos.posix.separator)) {
|
| + path = r'C:' +
|
| + path.replaceAll(pathos.posix.separator, pathos.windows.separator);
|
| + }
|
| + return path;
|
| + }
|
|
|
| /**
|
| * Delete the file with the given path.
|
| @@ -37,14 +64,14 @@ class MemoryResourceProvider implements ResourceProvider {
|
| void deleteFile(String path) {
|
| _checkFileAtPath(path);
|
| _pathToResource.remove(path);
|
| - _pathToContent.remove(path);
|
| + _pathToBytes.remove(path);
|
| _pathToTimestamp.remove(path);
|
| _notifyWatchers(path, ChangeType.REMOVE);
|
| }
|
|
|
| /**
|
| * Delete the folder with the given path
|
| - * and recurively delete nested files and folders.
|
| + * and recursively delete nested files and folders.
|
| */
|
| void deleteFolder(String path) {
|
| _checkFolderAtPath(path);
|
| @@ -59,7 +86,7 @@ class MemoryResourceProvider implements ResourceProvider {
|
| }
|
| }
|
| _pathToResource.remove(path);
|
| - _pathToContent.remove(path);
|
| + _pathToBytes.remove(path);
|
| _pathToTimestamp.remove(path);
|
| _notifyWatchers(path, ChangeType.REMOVE);
|
| }
|
| @@ -70,6 +97,14 @@ class MemoryResourceProvider implements ResourceProvider {
|
| @override
|
| Folder getFolder(String path) => newFolder(path);
|
|
|
| + @override
|
| + Future<List<int>> getModificationTimes(List<Source> sources) async {
|
| + return sources.map((source) {
|
| + String path = source.fullName;
|
| + return _pathToTimestamp[path] ?? -1;
|
| + }).toList();
|
| + }
|
| +
|
| @override
|
| Resource getResource(String path) {
|
| path = pathContext.normalize(path);
|
| @@ -87,7 +122,7 @@ class MemoryResourceProvider implements ResourceProvider {
|
|
|
| void modifyFile(String path, String content) {
|
| _checkFileAtPath(path);
|
| - _pathToContent[path] = content;
|
| + _pathToBytes[path] = UTF8.encode(content);
|
| _pathToTimestamp[path] = nextStamp++;
|
| _notifyWatchers(path, ChangeType.MODIFY);
|
| }
|
| @@ -108,25 +143,26 @@ class MemoryResourceProvider implements ResourceProvider {
|
|
|
| File newFile(String path, String content, [int stamp]) {
|
| path = pathContext.normalize(path);
|
| - _MemoryResource folder = _pathToResource[pathContext.dirname(path)];
|
| - if (folder == null) {
|
| - newFolder(pathContext.dirname(path));
|
| - } else if (folder is! Folder) {
|
| - throw new ArgumentError('Cannot create file ($path) as child of file');
|
| - }
|
| - _MemoryFile file = new _MemoryFile(this, path);
|
| - _pathToResource[path] = file;
|
| - _pathToContent[path] = content;
|
| - _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
|
| + _MemoryFile file = _newFile(path);
|
| + _pathToBytes[path] = UTF8.encode(content);
|
| + _pathToTimestamp[path] = stamp ?? nextStamp++;
|
| + _notifyWatchers(path, ChangeType.ADD);
|
| + return file;
|
| + }
|
| +
|
| + File newFileWithBytes(String path, List<int> bytes, [int stamp]) {
|
| + path = pathContext.normalize(path);
|
| + _MemoryFile file = _newFile(path);
|
| + _pathToBytes[path] = bytes;
|
| + _pathToTimestamp[path] = stamp ?? nextStamp++;
|
| _notifyWatchers(path, ChangeType.ADD);
|
| return file;
|
| }
|
|
|
| Folder newFolder(String path) {
|
| path = pathContext.normalize(path);
|
| - if (!path.startsWith(pathContext.separator)) {
|
| - throw new ArgumentError(
|
| - "Path must start with '${pathContext.separator}' : $path");
|
| + if (!pathContext.isAbsolute(path)) {
|
| + throw new ArgumentError("Path must be absolute : $path");
|
| }
|
| _MemoryResource resource = _pathToResource[path];
|
| if (resource == null) {
|
| @@ -149,17 +185,48 @@ class MemoryResourceProvider implements ResourceProvider {
|
| }
|
| }
|
|
|
| + _MemoryFile renameFileSync(_MemoryFile file, String newPath) {
|
| + String path = file.path;
|
| + if (newPath == path) {
|
| + return file;
|
| + }
|
| + _MemoryResource existingNewResource = _pathToResource[newPath];
|
| + if (existingNewResource is _MemoryFolder) {
|
| + throw new FileSystemException(
|
| + path, 'Could not be renamed: $newPath is a folder.');
|
| + }
|
| + _MemoryFile newFile = _newFile(newPath);
|
| + _pathToResource.remove(path);
|
| + _pathToBytes[newPath] = _pathToBytes.remove(path);
|
| + _pathToTimestamp[newPath] = _pathToTimestamp.remove(path);
|
| + if (existingNewResource != null) {
|
| + _notifyWatchers(newPath, ChangeType.REMOVE);
|
| + }
|
| + _notifyWatchers(path, ChangeType.REMOVE);
|
| + _notifyWatchers(newPath, ChangeType.ADD);
|
| + return newFile;
|
| + }
|
| +
|
| File updateFile(String path, String content, [int stamp]) {
|
| path = pathContext.normalize(path);
|
| newFolder(pathContext.dirname(path));
|
| _MemoryFile file = new _MemoryFile(this, path);
|
| _pathToResource[path] = file;
|
| - _pathToContent[path] = content;
|
| - _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
|
| + _pathToBytes[path] = UTF8.encode(content);
|
| + _pathToTimestamp[path] = stamp ?? nextStamp++;
|
| _notifyWatchers(path, ChangeType.MODIFY);
|
| return file;
|
| }
|
|
|
| + /**
|
| + * Write a representation of the file system on the given [sink].
|
| + */
|
| + void writeOn(StringSink sink) {
|
| + List<String> paths = _pathToResource.keys.toList();
|
| + paths.sort();
|
| + paths.forEach(sink.writeln);
|
| + }
|
| +
|
| void _checkFileAtPath(String path) {
|
| _MemoryResource resource = _pathToResource[path];
|
| if (resource is! _MemoryFile) {
|
| @@ -176,6 +243,22 @@ class MemoryResourceProvider implements ResourceProvider {
|
| }
|
| }
|
|
|
| + /**
|
| + * Create a new [_MemoryFile] without any content.
|
| + */
|
| + _MemoryFile _newFile(String path) {
|
| + String folderPath = pathContext.dirname(path);
|
| + _MemoryResource folder = _pathToResource[folderPath];
|
| + if (folder == null) {
|
| + newFolder(folderPath);
|
| + } else if (folder is! Folder) {
|
| + throw new ArgumentError('Cannot create file ($path) as child of file');
|
| + }
|
| + _MemoryFile file = new _MemoryFile(this, path);
|
| + _pathToResource[path] = file;
|
| + return file;
|
| + }
|
| +
|
| void _notifyWatchers(String path, ChangeType changeType) {
|
| _pathToWatchers.forEach((String watcherPath,
|
| List<StreamController<WatchEvent>> streamControllers) {
|
| @@ -187,6 +270,14 @@ class MemoryResourceProvider implements ResourceProvider {
|
| }
|
| });
|
| }
|
| +
|
| + void _setFileContent(_MemoryFile file, List<int> bytes) {
|
| + String path = file.path;
|
| + _pathToResource[path] = file;
|
| + _pathToBytes[path] = bytes;
|
| + _pathToTimestamp[path] = nextStamp++;
|
| + _notifyWatchers(path, ChangeType.MODIFY);
|
| + }
|
| }
|
|
|
| /**
|
| @@ -205,6 +296,7 @@ class _MemoryDummyLink extends _MemoryResource implements File {
|
| @override
|
| bool get exists => false;
|
|
|
| + @override
|
| int get modificationStamp {
|
| int stamp = _provider._pathToTimestamp[path];
|
| if (stamp == null) {
|
| @@ -213,13 +305,14 @@ class _MemoryDummyLink extends _MemoryResource implements File {
|
| return stamp;
|
| }
|
|
|
| - String get _content {
|
| + @override
|
| + Source createSource([Uri uri]) {
|
| throw new FileSystemException(path, 'File could not be read');
|
| }
|
|
|
| @override
|
| - Source createSource([Uri uri]) {
|
| - throw new FileSystemException(path, 'File could not be read');
|
| + void delete() {
|
| + throw new FileSystemException(path, 'File could not be deleted');
|
| }
|
|
|
| @override
|
| @@ -227,10 +320,35 @@ class _MemoryDummyLink extends _MemoryResource implements File {
|
| return path == this.path;
|
| }
|
|
|
| + @override
|
| + List<int> readAsBytesSync() {
|
| + throw new FileSystemException(path, 'File could not be read');
|
| + }
|
| +
|
| @override
|
| String readAsStringSync() {
|
| throw new FileSystemException(path, 'File could not be read');
|
| }
|
| +
|
| + @override
|
| + File renameSync(String newPath) {
|
| + throw new FileSystemException(path, 'File could not be renamed');
|
| + }
|
| +
|
| + @override
|
| + File resolveSymbolicLinksSync() {
|
| + return throw new FileSystemException(path, "File does not exist");
|
| + }
|
| +
|
| + @override
|
| + void writeAsBytesSync(List<int> bytes) {
|
| + throw new FileSystemException(path, 'File could not be written');
|
| + }
|
| +
|
| + @override
|
| + void writeAsStringSync(String content) {
|
| + throw new FileSystemException(path, 'File could not be written');
|
| + }
|
| }
|
|
|
| /**
|
| @@ -243,6 +361,7 @@ class _MemoryFile extends _MemoryResource implements File {
|
| @override
|
| bool get exists => _provider._pathToResource[path] is _MemoryFile;
|
|
|
| + @override
|
| int get modificationStamp {
|
| int stamp = _provider._pathToTimestamp[path];
|
| if (stamp == null) {
|
| @@ -251,20 +370,15 @@ class _MemoryFile extends _MemoryResource implements File {
|
| return stamp;
|
| }
|
|
|
| - String get _content {
|
| - String content = _provider._pathToContent[path];
|
| - if (content == null) {
|
| - throw new FileSystemException(path, 'File "$path" does not exist.');
|
| - }
|
| - return content;
|
| + @override
|
| + Source createSource([Uri uri]) {
|
| + uri ??= _provider.pathContext.toUri(path);
|
| + return new FileSource(this, uri);
|
| }
|
|
|
| @override
|
| - Source createSource([Uri uri]) {
|
| - if (uri == null) {
|
| - uri = _provider.pathContext.toUri(path);
|
| - }
|
| - return new _MemoryFileSource(this, uri);
|
| + void delete() {
|
| + _provider.deleteFile(path);
|
| }
|
|
|
| @override
|
| @@ -273,107 +387,40 @@ class _MemoryFile extends _MemoryResource implements File {
|
| }
|
|
|
| @override
|
| - String readAsStringSync() {
|
| - String content = _provider._pathToContent[path];
|
| + List<int> readAsBytesSync() {
|
| + List<int> content = _provider._pathToBytes[path];
|
| if (content == null) {
|
| throw new FileSystemException(path, 'File "$path" does not exist.');
|
| }
|
| return content;
|
| }
|
| -}
|
| -
|
| -/**
|
| - * An in-memory implementation of [Source].
|
| - */
|
| -class _MemoryFileSource extends Source {
|
| - /**
|
| - * Map from encoded URI/filepath pair to a unique integer identifier. This
|
| - * identifier is used for equality tests and hash codes.
|
| - *
|
| - * The URI and filepath are joined into a pair by separating them with an '@'
|
| - * character.
|
| - */
|
| - static final Map<String, int> _idTable = new HashMap<String, int>();
|
| -
|
| - final _MemoryFile file;
|
| -
|
| - final Uri uri;
|
| -
|
| - /**
|
| - * The unique ID associated with this [_MemoryFileSource].
|
| - */
|
| - final int id;
|
| -
|
| - _MemoryFileSource(_MemoryFile file, Uri uri)
|
| - : uri = uri,
|
| - file = file,
|
| - id = _idTable.putIfAbsent('$uri@${file.path}', () => _idTable.length);
|
| -
|
| - @override
|
| - TimestampedData<String> get contents {
|
| - return new TimestampedData<String>(modificationStamp, file._content);
|
| - }
|
| -
|
| - @override
|
| - String get encoding {
|
| - return uri.toString();
|
| - }
|
|
|
| @override
|
| - String get fullName => file.path;
|
| -
|
| - @override
|
| - int get hashCode => id;
|
| -
|
| - @override
|
| - bool get isInSystemLibrary => uriKind == UriKind.DART_URI;
|
| -
|
| - @override
|
| - int get modificationStamp {
|
| - try {
|
| - return file.modificationStamp;
|
| - } on FileSystemException {
|
| - return -1;
|
| - }
|
| - }
|
| -
|
| - @override
|
| - String get shortName => file.shortName;
|
| -
|
| - @override
|
| - UriKind get uriKind {
|
| - String scheme = uri.scheme;
|
| - if (scheme == PackageUriResolver.PACKAGE_SCHEME) {
|
| - return UriKind.PACKAGE_URI;
|
| - } else if (scheme == DartUriResolver.DART_SCHEME) {
|
| - return UriKind.DART_URI;
|
| - } else if (scheme == FileUriResolver.FILE_SCHEME) {
|
| - return UriKind.FILE_URI;
|
| + String readAsStringSync() {
|
| + List<int> content = _provider._pathToBytes[path];
|
| + if (content == null) {
|
| + throw new FileSystemException(path, 'File "$path" does not exist.');
|
| }
|
| - return UriKind.FILE_URI;
|
| + return UTF8.decode(content);
|
| }
|
|
|
| @override
|
| - bool operator ==(other) {
|
| - return other is _MemoryFileSource && other.id == id;
|
| + File renameSync(String newPath) {
|
| + return _provider.renameFileSync(this, newPath);
|
| }
|
|
|
| @override
|
| - bool exists() => file.exists;
|
| + File resolveSymbolicLinksSync() => this;
|
|
|
| @override
|
| - Uri resolveRelativeUri(Uri relativeUri) {
|
| - Uri baseUri = uri;
|
| - String scheme = uri.scheme;
|
| - if (scheme == DartUriResolver.DART_SCHEME) {
|
| - String libraryName = uri.path;
|
| - baseUri = Uri.parse('$scheme:$libraryName/$libraryName.dart');
|
| - }
|
| - return baseUri.resolveUri(relativeUri);
|
| + void writeAsBytesSync(List<int> bytes) {
|
| + _provider._setFileContent(this, bytes);
|
| }
|
|
|
| @override
|
| - String toString() => file.toString();
|
| + void writeAsStringSync(String content) {
|
| + _provider._setFileContent(this, UTF8.encode(content));
|
| + }
|
| }
|
|
|
| /**
|
| @@ -399,6 +446,11 @@ class _MemoryFolder extends _MemoryResource implements Folder {
|
| return _provider.pathContext.isWithin(this.path, path);
|
| }
|
|
|
| + @override
|
| + void delete() {
|
| + _provider.deleteFolder(path);
|
| + }
|
| +
|
| @override
|
| Resource getChild(String relPath) {
|
| String childPath = canonicalizePath(relPath);
|
| @@ -409,6 +461,16 @@ class _MemoryFolder extends _MemoryResource implements Folder {
|
| return resource;
|
| }
|
|
|
| + @override
|
| + _MemoryFile getChildAssumingFile(String relPath) {
|
| + String childPath = canonicalizePath(relPath);
|
| + _MemoryResource resource = _provider._pathToResource[childPath];
|
| + if (resource is _MemoryFile) {
|
| + return resource;
|
| + }
|
| + return new _MemoryFile(_provider, childPath);
|
| + }
|
| +
|
| @override
|
| _MemoryFolder getChildAssumingFolder(String relPath) {
|
| String childPath = canonicalizePath(relPath);
|
| @@ -440,6 +502,9 @@ class _MemoryFolder extends _MemoryResource implements Folder {
|
| }
|
| return contains(path);
|
| }
|
| +
|
| + @override
|
| + Folder resolveSymbolicLinksSync() => this;
|
| }
|
|
|
| /**
|
| @@ -447,6 +512,7 @@ class _MemoryFolder extends _MemoryResource implements Folder {
|
| */
|
| abstract class _MemoryResource implements Resource {
|
| final MemoryResourceProvider _provider;
|
| + @override
|
| final String path;
|
|
|
| _MemoryResource(this._provider, this.path);
|
| @@ -492,4 +558,7 @@ abstract class _MemoryResource implements Resource {
|
|
|
| @override
|
| String toString() => path;
|
| +
|
| + @override
|
| + Uri toUri() => _provider.pathContext.toUri(path);
|
| }
|
|
|