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 library path; | 6 library path; |
7 | 7 |
8 import 'dart:io' as io; | 8 import 'dart:io' as io; |
9 | 9 |
10 /// An internal builder for the current OS so we can provide a straight | 10 /// An internal builder for the current OS so we can provide a straight |
(...skipping 10 matching lines...) Expand all Loading... | |
21 /// Converts [path] to an absolute path by resolving it relative to the current | 21 /// Converts [path] to an absolute path by resolving it relative to the current |
22 /// working directory. If [path] is already an absolute path, just returns it. | 22 /// working directory. If [path] is already an absolute path, just returns it. |
23 /// | 23 /// |
24 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt | 24 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt |
25 String absolute(String path) => join(current, path); | 25 String absolute(String path) => join(current, path); |
26 | 26 |
27 /// Gets the part of [path] after the last separator. | 27 /// Gets the part of [path] after the last separator. |
28 /// | 28 /// |
29 /// path.basename('path/to/foo.dart'); // -> 'foo.dart' | 29 /// path.basename('path/to/foo.dart'); // -> 'foo.dart' |
30 /// path.basename('path/to'); // -> 'to' | 30 /// path.basename('path/to'); // -> 'to' |
31 /// | |
32 /// Trailing separators are ignored. | |
33 /// | |
34 /// builder.dirname('path/to/'); // -> 'to' | |
31 String basename(String path) => _builder.basename(path); | 35 String basename(String path) => _builder.basename(path); |
32 | 36 |
33 /// Gets the part of [path] after the last separator, and without any trailing | 37 /// Gets the part of [path] after the last separator, and without any trailing |
34 /// file extension. | 38 /// file extension. |
35 /// | 39 /// |
36 /// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' | 40 /// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' |
41 /// | |
42 /// Trailing separators are ignored. | |
43 /// | |
44 /// builder.dirname('path/to/foo.dart/'); // -> 'foo' | |
37 String basenameWithoutExtension(String path) => | 45 String basenameWithoutExtension(String path) => |
38 _builder.basenameWithoutExtension(path); | 46 _builder.basenameWithoutExtension(path); |
39 | 47 |
40 /// Gets the part of [path] before the last separator. | 48 /// Gets the part of [path] before the last separator. |
41 /// | 49 /// |
42 /// path.dirname('path/to/foo.dart'); // -> 'path/to' | 50 /// path.dirname('path/to/foo.dart'); // -> 'path/to' |
43 /// path.dirname('path/to'); // -> 'to' | 51 /// path.dirname('path/to'); // -> 'to' |
52 /// | |
53 /// Trailing separators are ignored. | |
54 /// | |
55 /// builder.dirname('path/to/'); // -> 'path' | |
44 String dirname(String path) => _builder.dirname(path); | 56 String dirname(String path) => _builder.dirname(path); |
45 | 57 |
46 /// Gets the file extension of [path]: the portion of [basename] from the last | 58 /// Gets the file extension of [path]: the portion of [basename] from the last |
47 /// `.` to the end (including the `.` itself). | 59 /// `.` to the end (including the `.` itself). |
48 /// | 60 /// |
49 /// path.extension('path/to/foo.dart'); // -> '.dart' | 61 /// path.extension('path/to/foo.dart'); // -> '.dart' |
50 /// path.extension('path/to/foo'); // -> '' | 62 /// path.extension('path/to/foo'); // -> '' |
51 /// path.extension('path.to/foo'); // -> '' | 63 /// path.extension('path.to/foo'); // -> '' |
52 /// path.extension('path/to/foo.dart.js'); // -> '.js' | 64 /// path.extension('path/to/foo.dart.js'); // -> '.js' |
53 /// | 65 /// |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
183 | 195 |
184 /// Gets the path separator for the builder's [style]. On Mac and Linux, | 196 /// Gets the path separator for the builder's [style]. On Mac and Linux, |
185 /// this is `/`. On Windows, it's `\`. | 197 /// this is `/`. On Windows, it's `\`. |
186 String get separator => style.separator; | 198 String get separator => style.separator; |
187 | 199 |
188 /// Gets the part of [path] after the last separator on the builder's | 200 /// Gets the part of [path] after the last separator on the builder's |
189 /// platform. | 201 /// platform. |
190 /// | 202 /// |
191 /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' | 203 /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' |
192 /// builder.basename('path/to'); // -> 'to' | 204 /// builder.basename('path/to'); // -> 'to' |
205 /// | |
206 /// Trailing separators are ignored. | |
207 /// | |
208 /// builder.dirname('path/to/'); // -> 'to' | |
193 String basename(String path) => _parse(path).basename; | 209 String basename(String path) => _parse(path).basename; |
194 | 210 |
195 /// Gets the part of [path] after the last separator on the builder's | 211 /// Gets the part of [path] after the last separator on the builder's |
196 /// platform, and without any trailing file extension. | 212 /// platform, and without any trailing file extension. |
197 /// | 213 /// |
198 /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' | 214 /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' |
215 /// | |
216 /// Trailing separators are ignored. | |
217 /// | |
218 /// builder.dirname('path/to/foo.dart/'); // -> 'foo' | |
199 String basenameWithoutExtension(String path) => | 219 String basenameWithoutExtension(String path) => |
200 _parse(path).basenameWithoutExtension; | 220 _parse(path).basenameWithoutExtension; |
201 | 221 |
202 /// Gets the part of [path] before the last separator. | 222 /// Gets the part of [path] before the last separator. |
203 /// | 223 /// |
204 /// builder.dirname('path/to/foo.dart'); // -> 'path/to' | 224 /// builder.dirname('path/to/foo.dart'); // -> 'path/to' |
205 /// builder.dirname('path/to'); // -> 'to' | 225 /// builder.dirname('path/to'); // -> 'path' |
226 /// | |
227 /// Trailing separators are ignored. | |
228 /// | |
229 /// builder.dirname('path/to/'); // -> 'path' | |
206 String dirname(String path) { | 230 String dirname(String path) { |
207 var parsed = _parse(path); | 231 var parsed = _parse(path); |
232 parsed.removeTrailingSeparators(); | |
208 if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; | 233 if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; |
209 if (!parsed.hasTrailingSeparator) { | 234 if (parsed.parts.length == 1) { |
210 if (parsed.parts.length == 1) { | 235 return parsed.root == null ? '.' : parsed.root; |
211 return parsed.root == null ? '.' : parsed.root; | |
212 } | |
213 parsed.parts.removeLast(); | |
214 parsed.separators.removeLast(); | |
215 } | 236 } |
216 parsed.separators[parsed.separators.length - 1] = ''; | 237 parsed.parts.removeLast(); |
238 parsed.separators.removeLast(); | |
239 parsed.removeTrailingSeparators(); | |
Bob Nystrom
2012/12/20 02:27:28
Is this redundant with the above call to removeTra
nweiz
2012/12/20 20:54:49
No. The first call removes the separators at the e
| |
217 return parsed.toString(); | 240 return parsed.toString(); |
218 } | 241 } |
219 | 242 |
220 /// Gets the file extension of [path]: the portion of [basename] from the last | 243 /// Gets the file extension of [path]: the portion of [basename] from the last |
221 /// `.` to the end (including the `.` itself). | 244 /// `.` to the end (including the `.` itself). |
222 /// | 245 /// |
223 /// builder.extension('path/to/foo.dart'); // -> '.dart' | 246 /// builder.extension('path/to/foo.dart'); // -> '.dart' |
224 /// builder.extension('path/to/foo'); // -> '' | 247 /// builder.extension('path/to/foo'); // -> '' |
225 /// builder.extension('path.to/foo'); // -> '' | 248 /// builder.extension('path.to/foo'); // -> '' |
226 /// builder.extension('path/to/foo.dart.js'); // -> '.js' | 249 /// builder.extension('path/to/foo.dart.js'); // -> '.js' |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
433 // out of them. | 456 // out of them. |
434 pathParsed.parts.insertRange(0, fromParsed.parts.length, '..'); | 457 pathParsed.parts.insertRange(0, fromParsed.parts.length, '..'); |
435 pathParsed.separators.insertRange(0, fromParsed.parts.length, | 458 pathParsed.separators.insertRange(0, fromParsed.parts.length, |
436 style.separator); | 459 style.separator); |
437 | 460 |
438 // Corner case: the paths completely collapsed. | 461 // Corner case: the paths completely collapsed. |
439 if (pathParsed.parts.length == 0) return '.'; | 462 if (pathParsed.parts.length == 0) return '.'; |
440 | 463 |
441 // Make it relative. | 464 // Make it relative. |
442 pathParsed.root = ''; | 465 pathParsed.root = ''; |
443 pathParsed.removeTrailingSeparator(); | 466 pathParsed.removeTrailingSeparators(); |
444 | 467 |
445 return pathParsed.toString(); | 468 return pathParsed.toString(); |
446 } | 469 } |
447 | 470 |
448 /// Removes a trailing extension from the last part of [path]. | 471 /// Removes a trailing extension from the last part of [path]. |
449 /// | 472 /// |
450 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | 473 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' |
451 String withoutExtension(String path) { | 474 String withoutExtension(String path) { |
452 var parsed = _parse(path); | 475 var parsed = _parse(path); |
453 if (parsed.hasTrailingSeparator) return parsed.toString(); | |
454 | 476 |
455 if (!parsed.parts.isEmpty) { | 477 for (var i = parsed.parts.length - 1; i >= 0; i--) { |
Bob Nystrom
2012/12/20 02:27:28
This is pretty hairy. One option would to have _Pa
nweiz
2012/12/20 20:54:49
It's not just trailing slashes that cause empty co
Bob Nystrom
2012/12/20 21:32:57
Ah, right.
| |
456 parsed.parts[parsed.parts.length - 1] = parsed.basenameWithoutExtension; | 478 if (!parsed.parts[i].isEmpty) { |
479 parsed.parts[i] = parsed.basenameWithoutExtension; | |
480 break; | |
481 } | |
457 } | 482 } |
458 | 483 |
459 return parsed.toString(); | 484 return parsed.toString(); |
460 } | 485 } |
461 | 486 |
462 _ParsedPath _parse(String path) { | 487 _ParsedPath _parse(String path) { |
463 var before = path; | 488 var before = path; |
464 | 489 |
465 // Remove the root prefix, if any. | 490 // Remove the root prefix, if any. |
466 var root = style.getRoot(path); | 491 var root = style.getRoot(path); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
548 /// directories. | 573 /// directories. |
549 List<String> parts; | 574 List<String> parts; |
550 | 575 |
551 /// The path separators following each part. The last one will be an empty | 576 /// The path separators following each part. The last one will be an empty |
552 /// string unless the path ends with a trailing separator. | 577 /// string unless the path ends with a trailing separator. |
553 List<String> separators; | 578 List<String> separators; |
554 | 579 |
555 /// The file extension of the last part, or "" if it doesn't have one. | 580 /// The file extension of the last part, or "" if it doesn't have one. |
556 String get extension => _splitExtension()[1]; | 581 String get extension => _splitExtension()[1]; |
557 | 582 |
558 /// `true` if the path ends with a trailing separator. | |
559 bool get hasTrailingSeparator { | |
560 if (separators.length == 0) return false; | |
561 return separators[separators.length - 1] != ''; | |
562 } | |
563 | |
564 /// `true` if this is an absolute path. | 583 /// `true` if this is an absolute path. |
565 bool get isAbsolute => root != null; | 584 bool get isAbsolute => root != null; |
566 | 585 |
567 _ParsedPath(this.style, this.root, this.parts, this.separators); | 586 _ParsedPath(this.style, this.root, this.parts, this.separators); |
568 | 587 |
569 String get basename { | 588 String get basename { |
570 if (parts.length == 0) return extension; | 589 var copy = this.copy(); |
571 if (hasTrailingSeparator) return ''; | 590 copy.removeTrailingSeparators(); |
572 return parts.last; | 591 if (copy.parts.isEmpty) return root == null ? '' : root; |
592 return copy.parts.last; | |
573 } | 593 } |
574 | 594 |
575 String get basenameWithoutExtension => _splitExtension()[0]; | 595 String get basenameWithoutExtension { |
596 var copy = this.copy(); | |
597 copy.removeTrailingSeparators(); | |
598 if (copy.parts.isEmpty) return root == null ? '' : root; | |
599 return copy._splitExtension()[0]; | |
600 } | |
576 | 601 |
577 void removeTrailingSeparator() { | 602 void removeTrailingSeparators() { |
578 if (separators.length > 0) { | 603 while (!parts.isEmpty && parts.last == '') { |
579 separators[separators.length - 1] = ''; | 604 parts.removeLast(); |
605 separators.removeLast(); | |
580 } | 606 } |
607 if (separators.length > 0) separators[separators.length - 1] = ''; | |
581 } | 608 } |
582 | 609 |
583 void normalize() { | 610 void normalize() { |
584 // Handle '.', '..', and empty parts. | 611 // Handle '.', '..', and empty parts. |
585 var leadingDoubles = 0; | 612 var leadingDoubles = 0; |
586 var newParts = []; | 613 var newParts = []; |
587 for (var part in parts) { | 614 for (var part in parts) { |
588 if (part == '.' || part == '') { | 615 if (part == '.' || part == '') { |
589 // Do nothing. Ignore it. | 616 // Do nothing. Ignore it. |
590 } else if (part == '..') { | 617 } else if (part == '..') { |
(...skipping 19 matching lines...) Expand all Loading... | |
610 newParts.add('.'); | 637 newParts.add('.'); |
611 } | 638 } |
612 | 639 |
613 // Canonicalize separators. | 640 // Canonicalize separators. |
614 var newSeparators = []; | 641 var newSeparators = []; |
615 newSeparators.insertRange(0, newParts.length, style.separator); | 642 newSeparators.insertRange(0, newParts.length, style.separator); |
616 | 643 |
617 parts = newParts; | 644 parts = newParts; |
618 separators = newSeparators; | 645 separators = newSeparators; |
619 | 646 |
620 removeTrailingSeparator(); | 647 removeTrailingSeparators(); |
621 } | 648 } |
622 | 649 |
623 String toString() { | 650 String toString() { |
624 var builder = new StringBuffer(); | 651 var builder = new StringBuffer(); |
625 if (root != null) builder.add(root); | 652 if (root != null) builder.add(root); |
626 for (var i = 0; i < parts.length; i++) { | 653 for (var i = 0; i < parts.length; i++) { |
627 builder.add(parts[i]); | 654 builder.add(parts[i]); |
628 builder.add(separators[i]); | 655 builder.add(separators[i]); |
629 } | 656 } |
630 | 657 |
631 return builder.toString(); | 658 return builder.toString(); |
632 } | 659 } |
633 | 660 |
634 /// Splits the last part of the path into a two-element list. The first is | 661 /// Splits the last part of the path into a two-element list. The first is |
635 /// the name of the file without any extension. The second is the extension | 662 /// the name of the file without any extension. The second is the extension |
636 /// or "" if it has none. | 663 /// or "" if it has none. |
637 List<String> _splitExtension() { | 664 List<String> _splitExtension() { |
638 if (parts.isEmpty) return ['', '']; | 665 if (parts.isEmpty) return ['', '']; |
639 if (hasTrailingSeparator) return ['', '']; | |
640 | 666 |
641 var file = parts.last; | 667 var file = parts.last; |
642 if (file == '..') return ['..', '']; | 668 if (file == '..') return ['..', '']; |
643 | 669 |
644 var lastDot = file.lastIndexOf('.'); | 670 var lastDot = file.lastIndexOf('.'); |
645 | 671 |
646 // If there is no dot, or it's the first character, like '.bashrc', it | 672 // If there is no dot, or it's the first character, like '.bashrc', it |
647 // doesn't count. | 673 // doesn't count. |
648 if (lastDot <= 0) return [file, '']; | 674 if (lastDot <= 0) return [file, '']; |
649 | 675 |
650 return [file.substring(0, lastDot), file.substring(lastDot)]; | 676 return [file.substring(0, lastDot), file.substring(lastDot)]; |
651 } | 677 } |
678 | |
679 _ParsedPath copy() => new _ParsedPath( | |
Bob Nystrom
2012/12/20 02:27:28
Nit, but I would prefer "clone" for this.
nweiz
2012/12/20 20:54:49
Done.
| |
680 style, root, new List.from(parts), new List.from(separators)); | |
652 } | 681 } |
OLD | NEW |