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 |