Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: pkg/path/lib/path.dart

Issue 11638021: Make dirname and basename ignore trailing separators and double slashes. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/path/test/path_posix_test.dart » ('j') | pkg/path/test/path_posix_test.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/path/test/path_posix_test.dart » ('j') | pkg/path/test/path_posix_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698