Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /// A comprehensive, cross-platform path manipulation library. | 5 /// A comprehensive, cross-platform path manipulation library. |
| 6 /// | 6 /// |
| 7 /// ## Installing ## | 7 /// ## Installing ## |
| 8 /// | 8 /// |
| 9 /// Use [pub][] to install this package. Add the following to your | 9 /// Use [pub][] to install this package. Add the following to your |
| 10 /// `pubspec.yaml` file. | 10 /// `pubspec.yaml` file. |
| 11 /// | 11 /// |
| 12 /// dependencies: | 12 /// dependencies: |
| 13 /// pathos: any | 13 /// pathos: any |
| 14 /// | 14 /// |
| 15 /// Then run `pub install`. | 15 /// Then run `pub install`. |
| 16 /// | 16 /// |
| 17 /// For more information, see the | 17 /// For more information, see the |
| 18 /// [pathos package on pub.dartlang.org][pkg]. | 18 /// [pathos package on pub.dartlang.org][pkg]. |
| 19 /// | 19 /// |
| 20 /// [pub]: http://pub.dartlang.org | 20 /// [pub]: http://pub.dartlang.org |
| 21 /// [pkg]: http://pub.dartlang.org/packages/pathos | 21 /// [pkg]: http://pub.dartlang.org/packages/pathos |
| 22 library path; | 22 library path; |
| 23 | 23 |
| 24 import 'dart:io' as io; | 24 import 'dart:mirrors'; |
| 25 | 25 |
| 26 /// An internal builder for the current OS so we can provide a straight | 26 /// An internal builder for the current OS so we can provide a straight |
| 27 /// functional interface and not require users to create one. | 27 /// functional interface and not require users to create one. |
| 28 final _builder = new Builder(); | 28 final _builder = new Builder(); |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * Inserts [length] elements in front of the [list] and fills them with the | 31 * Inserts [length] elements in front of the [list] and fills them with the |
| 32 * [fillValue]. | 32 * [fillValue]. |
| 33 */ | 33 */ |
| 34 void _growListFront(List list, int length, fillValue) => | 34 void _growListFront(List list, int length, fillValue) => |
| 35 list.insertAll(0, new List.filled(length, fillValue)); | 35 list.insertAll(0, new List.filled(length, fillValue)); |
| 36 | 36 |
| 37 /// If we're running in the server-side Dart VM, this will return a | |
| 38 /// [LibraryMirror] that gives access to the `dart:io` library. | |
| 39 /// | |
| 40 /// If `dart:io` is not available, this returns null. | |
| 41 LibraryMirror get _io { | |
| 42 try { | |
| 43 return currentMirrorSystem().libraries[Uri.parse('dart:io')]; | |
|
ahe
2013/06/12 21:47:52
You shouldn't need to wrap this in a catch block.
nweiz
2013/06/12 22:20:08
At least when I wrote this, dart2js's MirrorSystem
ahe
2013/06/12 22:24:57
Doh! I'll add that. I have focused on adding wha
| |
| 44 } catch (_) { | |
| 45 return null; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 // TODO(nweiz): when issue 6490 or 6943 are fixed, make this work under dart2js. | |
| 50 /// If we're running in Dartium, this will return a [LibraryMirror] that gives | |
| 51 /// access to the `dart:html` library. | |
| 52 /// | |
| 53 /// If `dart:html` is not available, this returns null. | |
| 54 LibraryMirror get _html { | |
| 55 try { | |
| 56 return currentMirrorSystem().libraries[Uri.parse('dart:html')]; | |
|
ahe
2013/06/12 21:47:52
dart2js will not include the code for 'dart:html'
nweiz
2013/06/12 22:20:08
I'll keep this in mind. Right now this code only w
| |
| 57 } catch (_) { | |
| 58 return null; | |
| 59 } | |
| 60 } | |
| 61 | |
| 37 /// Gets the path to the current working directory. | 62 /// Gets the path to the current working directory. |
| 38 String get current => io.Directory.current.path; | 63 /// |
| 64 /// In the browser, this means the current URL. When using dart2js, this | |
| 65 /// currently returns `.` due to technical constraints. In the future, it will | |
| 66 /// return the current URL. | |
| 67 String get current { | |
| 68 if (_io != null) { | |
| 69 return _io.classes[const Symbol('Directory')] | |
| 70 .getField(const Symbol('current')).reflectee.path; | |
| 71 } else if (_html != null) { | |
| 72 return _html.getField(const Symbol('window')) | |
| 73 .reflectee.location.href; | |
| 74 } else { | |
| 75 return '.'; | |
| 76 } | |
| 77 } | |
| 39 | 78 |
| 40 /// Gets the path separator for the current platform. On Mac and Linux, this | 79 /// Gets the path separator for the current platform. On Mac and Linux, this |
| 41 /// is `/`. On Windows, it's `\`. | 80 /// is `/`. On Windows, it's `\`. |
| 42 String get separator => _builder.separator; | 81 String get separator => _builder.separator; |
| 43 | 82 |
| 44 /// Converts [path] to an absolute path by resolving it relative to the current | 83 /// Converts [path] to an absolute path by resolving it relative to the current |
| 45 /// working directory. If [path] is already an absolute path, just returns it. | 84 /// working directory. If [path] is already an absolute path, just returns it. |
| 46 /// | 85 /// |
| 47 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt | 86 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt |
| 48 String absolute(String path) => join(current, path); | 87 String absolute(String path) => join(current, path); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 /// Returns the root of [path], if it's absolute, or the empty string if it's | 136 /// Returns the root of [path], if it's absolute, or the empty string if it's |
| 98 /// relative. | 137 /// relative. |
| 99 /// | 138 /// |
| 100 /// // Unix | 139 /// // Unix |
| 101 /// path.rootPrefix('path/to/foo'); // -> '' | 140 /// path.rootPrefix('path/to/foo'); // -> '' |
| 102 /// path.rootPrefix('/path/to/foo'); // -> '/' | 141 /// path.rootPrefix('/path/to/foo'); // -> '/' |
| 103 /// | 142 /// |
| 104 /// // Windows | 143 /// // Windows |
| 105 /// path.rootPrefix(r'path\to\foo'); // -> '' | 144 /// path.rootPrefix(r'path\to\foo'); // -> '' |
| 106 /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' | 145 /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' |
| 146 /// | |
| 147 /// // URL | |
| 148 /// path.rootPrefix('path/to/foo'); // -> '' | |
| 149 /// path.rootPrefix('http://dartlang.org/path/to/foo'); | |
| 150 /// // -> 'http://dartlang.org' | |
| 107 String rootPrefix(String path) => _builder.rootPrefix(path); | 151 String rootPrefix(String path) => _builder.rootPrefix(path); |
| 108 | 152 |
| 109 /// Returns `true` if [path] is an absolute path and `false` if it is a | 153 /// Returns `true` if [path] is an absolute path and `false` if it is a |
| 110 /// relative path. | 154 /// relative path. |
| 111 /// | 155 /// |
| 112 /// On POSIX systems, absolute paths start with a `/` (forward slash). On | 156 /// On POSIX systems, absolute paths start with a `/` (forward slash). On |
| 113 /// Windows, an absolute path starts with `\\`, or a drive letter followed by | 157 /// Windows, an absolute path starts with `\\`, or a drive letter followed by |
| 114 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and | 158 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and |
| 115 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. | 159 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. |
| 116 /// | 160 /// |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 180 /// path.split('path/../foo'); // -> ['path', '..', 'foo'] | 224 /// path.split('path/../foo'); // -> ['path', '..', 'foo'] |
| 181 /// | 225 /// |
| 182 /// If [path] is absolute, the root directory will be the first element in the | 226 /// If [path] is absolute, the root directory will be the first element in the |
| 183 /// array. Example: | 227 /// array. Example: |
| 184 /// | 228 /// |
| 185 /// // Unix | 229 /// // Unix |
| 186 /// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] | 230 /// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] |
| 187 /// | 231 /// |
| 188 /// // Windows | 232 /// // Windows |
| 189 /// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] | 233 /// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] |
| 234 /// | |
| 235 /// // Browser | |
| 236 /// path.split('http://dartlang.org/path/to/foo'); | |
| 237 /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] | |
| 190 List<String> split(String path) => _builder.split(path); | 238 List<String> split(String path) => _builder.split(path); |
| 191 | 239 |
| 192 /// Normalizes [path], simplifying it by handling `..`, and `.`, and | 240 /// Normalizes [path], simplifying it by handling `..`, and `.`, and |
| 193 /// removing redundant path separators whenever possible. | 241 /// removing redundant path separators whenever possible. |
| 194 /// | 242 /// |
| 195 /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' | 243 /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' |
| 196 String normalize(String path) => _builder.normalize(path); | 244 String normalize(String path) => _builder.normalize(path); |
| 197 | 245 |
| 198 /// Attempts to convert [path] to an equivalent relative path from the current | 246 /// Attempts to convert [path] to an equivalent relative path from the current |
| 199 /// directory. | 247 /// directory. |
| 200 /// | 248 /// |
| 201 /// // Given current directory is /root/path: | 249 /// // Given current directory is /root/path: |
| 202 /// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' | 250 /// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' |
| 203 /// path.relative('/root/other.dart'); // -> '../other.dart' | 251 /// path.relative('/root/other.dart'); // -> '../other.dart' |
| 204 /// | 252 /// |
| 205 /// If the [from] argument is passed, [path] is made relative to that instead. | 253 /// If the [from] argument is passed, [path] is made relative to that instead. |
| 206 /// | 254 /// |
| 207 /// path.relative('/root/path/a/b.dart', | 255 /// path.relative('/root/path/a/b.dart', |
| 208 /// from: '/root/path'); // -> 'a/b.dart' | 256 /// from: '/root/path'); // -> 'a/b.dart' |
| 209 /// path.relative('/root/other.dart', | 257 /// path.relative('/root/other.dart', |
| 210 /// from: '/root/path'); // -> '../other.dart' | 258 /// from: '/root/path'); // -> '../other.dart' |
| 211 /// | 259 /// |
| 212 /// Since there is no relative path from one drive letter to another on Windows, | 260 /// Since there is no relative path from one drive letter to another on Windows, |
| 213 /// this will return an absolute path in that case. | 261 /// or from one hostname to another for URLs, this will return an absolute path |
| 262 /// in those cases. | |
| 214 /// | 263 /// |
| 264 /// // Windows | |
| 215 /// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' | 265 /// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' |
| 266 /// | |
| 267 /// // URL | |
| 268 /// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); | |
| 269 /// // -> 'http://dartlang.org' | |
| 216 String relative(String path, {String from}) => | 270 String relative(String path, {String from}) => |
| 217 _builder.relative(path, from: from); | 271 _builder.relative(path, from: from); |
| 218 | 272 |
| 219 /// Removes a trailing extension from the last part of [path]. | 273 /// Removes a trailing extension from the last part of [path]. |
| 220 /// | 274 /// |
| 221 /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | 275 /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' |
| 222 String withoutExtension(String path) => _builder.withoutExtension(path); | 276 String withoutExtension(String path) => _builder.withoutExtension(path); |
| 223 | 277 |
| 224 /// Validates that there are no non-null arguments following a null one and | 278 /// Validates that there are no non-null arguments following a null one and |
| 225 /// throws an appropriate [ArgumentError] on failure. | 279 /// throws an appropriate [ArgumentError] on failure. |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 245 } | 299 } |
| 246 | 300 |
| 247 /// An instantiable class for manipulating paths. Unlike the top-level | 301 /// An instantiable class for manipulating paths. Unlike the top-level |
| 248 /// functions, this lets you explicitly select what platform the paths will use. | 302 /// functions, this lets you explicitly select what platform the paths will use. |
| 249 class Builder { | 303 class Builder { |
| 250 /// Creates a new path builder for the given style and root directory. | 304 /// Creates a new path builder for the given style and root directory. |
| 251 /// | 305 /// |
| 252 /// If [style] is omitted, it uses the host operating system's path style. If | 306 /// If [style] is omitted, it uses the host operating system's path style. If |
| 253 /// [root] is omitted, it defaults to the current working directory. If [root] | 307 /// [root] is omitted, it defaults to the current working directory. If [root] |
| 254 /// is relative, it is considered relative to the current working directory. | 308 /// is relative, it is considered relative to the current working directory. |
| 309 /// | |
| 310 /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults | |
| 311 /// to the current URL. When using dart2js, it currently defaults to `.` due | |
| 312 /// to technical constraints. | |
| 255 factory Builder({Style style, String root}) { | 313 factory Builder({Style style, String root}) { |
| 256 if (style == null) { | 314 if (style == null) { |
| 257 if (io.Platform.operatingSystem == 'windows') { | 315 if (_io == null) { |
| 316 style = Style.url; | |
| 317 } else if (_io.classes[const Symbol('Platform')] | |
| 318 .getField(const Symbol('operatingSystem')).reflectee == 'windows') { | |
| 258 style = Style.windows; | 319 style = Style.windows; |
| 259 } else { | 320 } else { |
| 260 style = Style.posix; | 321 style = Style.posix; |
| 261 } | 322 } |
| 262 } | 323 } |
| 263 | 324 |
| 264 if (root == null) root = current; | 325 if (root == null) root = current; |
| 265 | 326 |
| 266 return new Builder._(style, root); | 327 return new Builder._(style, root); |
| 267 } | 328 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 340 /// Returns the root of [path], if it's absolute, or an empty string if it's | 401 /// Returns the root of [path], if it's absolute, or an empty string if it's |
| 341 /// relative. | 402 /// relative. |
| 342 /// | 403 /// |
| 343 /// // Unix | 404 /// // Unix |
| 344 /// builder.rootPrefix('path/to/foo'); // -> '' | 405 /// builder.rootPrefix('path/to/foo'); // -> '' |
| 345 /// builder.rootPrefix('/path/to/foo'); // -> '/' | 406 /// builder.rootPrefix('/path/to/foo'); // -> '/' |
| 346 /// | 407 /// |
| 347 /// // Windows | 408 /// // Windows |
| 348 /// builder.rootPrefix(r'path\to\foo'); // -> '' | 409 /// builder.rootPrefix(r'path\to\foo'); // -> '' |
| 349 /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' | 410 /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' |
| 411 /// | |
| 412 /// // URL | |
| 413 /// builder.rootPrefix('path/to/foo'); // -> '' | |
| 414 /// builder.rootPrefix('http://dartlang.org/path/to/foo'); | |
| 415 /// // -> 'http://dartlang.org' | |
| 350 String rootPrefix(String path) { | 416 String rootPrefix(String path) { |
| 351 var root = _parse(path).root; | 417 var root = _parse(path).root; |
| 352 return root == null ? '' : root; | 418 return root == null ? '' : root; |
| 353 } | 419 } |
| 354 | 420 |
| 355 /// Returns `true` if [path] is an absolute path and `false` if it is a | 421 /// Returns `true` if [path] is an absolute path and `false` if it is a |
| 356 /// relative path. | 422 /// relative path. |
| 357 /// | 423 /// |
| 358 /// On POSIX systems, absolute paths start with a `/` (forward slash). On | 424 /// On POSIX systems, absolute paths start with a `/` (forward slash). On |
| 359 /// Windows, an absolute path starts with `\\`, or a drive letter followed by | 425 /// Windows, an absolute path starts with `\\`, or a drive letter followed by |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 543 | 609 |
| 544 // If the path is still relative and `from` is absolute, we're unable to | 610 // If the path is still relative and `from` is absolute, we're unable to |
| 545 // find a path from `from` to `path`. | 611 // find a path from `from` to `path`. |
| 546 if (this.isRelative(path) && this.isAbsolute(from)) { | 612 if (this.isRelative(path) && this.isAbsolute(from)) { |
| 547 throw new ArgumentError('Unable to find a path to "$path" from "$from".'); | 613 throw new ArgumentError('Unable to find a path to "$path" from "$from".'); |
| 548 } | 614 } |
| 549 | 615 |
| 550 var fromParsed = _parse(from)..normalize(); | 616 var fromParsed = _parse(from)..normalize(); |
| 551 var pathParsed = _parse(path)..normalize(); | 617 var pathParsed = _parse(path)..normalize(); |
| 552 | 618 |
| 619 if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { | |
| 620 return pathParsed.toString(); | |
| 621 } | |
| 622 | |
| 553 // If the root prefixes don't match (for example, different drive letters | 623 // If the root prefixes don't match (for example, different drive letters |
| 554 // on Windows), then there is no relative path, so just return the absolute | 624 // on Windows), then there is no relative path, so just return the absolute |
| 555 // one. In Windows, drive letters are case-insenstive and we allow | 625 // one. In Windows, drive letters are case-insenstive and we allow |
| 556 // calculation of relative paths, even if a path has not been normalized. | 626 // calculation of relative paths, even if a path has not been normalized. |
| 557 if (fromParsed.root != pathParsed.root && | 627 if (fromParsed.root != pathParsed.root && |
| 558 ((fromParsed.root == null || pathParsed.root == null) || | 628 ((fromParsed.root == null || pathParsed.root == null) || |
| 559 fromParsed.root.toLowerCase().replaceAll('/', '\\') != | 629 fromParsed.root.toLowerCase().replaceAll('/', '\\') != |
| 560 pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { | 630 pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { |
| 561 return pathParsed.toString(); | 631 return pathParsed.toString(); |
| 562 } | 632 } |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 861 // doesn't count. | 931 // doesn't count. |
| 862 if (lastDot <= 0) return [file, '']; | 932 if (lastDot <= 0) return [file, '']; |
| 863 | 933 |
| 864 return [file.substring(0, lastDot), file.substring(lastDot)]; | 934 return [file.substring(0, lastDot), file.substring(lastDot)]; |
| 865 } | 935 } |
| 866 | 936 |
| 867 _ParsedPath clone() => new _ParsedPath( | 937 _ParsedPath clone() => new _ParsedPath( |
| 868 style, root, isRootRelative, | 938 style, root, isRootRelative, |
| 869 new List.from(parts), new List.from(separators)); | 939 new List.from(parts), new List.from(separators)); |
| 870 } | 940 } |
| OLD | NEW |