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 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 |