| Index: pkg/pathos/lib/path.dart
|
| diff --git a/pkg/pathos/lib/path.dart b/pkg/pathos/lib/path.dart
|
| index 145953f3c8aa619d13f4fbcbaec0c9df65a184dd..eeb426db62b5744701dc049b0ca4adbdf6e874fe 100644
|
| --- a/pkg/pathos/lib/path.dart
|
| +++ b/pkg/pathos/lib/path.dart
|
| @@ -275,6 +275,45 @@ String relative(String path, {String from}) =>
|
| /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
|
| String withoutExtension(String path) => _builder.withoutExtension(path);
|
|
|
| +/// Returns the path represented by [uri].
|
| +///
|
| +/// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
|
| +/// style, this will just convert [uri] to a string.
|
| +///
|
| +/// // POSIX
|
| +/// path.fromUri(Uri.parse('file:///path/to/foo'))
|
| +/// // -> '/path/to/foo'
|
| +///
|
| +/// // Windows
|
| +/// path.fromUri(Uri.parse('file:///C:/path/to/foo'))
|
| +/// // -> r'C:\path\to\foo'
|
| +///
|
| +/// // URL
|
| +/// path.fromUri(Uri.parse('http://dartlang.org/path/to/foo'))
|
| +/// // -> 'http://dartlang.org/path/to/foo'
|
| +String fromUri(Uri uri) => _builder.fromUri(uri);
|
| +
|
| +/// Returns the URI that represents [path].
|
| +///
|
| +/// For POSIX and Windows styles, this will return a `file:` URI. For the URL
|
| +/// style, this will just convert [path] to a [Uri].
|
| +///
|
| +/// This will always convert relative paths to absolute ones before converting
|
| +/// to a URI.
|
| +///
|
| +/// // POSIX
|
| +/// path.toUri('/path/to/foo')
|
| +/// // -> Uri.parse('file:///path/to/foo')
|
| +///
|
| +/// // Windows
|
| +/// path.toUri(r'C:\path\to\foo')
|
| +/// // -> Uri.parse('file:///C:/path/to/foo')
|
| +///
|
| +/// // URL
|
| +/// path.toUri('http://dartlang.org/path/to/foo')
|
| +/// // -> Uri.parse('http://dartlang.org/path/to/foo')
|
| +Uri toUri(String path) => _builder.toUri(path);
|
| +
|
| /// Validates that there are no non-null arguments following a null one and
|
| /// throws an appropriate [ArgumentError] on failure.
|
| _validateArgList(String method, List<String> args) {
|
| @@ -673,6 +712,48 @@ class Builder {
|
| return parsed.toString();
|
| }
|
|
|
| + /// Returns the path represented by [uri].
|
| + ///
|
| + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
|
| + /// style, this will just convert [uri] to a string.
|
| + ///
|
| + /// // POSIX
|
| + /// builder.fromUri(Uri.parse('file:///path/to/foo'))
|
| + /// // -> '/path/to/foo'
|
| + ///
|
| + /// // Windows
|
| + /// builder.fromUri(Uri.parse('file:///C:/path/to/foo'))
|
| + /// // -> r'C:\path\to\foo'
|
| + ///
|
| + /// // URL
|
| + /// builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo'))
|
| + /// // -> 'http://dartlang.org/path/to/foo'
|
| + String fromUri(Uri uri) => style.pathFromUri(uri);
|
| +
|
| + /// Returns the URI that represents [path].
|
| + ///
|
| + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL
|
| + /// style, this will just convert [path] to a [Uri].
|
| + ///
|
| + /// // POSIX
|
| + /// builder.toUri('/path/to/foo')
|
| + /// // -> Uri.parse('file:///path/to/foo')
|
| + ///
|
| + /// // Windows
|
| + /// builder.toUri(r'C:\path\to\foo')
|
| + /// // -> Uri.parse('file:///C:/path/to/foo')
|
| + ///
|
| + /// // URL
|
| + /// builder.toUri('http://dartlang.org/path/to/foo')
|
| + /// // -> Uri.parse('http://dartlang.org/path/to/foo')
|
| + Uri toUri(String path) {
|
| + if (isRelative(path)) {
|
| + return Uri.parse(path.replaceAll(style.separatorPattern, '/'));
|
| + } else {
|
| + return style.pathToUri(join(root, path));
|
| + }
|
| + }
|
| +
|
| _ParsedPath _parse(String path) {
|
| var before = path;
|
|
|
| @@ -711,18 +792,17 @@ class Builder {
|
| }
|
|
|
| /// An enum type describing a "flavor" of path.
|
| -class Style {
|
| +abstract class Style {
|
| /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
|
| /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
|
| - static final posix = new Style._('posix', '/', '/', r'[^/]$', '/');
|
| + static final posix = new _PosixStyle();
|
|
|
| /// Windows paths use "\" (backslash) as separators. Absolute paths start with
|
| /// a drive letter followed by a colon (example, "C:") or two backslashes
|
| /// ("\\") for UNC paths.
|
| // TODO(rnystrom): The UNC root prefix should include the drive name too, not
|
| // just the "\\".
|
| - static final windows = new Style._('windows', '\\', r'[/\\]', r'[^/\\]$',
|
| - r'\\\\|[a-zA-Z]:[/\\]');
|
| + static final windows = new _WindowsStyle();
|
|
|
| /// URLs aren't filesystem paths, but they're supported by Pathos to make it
|
| /// easier to manipulate URL paths in the browser.
|
| @@ -730,30 +810,19 @@ class Style {
|
| /// URLs use "/" (forward slash) as separators. Absolute paths either start
|
| /// with a protocol and optional hostname (e.g. `http://dartlang.org`,
|
| /// `file://`) or with "/".
|
| - static final url = new Style._('url', '/', '/',
|
| - r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$",
|
| - r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*", r"/");
|
| -
|
| - Style._(this.name, this.separator, String separatorPattern,
|
| - String needsSeparatorPattern, String rootPattern,
|
| - [String relativeRootPattern])
|
| - : separatorPattern = new RegExp(separatorPattern),
|
| - needsSeparatorPattern = new RegExp(needsSeparatorPattern),
|
| - _rootPattern = new RegExp('^$rootPattern'),
|
| - _relativeRootPattern = relativeRootPattern == null ? null :
|
| - new RegExp('^$relativeRootPattern');
|
| + static final url = new _UrlStyle();
|
|
|
| /// The name of this path style. Will be "posix" or "windows".
|
| - final String name;
|
| + String get name;
|
|
|
| /// The path separator for this style. On POSIX, this is `/`. On Windows,
|
| /// it's `\`.
|
| - final String separator;
|
| + String get separator;
|
|
|
| /// The [Pattern] that can be used to match a separator for a path in this
|
| - /// style. Windows allows both "/" and "\" as path separators even though
|
| - /// "\" is the canonical one.
|
| - final Pattern separatorPattern;
|
| + /// style. Windows allows both "/" and "\" as path separators even though "\"
|
| + /// is the canonical one.
|
| + Pattern get separatorPattern;
|
|
|
| /// The [Pattern] that matches path components that need a separator after
|
| /// them.
|
| @@ -763,24 +832,23 @@ class Style {
|
| /// separator between the root and the first component, even if the root
|
| /// already ends in a separator character. For example, to join "file://" and
|
| /// "usr", an additional "/" is needed (making "file:///usr").
|
| - final Pattern needsSeparatorPattern;
|
| + Pattern get needsSeparatorPattern;
|
|
|
| - // TODO(nweiz): make this a Pattern when issue 7080 is fixed.
|
| - /// The [RegExp] that can be used to match the root prefix of an absolute
|
| + /// The [Pattern] that can be used to match the root prefix of an absolute
|
| /// path in this style.
|
| - final RegExp _rootPattern;
|
| + Pattern get rootPattern;
|
|
|
| - /// The [RegExp] that can be used to match the root prefix of a root-relative
|
| + /// The [Pattern] that can be used to match the root prefix of a root-relative
|
| /// path in this style.
|
| ///
|
| /// This can be null to indicate that this style doesn't support root-relative
|
| /// paths.
|
| - final RegExp _relativeRootPattern;
|
| + final Pattern relativeRootPattern = null;
|
|
|
| /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
|
| /// returns `null`.
|
| String getRoot(String path) {
|
| - var match = _rootPattern.firstMatch(path);
|
| + var match = rootPattern.firstMatch(path);
|
| if (match != null) return match[0];
|
| return getRelativeRoot(path);
|
| }
|
| @@ -789,15 +857,146 @@ class Style {
|
| ///
|
| /// If [path] is relative or absolute and not root-relative, returns `null`.
|
| String getRelativeRoot(String path) {
|
| - if (_relativeRootPattern == null) return null;
|
| - var match = _relativeRootPattern.firstMatch(path);
|
| + if (relativeRootPattern == null) return null;
|
| + var match = relativeRootPattern.firstMatch(path);
|
| if (match == null) return null;
|
| return match[0];
|
| }
|
|
|
| + /// Returns the path represented by [uri] in this style.
|
| + String pathFromUri(Uri uri);
|
| +
|
| + /// Returns the URI that represents [path].
|
| + ///
|
| + /// Pathos will always path an absolute path for [path]. Relative paths are
|
| + /// handled automatically by [Builder].
|
| + Uri pathToUri(String path);
|
| +
|
| String toString() => name;
|
| }
|
|
|
| +/// The style for POSIX paths.
|
| +class _PosixStyle extends Style {
|
| + _PosixStyle();
|
| +
|
| + static final _builder = new Builder(style: Style.posix);
|
| +
|
| + final name = 'posix';
|
| + final separator = '/';
|
| + final separatorPattern = new RegExp(r'/');
|
| + final needsSeparatorPattern = new RegExp(r'[^/]$');
|
| + final rootPattern = new RegExp(r'^/');
|
| +
|
| + String pathFromUri(Uri uri) {
|
| + if (uri.scheme == '' || uri.scheme == 'file') {
|
| + return Uri.decodeComponent(uri.path);
|
| + }
|
| + throw new ArgumentError("Uri $uri must have scheme 'file:'.");
|
| + }
|
| +
|
| + Uri pathToUri(String path) {
|
| + var parsed = _builder._parse(path);
|
| +
|
| + if (parsed.parts.isEmpty) {
|
| + // If the path is a bare root (e.g. "/"), [components] will
|
| + // currently be empty. We add two empty components so the URL constructor
|
| + // produces "file:///", with a trailing slash.
|
| + parsed.parts.addAll(["", ""]);
|
| + } else if (parsed.hasTrailingSeparator) {
|
| + // If the path has a trailing slash, add a single empty component so the
|
| + // URI has a trailing slash as well.
|
| + parsed.parts.add("");
|
| + }
|
| +
|
| + return new Uri(scheme: 'file', pathSegments: parsed.parts);
|
| + }
|
| +}
|
| +
|
| +/// The style for Windows paths.
|
| +class _WindowsStyle extends Style {
|
| + _WindowsStyle();
|
| +
|
| + static final _builder = new Builder(style: Style.windows);
|
| +
|
| + final name = 'windows';
|
| + final separator = '\\';
|
| + final separatorPattern = new RegExp(r'[/\\]');
|
| + final needsSeparatorPattern = new RegExp(r'[^/\\]$');
|
| + final rootPattern = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])');
|
| +
|
| + String pathFromUri(Uri uri) {
|
| + if (uri.scheme != '' && uri.scheme != 'file') {
|
| + throw new ArgumentError("Uri $uri must have scheme 'file:'.");
|
| + }
|
| +
|
| + var path = uri.path;
|
| + if (uri.host == '') {
|
| + // Drive-letter paths look like "file:///C:/path/to/file". The
|
| + // replaceFirst removes the extra initial slash.
|
| + if (path.startsWith('/')) path = path.replaceFirst("/", "");
|
| + } else {
|
| + // Network paths look like "file://hostname/path/to/file".
|
| + path = '\\\\${uri.host}$path';
|
| + }
|
| + return Uri.decodeComponent(path.replaceAll("/", "\\"));
|
| + }
|
| +
|
| + Uri pathToUri(String path) {
|
| + var parsed = _builder._parse(path);
|
| + if (parsed.root == r'\\') {
|
| + // Network paths become "file://hostname/path/to/file".
|
| +
|
| + var host = parsed.parts.removeAt(0);
|
| +
|
| + if (parsed.parts.isEmpty) {
|
| + // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will
|
| + // currently be empty. We add two empty components so the URL
|
| + // constructor produces "file://hostname/", with a trailing slash.
|
| + parsed.parts.addAll(["", ""]);
|
| + } else if (parsed.hasTrailingSeparator) {
|
| + // If the path has a trailing slash, add a single empty component so the
|
| + // URI has a trailing slash as well.
|
| + parsed.parts.add("");
|
| + }
|
| +
|
| + return new Uri(scheme: 'file', host: host, pathSegments: parsed.parts);
|
| + } else {
|
| + // Drive-letter paths become "file:///C:/path/to/file".
|
| +
|
| + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently
|
| + // be empty. We add an empty component so the URL constructor produces
|
| + // "file:///C:/", with a trailing slash. We also add an empty component if
|
| + // the URL otherwise has a trailing slash.
|
| + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) {
|
| + parsed.parts.add("");
|
| + }
|
| +
|
| + // Get rid of the trailing "\" in "C:\" because the URI constructor will
|
| + // add a separator on its own.
|
| + parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, ""));
|
| +
|
| + return new Uri(scheme: 'file', pathSegments: parsed.parts);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/// The style for URL paths.
|
| +class _UrlStyle extends Style {
|
| + _UrlStyle();
|
| +
|
| + final name = 'url';
|
| + final separator = '/';
|
| + final separatorPattern = new RegExp(r'/');
|
| + final needsSeparatorPattern = new RegExp(
|
| + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$");
|
| + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*");
|
| + final relativeRootPattern = new RegExp(r"^/");
|
| +
|
| + String pathFromUri(Uri uri) => uri.toString();
|
| +
|
| + Uri pathToUri(String path) => Uri.parse(path);
|
| +}
|
| +
|
| // TODO(rnystrom): Make this public?
|
| class _ParsedPath {
|
| /// The [Style] that was used to parse this path.
|
| @@ -848,6 +1047,8 @@ class _ParsedPath {
|
| return copy._splitExtension()[0];
|
| }
|
|
|
| + bool get hasTrailingSeparator => !parts.isEmpty && (parts.last == '' || separators.last != '');
|
| +
|
| void removeTrailingSeparators() {
|
| while (!parts.isEmpty && parts.last == '') {
|
| parts.removeLast();
|
|
|