Chromium Code Reviews| Index: pkg/pathos/lib/path.dart |
| diff --git a/pkg/pathos/lib/path.dart b/pkg/pathos/lib/path.dart |
| index 80ffe72a57da05881db73e521e0753a0788d560b..796e06f8250fb9086367d679647a6b707e9b5527 100644 |
| --- a/pkg/pathos/lib/path.dart |
| +++ b/pkg/pathos/lib/path.dart |
| @@ -31,13 +31,8 @@ final _builder = new Builder(); |
| * Inserts [length] elements in front of the [list] and fills them with the |
| * [fillValue]. |
| */ |
| -void _growListFront(List list, int length, fillValue) { |
| - list.length += length; |
| - list.setRange(length, list.length, list); |
| - for (var i = 0; i < length; i++) { |
| - list[i] = fillValue; |
| - } |
| -} |
| +void _growListFront(List list, int length, fillValue) => |
| + list.insertAll(0, new List.filled(length, fillValue)); |
| /// Gets the path to the current working directory. |
| String get current => io.Directory.current.path; |
| @@ -112,9 +107,17 @@ String extension(String path) => _builder.extension(path); |
| String rootPrefix(String path) => _builder.rootPrefix(path); |
| /// Returns `true` if [path] is an absolute path and `false` if it is a |
| -/// relative path. On POSIX systems, absolute paths start with a `/` (forward |
| -/// slash). On Windows, an absolute path starts with `\\`, or a drive letter |
| -/// followed by `:/` or `:\`. |
| +/// relative path. |
| +/// |
| +/// On POSIX systems, absolute paths start with a `/` (forward slash). On |
| +/// Windows, an absolute path starts with `\\`, or a drive letter followed by |
| +/// `:/` or `:\`. For URLs, absolute paths either start with a protocol and |
| +/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. |
| +/// |
| +/// URLs that start with `/` are known as "root-relative", since they're |
| +/// relative to the root of the current URL. Since root-relative paths are still |
| +/// absolute in every other sense, [isAbsolute] will return true for them. They |
| +/// can be detected using [isRootRelative]. |
| bool isAbsolute(String path) => _builder.isAbsolute(path); |
| /// Returns `true` if [path] is a relative path and `false` if it is absolute. |
| @@ -123,6 +126,16 @@ bool isAbsolute(String path) => _builder.isAbsolute(path); |
| /// `:/` or `:\`. |
| bool isRelative(String path) => _builder.isRelative(path); |
| +/// Returns `true` if [path] is a root-relative path and `false` if it's not. |
| +/// |
| +/// URLs that start with `/` are known as "root-relative", since they're |
| +/// relative to the root of the current URL. Since root-relative paths are still |
| +/// absolute in every other sense, [isAbsolute] will return true for them. They |
| +/// can be detected using [isRootRelative]. |
| +/// |
| +/// No POSIX and Windows paths are root-relative. |
| +bool isRootRelative(String path) => _builder.isRootRelative(path); |
| + |
| /// Joins the given path parts into a single path using the current platform's |
| /// [separator]. Example: |
| /// |
| @@ -340,9 +353,17 @@ class Builder { |
| } |
| /// Returns `true` if [path] is an absolute path and `false` if it is a |
| - /// relative path. On POSIX systems, absolute paths start with a `/` (forward |
| - /// slash). On Windows, an absolute path starts with `\\`, or a drive letter |
| - /// followed by `:/` or `:\`. |
| + /// relative path. |
| + /// |
| + /// On POSIX systems, absolute paths start with a `/` (forward slash). On |
| + /// Windows, an absolute path starts with `\\`, or a drive letter followed by |
| + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and |
| + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. |
| + /// |
| + /// URLs that start with `/` are known as "root-relative", since they're |
| + /// relative to the root of the current URL. Since root-relative paths are still |
|
Bob Nystrom
2013/06/06 22:50:40
Long lines.
nweiz
2013/06/07 01:04:30
Done.
|
| + /// absolute in every other sense, [isAbsolute] will return true for them. They |
| + /// can be detected using [isRootRelative]. |
| bool isAbsolute(String path) => _parse(path).isAbsolute; |
| /// Returns `true` if [path] is a relative path and `false` if it is absolute. |
| @@ -351,6 +372,16 @@ class Builder { |
| /// `:/` or `:\`. |
| bool isRelative(String path) => !isAbsolute(path); |
| + /// Returns `true` if [path] is a root-relative path and `false` if it's not. |
| + /// |
| + /// URLs that start with `/` are known as "root-relative", since they're |
| + /// relative to the root of the current URL. Since root-relative paths are still |
|
Bob Nystrom
2013/06/06 22:50:40
Ditto.
nweiz
2013/06/07 01:04:30
Done.
|
| + /// absolute in every other sense, [isAbsolute] will return true for them. They |
| + /// can be detected using [isRootRelative]. |
| + /// |
| + /// No POSIX and Windows paths are root-relative. |
| + bool isRootRelative(String path) => _parse(path).isRootRelative; |
| + |
| /// Joins the given path parts into a single path. Example: |
| /// |
| /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' |
| @@ -390,7 +421,14 @@ class Builder { |
| var needsSeparator = false; |
| for (var part in parts) { |
| - if (this.isAbsolute(part)) { |
| + if (this.isRootRelative(part) && |
| + this.isAbsolute(buffer.toString()) && |
| + !this.isRootRelative(buffer.toString())) { |
| + var oldRoot = this.rootPrefix(buffer.toString()); |
|
Bob Nystrom
2013/06/06 22:50:40
A couple of things here:
1. Calling toString() on
nweiz
2013/06/07 01:04:30
Done.
|
| + buffer = new StringBuffer(); |
|
Bob Nystrom
2013/06/06 22:50:40
buffer.clear() seems cleaner to me. Below too?
nweiz
2013/06/07 01:04:30
Done.
|
| + buffer.write(oldRoot); |
| + buffer.write(part); |
| + } else if (this.isAbsolute(part)) { |
| // An absolute path discards everything before it. |
| buffer = new StringBuffer(); |
| buffer.write(part); |
| @@ -406,8 +444,7 @@ class Builder { |
| // Unless this part ends with a separator, we'll need to add one before |
| // the next part. |
| - needsSeparator = part.length > 0 && |
| - !part[part.length - 1].contains(style.separatorPattern); |
| + needsSeparator = part.contains(style.needsSeparatorPattern); |
| } |
| return buffer.toString(); |
| @@ -498,7 +535,9 @@ class Builder { |
| // If the given path is relative, resolve it relative to the root of the |
| // builder. |
| - if (this.isRelative(path)) path = this.resolve(path); |
| + if (this.isRelative(path) || this.isRootRelative(path)) { |
| + path = this.resolve(path); |
| + } |
| // If the path is still relative and `from` is absolute, we're unable to |
| // find a path from `from` to `path`. |
| @@ -524,16 +563,17 @@ class Builder { |
| while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && |
| fromParsed.parts[0] == pathParsed.parts[0]) { |
| fromParsed.parts.removeAt(0); |
| - fromParsed.separators.removeAt(0); |
| + fromParsed.separators.removeAt(1); |
| pathParsed.parts.removeAt(0); |
| - pathParsed.separators.removeAt(0); |
| + pathParsed.separators.removeAt(1); |
| } |
| // If there are any directories left in the root path, we need to walk up |
| // out of them. |
| _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); |
| - _growListFront( |
| - pathParsed.separators, fromParsed.parts.length, style.separator); |
| + pathParsed.separators[0] = ''; |
| + pathParsed.separators.insertAll(1, |
| + new List.filled(fromParsed.parts.length, style.separator)); |
| // Corner case: the paths completely collapsed. |
| if (pathParsed.parts.length == 0) return '.'; |
| @@ -566,11 +606,21 @@ class Builder { |
| // Remove the root prefix, if any. |
| var root = style.getRoot(path); |
| + var isRootRelative = style.getRelativeRoot(path) != null; |
| if (root != null) path = path.substring(root.length); |
| // Split the parts on path separators. |
| var parts = []; |
| var separators = []; |
| + |
| + var firstSeparator = style.separatorPattern.firstMatch(path); |
| + if (firstSeparator != null && firstSeparator.start == 0) { |
| + separators.add(firstSeparator[0]); |
| + path = path.substring(firstSeparator[0].length); |
| + } else { |
| + separators.add(''); |
| + } |
| + |
| var start = 0; |
| for (var match in style.separatorPattern.allMatches(path)) { |
| parts.add(path.substring(start, match.start)); |
| @@ -584,7 +634,7 @@ class Builder { |
| separators.add(''); |
| } |
| - return new _ParsedPath(style, root, parts, separators); |
| + return new _ParsedPath(style, root, isRootRelative, parts, separators); |
| } |
| } |
| @@ -592,20 +642,34 @@ class Builder { |
| 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', '/', '/', '/'); |
| + static final posix = new Style._('posix', '/', '/', r'[^/]$', '/'); |
| /// 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'[/\\]', |
| + static final windows = new Style._('windows', '\\', r'[/\\]', r'[^/\\]$', |
| r'\\\\|[a-zA-Z]:[/\\]'); |
| + /// URLs aren't filesystem paths, but they're supported by Pathos to make it |
| + /// easier to manipulate URL paths in the browser. |
| + /// |
| + /// 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 rootPattern) |
| + String needsSeparatorPattern, String rootPattern, |
| + [String relativeRootPattern]) |
| : separatorPattern = new RegExp(separatorPattern), |
| - _rootPattern = new RegExp('^$rootPattern'); |
| + needsSeparatorPattern = new RegExp(needsSeparatorPattern), |
| + _rootPattern = new RegExp('^$rootPattern'), |
| + _relativeRootPattern = relativeRootPattern == null ? null : |
| + new RegExp('^$relativeRootPattern'); |
| /// The name of this path style. Will be "posix" or "windows". |
| final String name; |
| @@ -619,15 +683,39 @@ class Style { |
| /// "\" is the canonical one. |
| final Pattern separatorPattern; |
| + /// The [Pattern] that matches path components that need a separator after |
| + /// them. |
| + /// |
| + /// Some styles ([url] in particlar) have moderately complex rules about when |
| + /// a separator is needed, especially for root path components. |
|
Bob Nystrom
2013/06/06 22:50:40
Can you explain what these rules are somewhere?
nweiz
2013/06/07 01:04:30
Done.
|
| + final Pattern 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 |
| /// path in this style. |
| final RegExp _rootPattern; |
| + /// The [RegExp] 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; |
| + |
| /// 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); |
| + if (match == null) return getRelativeRoot(path); |
| + return match[0]; |
|
Bob Nystrom
2013/06/06 22:50:40
Seems weird to refer back to match here after dele
nweiz
2013/06/07 01:04:30
Done.
|
| + } |
| + |
| + /// Gets the root prefix of [path] if it's root-relative. |
| + /// |
| + /// 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 (match == null) return null; |
| return match[0]; |
| } |
| @@ -646,12 +734,20 @@ class _ParsedPath { |
| /// letters. |
| String root; |
| + /// Whether this path is root-relative. |
| + /// |
| + /// See [Builder.isRootRelative]. |
| + bool isRootRelative; |
| + |
| /// The path-separated parts of the path. All but the last will be |
| /// directories. |
| List<String> parts; |
| - /// The path separators following each part. The last one will be an empty |
| - /// string unless the path ends with a trailing separator. |
| + /// The path separators preceeding each part. |
|
Bob Nystrom
2013/06/06 22:50:40
"preceeding" -> "preceding".
nweiz
2013/06/07 01:04:30
Done.
|
| + /// |
| + /// The first one will be an empty string unless the root requires a separator |
| + /// between it and the path. The last one will be an empty string unless the |
| + /// path ends with a trailing separator. |
| List<String> separators; |
| /// The file extension of the last part, or "" if it doesn't have one. |
| @@ -660,7 +756,8 @@ class _ParsedPath { |
| /// `true` if this is an absolute path. |
| bool get isAbsolute => root != null; |
| - _ParsedPath(this.style, this.root, this.parts, this.separators); |
| + _ParsedPath(this.style, this.root, this.isRootRelative, this.parts, |
| + this.separators); |
| String get basename { |
| var copy = this.clone(); |
| @@ -715,8 +812,12 @@ class _ParsedPath { |
| } |
| // Canonicalize separators. |
| - var newSeparators = []; |
| - _growListFront(newSeparators, newParts.length, style.separator); |
| + var newSeparators = new List.generate( |
| + newParts.length, (_) => style.separator, growable: true); |
| + newSeparators.insert(0, |
| + isAbsolute && newParts.length > 0 && |
| + root.contains(style.needsSeparatorPattern) ? |
| + style.separator : ''); |
| parts = newParts; |
| separators = newSeparators; |
| @@ -732,9 +833,10 @@ class _ParsedPath { |
| var builder = new StringBuffer(); |
| if (root != null) builder.write(root); |
| for (var i = 0; i < parts.length; i++) { |
| - builder.write(parts[i]); |
| builder.write(separators[i]); |
| + builder.write(parts[i]); |
| } |
| + builder.write(separators.last); |
| return builder.toString(); |
| } |
| @@ -758,5 +860,6 @@ class _ParsedPath { |
| } |
| _ParsedPath clone() => new _ParsedPath( |
| - style, root, new List.from(parts), new List.from(separators)); |
| + style, root, isRootRelative, |
| + new List.from(parts), new List.from(separators)); |
| } |